Skip to content

Instantly share code, notes, and snippets.

@NullVoxPopuli
Last active May 18, 2021 05:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NullVoxPopuli/f95baaa48b4e9854dcfe to your computer and use it in GitHub Desktop.
Save NullVoxPopuli/f95baaa48b4e9854dcfe to your computer and use it in GitHub Desktop.
Deep Equality Compare of Nested Dictionaries and Lists
Imports System.Collections.Generic
Namespace AUL
Partial Class Utils
Private Shared _valueComparer As EqualityComparer(Of Object) = EqualityComparer(Of Object).Default
''' <summary>
'''
''' There is no pre-given implementation of .Equals
''' that deeply compares based on value instead of
''' reference. The standard .Equals only returns
''' true if the two Dictionaries are the same object
'''
''' </summary>
''' <param name="a"></param>
''' <param name="b"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Shared Function CollectionsAreEqual( _
ByVal a As Object, _
ByVal b As Object) As Boolean
Dim result As Boolean = True
If (a.Equals(b)) Then Return True
If (a Is Nothing Or b Is Nothing) Then Return True
If (Not a.Count.Equals(b.Count)) Then Return False
If (Not a.GetType.Equals(b.GetType)) Then Return False
' we can assume both types are the same due to the type check above
' call the corresponding helper method based on whether this is
' a dictionary or a list
If (a.GetType.Equals(GetType(Dictionary(Of String, Object)))) Then
result = result AndAlso CollectionDictionariesAreEqualHelper(a, b)
ElseIf (a.GetType.Equals(GetType(List(Of Object)))) Then
result = result AndAlso CollectionListsAreEqualHelper(a, b)
Else
' Nothing to iterate over, we should have returned before getting here
End If
Return result
End Function
Private Shared Function CollectionDictionariesAreEqualHelper(ByVal a As Dictionary(Of String, Object), _
ByVal b As Dictionary(Of String, Object)) As Boolean
Dim result As Boolean = True
For Each kvp As KeyValuePair(Of String, Object) In a
Dim valueA As Object = kvp.Value
Dim valueB As Object
' if the second dictionary has the key
If (Not b.TryGetValue(kvp.Key, valueB)) Then Return False
' We don't care what a and b are, but the CollectionsAreEqual
' Function should take care of every case of object
result = (result And CollectionsAreEqual(valueA, valueB))
' Terminate if this loop caused an inequality
If (Not result) Then Return False
Next
Return result
End Function
Private Shared Function CollectionListsAreEqualHelper(ByVal a As List(Of Object), _
ByVal b As List(Of Object)) As Boolean
Dim result As Boolean = True
Dim i As Integer
For i = 0 To a.Count - 1
Dim valueA As Object = a(i)
Dim valueB As Object = b(i)
' We don't care what a and b are, but the CollectionsAreEqual
' Function should take care of every case of object
result = (result And CollectionsAreEqual(valueA, valueB))
If (Not result) Then Return False
Next
Return result
End Function
End Class
End Namespace
Imports System.Text
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports System.Collections.Generic
<TestClass()> Public Class CollectionEqualityTest
<TestMethod()> Public Sub EmptyCollections()
Dim h1 As New Dictionary(Of String, Object)
Dim h2 As New Dictionary(Of String, Object)
Assert.IsTrue(AUL.Utils.CollectionsAreEqual(h1, h2))
End Sub
<TestMethod> Public Sub SmallSameCollections()
Dim h1 As New Dictionary(Of String, Object)
Dim h2 As New Dictionary(Of String, Object)
h1.Add("1", "2")
h2.Add("1", "2")
Assert.IsTrue(AUL.Utils.CollectionsAreEqual(h1, h2))
End Sub
<TestMethod> Public Sub MultipleEntries()
Dim h1 As New Dictionary(Of String, Object)
Dim h2 As New Dictionary(Of String, Object)
h1.Add("1", "2")
h2.Add("1", "2")
h1.Add("one", "two")
h2.Add("one", "two")
h1.Add("and 1", "and 2")
h2.Add("and 1", "and 2")
Assert.IsTrue(AUL.Utils.CollectionsAreEqual(h1, h2))
End Sub
<TestMethod> Public Sub NestedHashes()
Dim h1 As New Dictionary(Of String, Object)
Dim h2 As New Dictionary(Of String, Object)
Dim h3 As New Dictionary(Of String, Object)
Dim h4 As New Dictionary(Of String, Object)
h1.Add("1", "2")
h2.Add("1", "2")
h1.Add("one", h3)
h2.Add("one", h4)
Assert.IsTrue(AUL.Utils.CollectionsAreEqual(h1, h2))
End Sub
<TestMethod> Public Sub Lists()
Dim h1 As New List(Of Object)
Dim h2 As New List(Of Object)
h1.Add(1)
h1.Add(2)
h2.Add(1)
h2.Add(2)
Assert.IsTrue(AUL.Utils.CollectionsAreEqual(h1, h2))
End Sub
<TestMethod> Public Sub HashesWithLists()
Dim h1 As New Dictionary(Of String, Object)
Dim h2 As New Dictionary(Of String, Object)
Dim h3 As New List(Of Object)
Dim h4 As New List(Of Object)
h3.Add(1)
h3.Add(2)
h4.Add(1)
h4.Add(2)
h1.Add("1", "2")
h2.Add("1", "2")
h1.Add("one", h3)
h2.Add("one", h4)
Assert.IsTrue(AUL.Utils.CollectionsAreEqual(h1, h2))
End Sub
<TestMethod> Public Sub DeeplyNestedData()
Dim h1 As Dictionary(Of String, Object) = TestData.GenerateGroupData
Dim h2 As Dictionary(Of String, Object) = TestData.GenerateGroupData
Assert.IsTrue(AUL.Utils.CollectionsAreEqual(h1, h2))
End Sub
End Class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment