Reg Exp
Web Design
Notes Client
Are Arrays Equal
In a recent project, we had the need to compare two arrays to see if all the elements in one array were in the other array and vice versa. The elements didn't have to appear in the same order in the arrays to be equal. So that prompted a generic "AreArraysEqual" function.

First, we have to define what "equality" is when comparing an array. Is the array "A" : "B" : "" : "C" equal to the array "A" : "B" : "C"? Well, we thought about including an "includeEmpties" parameter that would let the programmer decide if those two arrays are equal. But the more we thought about it, the more we decided that those arrays are not equal, no matter what.

Is the array "A" : "A" : "B" equal to the array "A" : "B" : "A"? Well, they both have two A's and two B's, so they should be equal. But what about "A" : "A" : "B" and "A" : "B" : "B"? Well, the first has two A's and one B, but the second has one A and two B's. So they shouldn't be equal. If you're comparing just for the presence of the value in the other array, then your code might see these two arrays as equal (since they have the same number of elements and are both made up of A's and B's).

Next, what if you define the first array to have a lower bound of 5 and an upper bound of 6 (two elements) and the second array to have a lower bound of 1 and an upper bound of 2 (also two elements)? The code shouldn't care about the bounds - it should only care about the number of elements and the contents of the arrays. Our code does.

Finally, we had to decide the best way to handle checking for equality. One way would be to have two loops - the outer loop would go through all the elements in array 1 and the inner loop would go through all the elements in array 2. If each element from array1 is also in array2 and vice versa, the arrays are equal. That is very inefficient. In fact, one test we did compared elements of 500 entries. It took 12 seconds to find out that the two arrays were equal, and our code took less than a second. The reason it's so inefficient is because you're processing n*n statements (where n is the number of array entries) - for each outer element you're going through all the inner elements. Our code works with n*2 statements (we go through each array once only).

What We Do

Conceptually, go through all the elements in the first array and put the element into a list. But don't just blindly put it into the list. The list will count the number of times the value occurs in the array (to handle the "A" : "A" : "B" situation). Also, tags in a list are always strings, no matter what the data type is. So if you say MyList(3) = 0 and MyList("3") = 0, you're modifying the same element in the list and not two different elements. So this would mean "3" = 3 if we didn't do something different. So we use the LotusScript Typename keyword to uniquely distinguish between a string of "3" and a number of 3.

Let's go over the code in pieces:

Function AreArraysEqual(array1 As Variant, array2 As Variant) As Integer
   Dim i As Integer
   Dim key As String
   Dim tempList List As Integer
   If Not Isarray(array1) Or Not Isarray(array2) Then
      AreArraysEqual = False
      Exit Function
   End If
   If Ubound(array1) - Lbound(array1) <> Ubound(array2) - Lbound(array2) Then
      AreArraysEqual = False
      Exit Function
   End If

The function is going to return a True or False value, so its return value is an integer. First, check to see if two arrays are passed in. If either one is not an array, then it's kind of a "trick question". You're asking if these two arrays are equal, but they aren't arrays. So, the answer to the question "are the arrays equal?" is "no".

If the two arrays do not have the same number of elements (note that we subtract the upper and lower bounds instead of comparing the upper and lower bounds) then there's no way they are going to match.

   For i = Lbound(array1) To Ubound(array1)
      key = Typename(array1(i)) & "~" & Cstr(array1(i))

Build a key value of the type of the variable ("STRING" or "INTEGER" or whatever) followed by a tilde and the value (converted to string if it isn't a string). This key will differentiate between "3" and 3 (the first will result in a key of STRING~3 and the second will result in a key of INTEGER~3).

      If Iselement(tempList(key)) Then   ' Already in the list - increase the counter
         tempList(key) = tempList(key) + 1
      Else   ' Not an element - put it into the list
         tempList(key) = 1
      End If

If that key value is already in the list, then this is the second or third or whatever occurrence of the tag in the array (the "A" : "A" : "B" situation). So the value in tempList will be the total count of all the tags in the array. Continuing our example situation, tempList("STRING~A") = 2 and tempList("STRING~B") = 1. Continue until all the elements in array1 are in the list.

   For i = Lbound(array2) To Ubound(array2)
      key = Typename(array2(i)) & "~" & Cstr(array2(i))
      If Not Iselement(tempList(key)) Then
         AreArraysEqual = False
         Exit Function
      End If

Go through all the elements in array 2. Build the same kind of key that we did for the elements of the first array. If the key we built is not in the list, then this value from array 2 didn't exist in array 1 and the arrays are not equal.

      tempList(key) = tempList(key) - 1
      If tempList(key) = 0 Then Erase tempList(key)

At this point, the key is found in tempList, so subtract one from the number of occurrences (for example, we have found the first "A" in array 2, but maybe not the second yet). If the number of occurrences is zero, then erase the element from the list (look up the help on the Erase keyword if you're unsure what the statement does). Working through an example, if array1 has "A" : "B" : "C" and array2 has "A" : "A" : "B", when processing array2 the first element will remove the tag of "STRING~A" from the list, so the second element won't find "STRING~A" in the list and will return that the arrays don't match.

   Forall indElement In tempList
      AreArraysEqual = False
      Exit Function
   End Forall
   AreArraysEqual = True
End Function

After working through all the elements in array 2, we need to see if there are any elements left in the list. If there are, the arrays are not equal. Since we have made sure that the arrays have the same number of elements and have erased the elements as we went along, I'm pretty sure that we'll never get into the "Forall" loop, but it's there just in case. If there aren't any elements left in the list, then everything matched up and the arrays are equal.

Note: the passed-in variables were defined as variants. So this code can check arrays of strings, arrays of numbers, arrays of dates, or any other built-in LotusScript type. It will not handle arrays of objects (comparing to see if two arrays of Notes Documents are the same collection) because when you're dealing with an object you don't know how to uniquely identify the object (there isn't necessarily a "value" for the object).