在VBA中select(大)范围内计算不同值的数量?

如何计算VBA中所选(大)范围内的不同值(数字和string混合)的数量?

我这样想:
1.将数据读入一维数组。
2.sorting数组(快速或合并sorting)需要testing哪个
3.如果对数组进行sorting,只需计算不同值的个数: if(a[i]<>a[i+1]) then counter=counter+1

这是解决这个问题的最有效的方法吗?

编辑:我想在Excel中做到这一点。

这是一个VBA解决scheme

你不需要一个数组来完成这个任务。 你也可以使用一个集合。 例

 Sub Samples() Dim scol As New Collection With Sheets("Sheet1") For i = 1 To 100 '<~~ Assuming the range is from A1 to A100 On Error Resume Next scol.Add .Range("A" & i).Value, Chr(34) & _ .Range("A" & i).Value & Chr(34) On Error GoTo 0 Next i End With Debug.Print scol.Count 'For Each itm In scol ' Debug.Print itm 'Next End Sub 

跟进

 Sub Samples() Dim scol As New Collection Dim MyAr As Variant With Sheets("Sheet1") '~~> Select your range in a column here MyAr = .Range("A1:A10").Value For i = 1 To UBound(MyAr) On Error Resume Next scol.Add MyAr(i, 1), Chr(34) & _ MyAr(i, 1) & Chr(34) On Error GoTo 0 Next i End With Debug.Print scol.Count 'For Each itm In scol ' Debug.Print itm 'Next End Sub 

也许您可以使用Scripting.Dictionary并将每个值添加到字典中,而不是步骤2和3。 任何重复的条目都会导致运行时错误,您可能会陷入或忽略( resume next )。 最后,你可以只返回字典的count ,这会给你唯一的条目数。

这里有一小段代码我赶紧扔在一起:

 Function UniqueEntryCount(SourceRange As Range) As Long Dim MyDataset As Variant Dim dic As Scripting.Dictionary Set dic = New Scripting.Dictionary MyDataset = SourceRange On Error Resume Next Dim i As Long For i = 1 To UBound(MyDataset, 1) dic.Add MyDataset(i, 1), "" Next i On Error GoTo 0 UniqueEntryCount = dic.Count Set dic = Nothing End Function 

我知道, resume next可以被认为是“代码味道”,但是替代scheme可以是使用字典的existsfunction来testing指定的键是否已经存在,然后如果没有,则添加该值。 我只是有一种感觉,当我在过去做过类似的事情时,忽略为重复键提出的任何错误,而不是使用exists YMMY会更快。 为了完整性,下面是使用exists的另一种方法:

 Function UniqueEntryCount(SourceRange As Range) As Long Dim MyDataset As Variant Dim dic As Scripting.Dictionary Set dic = New Scripting.Dictionary MyDataset = SourceRange Dim i As Long For i = 1 To UBound(MyDataset, 1) if not dic.Exists(MyDataset(i,1)) then dic.Add MyDataset(i, 1), "" Next i UniqueEntryCount = dic.Count Set dic = Nothing End Function 

虽然上面的代码比您提出的方法更简单,但是根据您的解决schemetesting它的性能是值得的。

基于i_saw_drones提出的思想 ,我强烈推荐Scripting.Dictionary 。 但是,如下所示,这可以在没有On Error Resume Next完成。 另外,他的示例需要链接Microsoft Scripting Runtime库。 我的例子将演示如何做到这一点,而不需要做任何连接。

另外,由于您在Excel中执行此操作,因此您无需在第1步中创build数组。 下面的函数将接受一系列单元格,这些单元格将被完全迭代。

(即UniqueCount = UniqueEntryCount(ActiveSheet.Cells)UniqueCount = UniqueEntryCount(MySheet.Range("A1:D100")

 Function UniqueEntryCount(SourceRange As Range) As Long Dim MyDataset As Variant Dim MyRow As Variant Dim MyCell As Variant Dim dic As Object Dim l1 As Long, l2 As Long Set dic = CreateObject("Scripting.Dictionary") MyDataset = SourceRange For l1 = 1 To UBound(MyDataset) ' There is no function to get the UBound of the 2nd dimension ' of an array (that I'm aware of), so use this division to ' get this value. This does not work for >=3 dimensions! For l2 = 1 To SourceRange.Count / UBound(MyDataset) If Not dic.Exists(MyDataset(l1, l2)) Then dic.Add MyDataset(l1, l2), MyDataset(l1, l2) End If Next l2 Next l1 UniqueEntryCount = dic.Count Set dic = Nothing End Function 

注意到上面的内容会计算一个空string""作为一个不同的值也是很重要的。 如果你不想这样做,只需将代码更改为:

  For l1 = 1 To UBound(MyDataset) For l2 = 1 To SourceRange.Count / UBound(MyDataset) If Not dic.Exists(MyDataset(l1, l2)) And MyDataset(l1, l2) <> "" Then dic.Add MyDataset(l1, l2), MyDataset(l1, l2) End If Next l2 Next l1 

对不起,这是用C#编写的。 这是我将如何做到这一点。

 // first copy the array so you don't lose any data List<value> copiedList = new List<value>(yourArray.ToList()); //for through your list so you test every value for (int a = 0; a < copiedList.Count; a++) { // copy instances to a new list so you can count the values and do something with them List<value> subList = new List<value>(copiedList.FindAll(v => v == copiedList[i]); // do not do anything if there is only 1 value found if(subList.Count > 1) // You would want to leave 1 'duplicate' in for (int i = 0; i < subList.Count - 1; i++) // remove every instance from the array but one copiedList.Remove(subList[i]); } int count = copiedList.Count; //this is your actual count 

没有testing过,请尝试。

你应该把它包装在一个方法里面,这样就不会弄乱垃圾了。 否则,以后只会丢失数组的副本。 (退货数量)

编辑:你需要一个列表这个工作,使用Array.ToList();