Finding Duplicate Strings
Windows only

Problem

Imagine you have an array of strings which contains duplicates. RhinoScript has a method to cull the duplicate strings. But rather than cull them, one would like to find them with a routine that will return the indices of the duplicate items.

Better yet, a routine that would return sets of indices, with each set containing the indices of a particular string would solve the problem. For example, if an array contained “Curve”, “Surface”, “curve”, “surface”, we would like to have an array containing [0,2] and [1,3] returned.

Solution

VBScript’s Dictionary is a useful tool for storing associative data, or data in the form of (key, item) pairs. In the problem outlined above, you could use a Dictionary to track each string and the indices where is appears in the array. In other words, use a Dictionary to store (string, indices) pairs.

To store the indices, you are going to need an array. But creating and resizing VBScript arrays is always a challenge. So, you might consider using a .NET’s ArrayList object. A .NET ArrayList is a COM-enabled object, which means it can be used in VBScript.

The following sample function demonstrates how you can use a Dictionary of strings and .NET ArrayList objects to find the indices of duplicate string items in an array.

Function FindDuplicateStrings(arrStrings, blnCase)

  ' Local variables
  Dim objDict, strKey, objItem, arrItems
  Dim i, j, nCount
  Dim arrResults()

  ' Default return value
  FindDuplicateStrings = Null

  ' Create a dictionary object and set it's compare mode
  Set objDict = CreateObject("Scripting.Dictionary")
  If (blnCase = True) Then
    objDict.CompareMode = vbBinaryCompare
  Else
    objDict.CompareMode = vbTextCompare
  End If

  ' Process input strings. If the string is not in the dictionary,
  ' then add it and add it's index to the ArrayList. Otherwise,
  ' just add it's index to the dictionary item's existing ArrayList.
  For i = 0 To UBound(arrStrings)
    strKey = arrStrings(i)
    If Not objDict.Exists(strKey) Then
      objDict.Add strKey, CreateObject("System.Collections.ArrayList")
    End If      
    objDict(strKey).Add(i)
  Next

  ' Find all of the dictionary items that have more than one index.
  ' Add those arrays to our result array
  nCount = 0
  arrItems = objDict.Items
  For Each objItem In arrItems
    If (objItem.Count > 1) Then
      ReDim Preserve arrResults(nCount)    
      arrResults(nCount) = objItem.ToArray()
      nCount = nCount + 1
    End If
  Next

  ' Done!
  FindDuplicateStrings = arrResults

End Function

Here is an example of how to use the above function:

Sub TestFindDuplicateStrings

  Dim arrStrings, arrResults, arrItem, nItem, i

  arrStrings = Array("Curve" ,  _
                     "Surface", _
                     "Mesh",    _
                     "Point",   _
                     "Surface", _
                     "Curve",   _
                     "Curve")

  arrResults = FindDuplicateStrings(arrStrings, False)
  If IsArray(arrResults) Then
    Call Rhino.Print("Duplicate Sets = " & CStr(UBound(arrResults) + 1))
    For i = 0 To UBound(arrResults)
      Call Rhino.Print("Set = " & CStr(i + 1))
      arrItem = arrResults(i)
      For Each nItem In arrItem
        Call Rhino.Print("Item " & CStr(nItem) & " = " & arrStrings(nItem))
      Next
    Next
  End If

End Sub