5

VBAで選択した(大きな)範囲内のさまざまな値(数値と文字列が混在している)の数をカウントするにはどうすればよいですか?

私はこれについて次のように考えてい
ます。1。データを1次元配列に読み込みます。
2.配列の並べ替え(クイックまたはマージソート)は、どれをテストする必要がありますか。3.
配列が並べ替えられている場合は、異なる値の数を数えるだけですif(a[i]<>a[i+1]) then counter=counter+1

この問題を解決する最も効率的な方法ですか?

編集:Excelでやりたいです。

4

4 に答える 4

7

これがVBAソリューションです

これを行うのに配列は必要ありません。コレクションを使用することもできます。例

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
于 2012-08-01T15:15:35.957 に答える
4

手順2と3の代わりに、aを使用しScripting.Dictionaryて、各値を辞書に追加することもできます。重複するエントリがあると、実行時エラーが発生し、トラップまたは無視することができます(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は「コードの臭い」と見なすことができますが、代わりexistsに、辞書の機能を使用して、指定されたキーがすでに存在するかどうかをテストし、存在しない場合は値を追加することもできます。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

上記のコードは提案された方法よりも単純ですが、ソリューションに対してそのパフォーマンスをテストする価値があります。

于 2012-08-01T15:13:16.567 に答える
3

i_saw_dronesによって提示されたアイデアに基づいて、私は強くお勧めしScripting.Dictionaryます。ただし、これはOn Error Resume Next以下に示すようにせずに行うことができます。Microsoft Scripting Runtimeまた、彼の例では、ライブラリをリンクする必要があります。私の例では、リンクを作成せずにこれを行う方法を示します。

また、これはExcelで行っているため、手順1で配列を作成する必要はまったくありません。以下の関数は、完全に繰り返されるセルの範囲を受け入れます。

(すなわち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

""上記はnull文字列を個別の値としてカウントすることに注意することも重要かもしれません。これを望まない場合は、コードを次のように変更するだけです。

    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
于 2012-08-01T15:20:32.780 に答える
0

申し訳ありませんが、これは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

テストしていません。試してみてください。

ガベージをいじらないように、これをメソッド内にラップする必要があります。そうしないと、後でアレイのコピーが失われます。(返品回数)

編集:これを機能させるにはリストが必要です。Array.ToList();を使用してください。

于 2012-08-01T14:53:24.870 に答える