3
\$\begingroup\$

I am a technical reviewer/writer and I use VBA for Word a lot to manage issues in Word documents I receive. I have frequently come across niggles when using Collections, Scripting.Dictionaries or Array lists. Then I saw some posts on creating a C# library for consumption by VBA. After much reading and research I eventually produced a C# library that wraps a C# dictionary and which can be consumed by VBA. It works very well but there are some wrinkles.

  • I'd like to be able to view the content of the dictionary in the same way that the content of Collections and ArrayLists (but not Scripting.Dictionaries) can be viewed using the VBA IDE Locals Window.

  • I'd like to understand why, when I pass the object created by the C# class, I can use class_instance.Item = value in the method which creates the class instance, but where if I pass the class instance to another method in VBA I first have to cast it to an object before I can assign a value to an element in the cast class_instance (e.g. class_instance_as_object.Item =Value).

Please be aware that this is a work in progress as I keep adding methods to the class whenever I have some VBA code that I wish to simplify.

I am not a professional programmer and the C# code below is my first foray into using this language.

I'm also looking for useful pointers to articles, or chapters in books that I should go and read to help me improve my c# code, but all comments are welcome.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;

namespace VBAExtensions
{
    /// <summary>
    /// Enum for accessing the kvp structure returned by Method Cohorts
    /// </summary>

    public enum CohortType
    {
        /// <summary>1 = the keys in A plus keys in B that are not shared</summary>
        KeysInAAndOnlyB = 1,
        /// <summary>2 = the Keys from B in A where B has a different value to A</summary>
        KeysInAandBWithDifferentValues,
        /// <summary>3 = the keys that are only in A and only in B</summary>
        KeysNotInAandB,
        /// <summary>4 = the keys that are inA and  B </summary>
        KeysInAandB,
        /// <summary>5 = the keys in A only   </summary>
        KeysInAOnly,
        /// <summary>6 = the keys in B only</summary>
        KeysInBOnly
    }

    /// <summary>
    /// Kvp is a C# class for VBA which implements a Key/Value Dictionary
    /// The object is a morer flexible version of the Scripting.Dictionary
    /// </summary>
    [Guid("6DC1808F-81BA-4DE0-9F7C-42EA11621B7E")]
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IKvp
    {
        /// <summary>
        /// Returns/Sets the "Value" specified by "Key" (i) of a Key/Value Pair
        /// </summary>
        /// <param name="Key"></param>
        /// <returns>Type used in Set statement (C# dynamic)</returns> 
        dynamic this[dynamic Key] { get; set; }

        /// <summary>
        /// Adds "Value" to the Kvp using an integer (VBA Long) Key.
        /// The integer key is is started at 0
        /// </summary>
        /// <param name="Value"></param>
        void AddByIndex(dynamic Value);

        /// <summary>
        /// Populates this Kvp using AddByIndex for each character in the string
        /// </summary>
        /// <param name="ipString"></param>
        void AddByIndexAsChars(string ipString);

        /// <summary>
        /// Pupulates this Kvp using AddByIndex for each substring in ipString delineated by ipSeparator
        /// </summary>
        /// <param name="ipString"></param>
        /// <param name="ipSeparator"></param>
        void AddByIndexAsSubStr(string ipString, string ipSeparator = ",");

        /// <summary>
        /// Pupulates a Kvp using AddByIndex for each array item
        /// </summary>
        /// <param name="this_array"></param>
        void AddByIndexFromArray(dynamic this_array);

        /// <summary>
        /// Adds "Value" to the Kvp with a key pf "Key"
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="Value"></param>
        void AddByKey(dynamic Key, dynamic Value);

        /// <summary>
        /// Adds array to the Kvp with ujsing array[x,1] as the key and array[x,0..end] as the value
        /// </summary>
        void AddByKeyFromArray(dynamic ipArray);

        /// <summary>
        /// Groups the keys of the two Kvp
        /// </summary>
        /// <param name="ArgKvp"></param>
        /// <returns>An array of 6 Kvp
        /// keys in a {1,2,3,4,5,6}
        /// keys in b {1,2,3,6,7,8}
        /// 1 = the keys in A plus keys in B that are not shared            {1,2,3( from A),4,5,6,7,8}
        /// 2 = the Keys from B in A where B has a different value to A     {3( from B) if value is different}
        /// 3 = the keys that are only in A and only in B                   {4,5,7,8}
        /// 4 = the keys that are in A and  B                               {1,2,3,6}
        /// 5 = the keys in A only                                          {4,5}
        /// 6 = the keys in B only                                          {7,8}
        /// </returns>
        Kvp Cohorts(Kvp ArgKvp);

        /// <summary>
        /// The number of key/vaue pairs in the Kvp
        /// </summary>
        /// <returns>Long</returns>
        int Count();

        /// <summary>
        /// Returns a shallow copy of the Kvp
        /// </summary>
        /// <returns>New kvp as a copy of the old kvp</returns>
        Kvp Clone();

        /// <summary>
        /// Gets the "Key" for the first ocurrence of "Value" in the Kvp.
        /// </summary>
        /// <param name="Value"></param>
        /// <returns>Key</returns>
        dynamic GetKey(dynamic Value);

        /// <summary>
        /// Returns a variant array of the Keys of the Kvp
        /// </summary>
        /// /// <returns>Variant Array</returns>
        dynamic GetKeys();

        /// <summary>
        /// Gets the 0th element of the Kvp
        /// </summary>
        /// <returns>Value at )th position</returns>
        dynamic GetFirst();


        /// <summary>
        /// Gets the element at the upper bound of MyKvp
        /// This may not be the maximum integer key
        /// </summary>
        /// <returns>Value of the element at the upper bound</returns>
        dynamic GetLast();

        /// <summary>
        /// Returns a variant array of the values of the Kvp
        /// </summary>
        /// <returns>Variant Array</returns>
        dynamic GetValues();

        /// <summary>
        /// Concatenates the keys to a string
        /// Will fall over is an element is not a primitive
        /// </summary>summary>
        /// <returns>Keys seperated by seperator</returns>
        string GetKeysAsString(string ipSeparator = ",");

        /// <summary>
        /// Concatenates the keys to a string
        /// Will fall over is an element is not a primitive
        /// </summary>summary>
        /// <returns>values seperated by seperator</returns>
        string GetValuesAsString(string ipSeparator = ",");

        /// <summary>
        /// True if the "Key" exists in the keys of the Kvp
        /// </summary>
        /// <param name="Key"></param>
        /// <returns>Boolean</returns>
        bool HoldsKey(dynamic Key);

        /// <summary>
        /// True if the "Value" exists in the values of the Kvp
        /// </summary>
        /// <param name="Value"></param>
        /// <returns>Boolean</returns>
        bool HoldsValue(dynamic Value);

        /// <summary>
        /// True if the Kvp holds 0 key/value pairs
        /// </summary>
        /// <returns>Boolean</returns>
        bool IsEmpty();

        /// <summary>
        /// True if the Kvp holds one or more key/value pairs
        /// </summary>
        /// <returns>Boolean</returns>
        bool IsNotEmpty();

        /// <summary>
        /// True is the "Key" is not found in the keys of the Kvp
        /// </summary>
        /// <param name="Key"></param>
        /// <returns>Boolean</returns>
        bool LacksKey(dynamic Key);

        /// <summary>
        /// True if the "Value" is not found in the values of the Kvp
        /// </summary>
        /// <param name="Value"></param>
        /// <returns>Boolean</returns>
        bool LacksValue(dynamic Value);

        /// <summary>
        /// Reverses the Key/Value pairs in a Kvp
        /// </summary>
        /// <returns>New Kvp where: 
        ///     Kvp.Value(1) = Kvp Unique values as Value/Key pairs 
        ///     Kvp.Value(2) = Kvp Non unique values as Key/Value pairs</returns>
        Kvp Mirror();

        /// <summary>
        /// Gets the value of the key then deletes the element from MyKvp
        /// </summary>
        /// <param name="ipKey"></param>
        /// <returns>Value at key</returns>
        dynamic Pull(dynamic ipKey);

        /// <summary>
        /// get the value of the key at the lower bound of array keys
        /// deletes the elemnt
        /// </summary>
        /// <returns>value at key of lower bound element</returns>
        dynamic PullFirst();

        /// <summary>
        /// get the value of the key at the upper bound of array keys
        /// deletes the element
        /// </summary>
        /// <returns>value at key of upper bound element</returns>
        dynamic PullLast();

        /// <summary>
        /// Removes the Key/Value pair spacified by "Key" from the Kvp
        /// </summary>
        void Remove(dynamic Key);

        /// <summary>
        /// deletes the element at th key of the lower bound of array MyKvp.keys
        /// </summary>
        void RemoveFirst();

        /// <summary>
        /// deletes the element with the key at the upper bound of array MyKvp.keys
        /// </summary>
        void RemoveLast();

        /// <summary>
        /// Removes all Key/Value pairs from the Kvp
        /// </summary>
        void RemoveAll();

        /// <summary>
        /// Returns true if the Values in Kvp are unique.
        /// </summary>
        /// <returns>Boolean</returns>
        bool ValuesAreUnique();

        /// <summary>
        /// Returns true if the Values in Kvp are not unique.
        /// </summary>
        /// <returns>Boolean</returns>
        bool ValuesAreNotUnique();
    }


    [Guid("434C844C-9FA2-4EC6-AB75-45D3013D75BE")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class Kvp : IKvp, IEnumerable
    {
        private Dictionary<dynamic, dynamic> MyKvp = new Dictionary<dynamic, dynamic>();
        public dynamic this[dynamic Key]
        {
            get
            {
                return MyKvp[Key];
            }
            set
            {
                MyKvp[Key] = (dynamic) value;
            }
        }

        public void AddByIndex(dynamic Value)
        {
            int my_index = GetNextKvpIndex();
            MyKvp.Add(my_index, Value);
        }

        public void AddByIndexAsChars(string ipString)
        {
            int myIndex = GetNextKvpIndex();
            char[] MyArray = ipString.ToCharArray();
            for (int i = 0; i <= MyArray.GetUpperBound(0); i++)
            {
                //Kvp uses ordinal indexes
                MyKvp.Add(i + myIndex, MyArray[i].ToString());
            }
        }

        private int GetNextKvpIndex()
        {
            // Can probably do this better by getting the keys, sorting and taking max +1
            // but only if the keys are integers
            if (MyKvp.Count==0)
            {
                return 0;
            }
            int my_index = MyKvp.Count + 1;
            while (MyKvp.ContainsKey(my_index))
            {
                my_index++;
            }

            return my_index;
        }

        public void AddByIndexAsSubStr(string ipString, string ipSeparator = ",")
        {
            int my_index = GetNextKvpIndex();
            string[] MyArray = ipString.Split(ipSeparator.ToArray());
            for (int i = 0; i <= MyArray.GetUpperBound(0); i++)
            {
                //Kvp uses ordinal indexes
                MyKvp.Add(i + my_index, MyArray[i]);
            }
        }

        public void AddByIndexFromArray(dynamic ipArrayrray)
        {
            int my_index = GetNextKvpIndex();
            dynamic[] myArray = (dynamic)ipArrayrray;
            for (int i = 0; i <= myArray.GetUpperBound(0); i++)
            {
                //Kvp uses ordinal indexes
                MyKvp.Add(i + my_index, ipArrayrray[i]);
            }
        }

        public void AddByKey(dynamic Key, dynamic Value)
        {
            MyKvp.Add(Key, Value);
        }

        public void AddByKeyFromArray(dynamic ipArray)
        {
            int rowLower = ipArray.GetLowerBound(0);
            int rowUpper = ipArray.GetUpperBound(0);
            int colLower = ipArray.GetLowerBound(1);
            int colUpper = ipArray.GetUpperBound(1);

            for (int i = rowLower; i < rowUpper; i++)
            {
                Kvp thisRow = new Kvp();
                for (int j = colLower; j < colUpper; j++)
                {
                    thisRow.AddByIndex(ipArray[i, j]);
                }
                MyKvp.Add(ipArray[i, 1], thisRow);
            }
        }

        public Kvp Cohorts(Kvp ArgKvp)
        {
            Kvp ResultKvp = new Kvp();
            // VBA reports object not set error if the resuly kvp are not newed
            for (int i = 1; i < 7; i++)
            {
                ResultKvp.AddByKey(i, new Kvp());
            }

            foreach (KeyValuePair<dynamic, dynamic> MyKvpPair in MyKvp)
            {
                // A plus unique in B
                ResultKvp[1].AddByKey(MyKvpPair.Key, MyKvpPair.Value);

                if (ArgKvp.LacksKey(MyKvpPair.Key))  // problem is here
                {
                    // In A only or in B only
                    ResultKvp[3].AddByKey(MyKvpPair.Key, MyKvpPair.Value);
                    // In A only
                    ResultKvp[5].AddByKey(MyKvpPair.Key, MyKvpPair.Value);
                }
                else
                {
                    // In A and In B
                    ResultKvp[4].AddByKey(MyKvpPair.Key, MyKvpPair.Value);
                }
            }

            // ArgKvp is a Kvp.  The GetEnumerator for ArgKvp returns a Value
            // and not a key value pair.  Therefore we need to iterate over the keys
            // of ArgKvp
            foreach (dynamic MyKey in ArgKvp.GetKeys())
            {
                // B in A with different value
                if (ResultKvp[1].LacksKey(MyKey))  // Result 0 will contain all of A
                {
                    ResultKvp[1].AddByKey(MyKey, ArgKvp[MyKey]);
                    ResultKvp[3].AddByKey(MyKey, ArgKvp[MyKey]);
                    ResultKvp[6].AddByKey(MyKey, ArgKvp[MyKey]);
                }
                else
                {
                    if (ResultKvp[1][MyKey] != ArgKvp[MyKey])
                    {
                        ResultKvp[2].AddByKey(MyKey, ArgKvp[MyKey]);
                    }
                }
            }
            return ResultKvp;
        }

        public Int32 Count()
        {
            return MyKvp.Count;
        }

        public Kvp Clone()
        {
            Kvp CloneKvp = new Kvp();
            foreach (KeyValuePair<dynamic, dynamic> myPair in MyKvp)
            {
                CloneKvp.AddByKey(myPair.Key, myPair.Value);
            }
            return CloneKvp;
        }

        public bool IsEmpty()
        {
            return MyKvp.Count == 0;
        }

        public bool IsNotEmpty()
        {
            return !IsEmpty();
        }

        public IEnumerator GetEnumerator()
        {
            foreach (KeyValuePair<dynamic, dynamic> my_pair in MyKvp)
            {
                yield return my_pair.Value;
            }
        }

        public dynamic GetKey(dynamic Value)
        {
            return this.Mirror()[1][Value];
        }

        public dynamic GetKeys()
        {
            return MyKvp.Keys.ToArray();
        }

        public string GetKeysAsString(string ipSeparator = ",")
        {
            string myReturn = string.Empty;
            foreach (KeyValuePair<dynamic, dynamic> myPair in MyKvp)
            {
                myReturn += myPair.Key.ToString() + ipSeparator;

            }
            return myReturn.TrimEnd(ipSeparator.ToCharArray());
        }

        public dynamic GetFirst()
        {
            return  MyKvp.First().Value;
        }

        public dynamic GetLast()
        {
            return MyKvp.Last().Value;
        }

        public string GetValuesAsString(string ipSeparator = ",")
        {
            string myReturn = string.Empty;
            foreach (KeyValuePair<dynamic, dynamic> myPair in MyKvp)
            {
                myReturn += myPair.Value.ToString() + ipSeparator;

            }

            return myReturn.TrimEnd(ipSeparator.ToCharArray());
        }

        public dynamic GetValues()
        {
            return MyKvp.Values.ToArray();
        }

        public bool HoldsKey(dynamic Key)
        {
            return MyKvp.ContainsKey(Key);
        }

        public bool HoldsValue(dynamic Value)
        {
            return MyKvp.ContainsValue(Value);
        }

        public bool LacksKey(dynamic Key)
        {
            return !HoldsKey(Key);
        }

        public bool LacksValue(dynamic Value)
        {
            return !HoldsValue(Value);
        }

        public Kvp Mirror()
        {
            Kvp MyResult = new Kvp();
            MyResult.AddByKey((int)1, new Kvp());
            MyResult.AddByKey((int)2, new Kvp());
            foreach (KeyValuePair<dynamic, dynamic> my_pair in MyKvp)
            {
                if (MyResult[1].LacksKey(my_pair.Value))
                {
                    MyResult[1].AddByKey(my_pair.Value, my_pair.Key);
                }
                else
                {
                    MyResult[2].AddByKey(my_pair.Key, my_pair.Value);
                }
            }
            return MyResult;
        }

        public dynamic Pull(dynamic ipKey)
        {

            dynamic myValue = MyKvp[ipKey];
            MyKvp.Remove(ipKey);
            return myValue;
        }
        public dynamic PullFirst()
        {
            dynamic myValue = MyKvp[MyKvp.Keys.ToArray()[0]];
            MyKvp.Remove(MyKvp.Keys.ToArray()[0]);
            return myValue;
        }

        public dynamic PullLast()
        {

            dynamic[] myKeys = MyKvp.Keys.ToArray();
            dynamic myValue = MyKvp[myKeys[myKeys.GetUpperBound(0)]];
            MyKvp.Remove(myKeys[myKeys.GetUpperBound(0)]);
            return myValue;
        }

        public void Remove(dynamic Key)
        {
            MyKvp.Remove(Key);
        }


        public void RemoveFirst()
        {

            MyKvp.Remove( MyKvp.Keys.ToArray()[0]);
        }

        public void RemoveLast()
        {
            dynamic[] myKeys = MyKvp.Keys.ToArray();
            MyKvp.Remove(myKeys[myKeys.GetUpperBound(0) ]);
        }

        public void RemoveAll()
        {
            MyKvp.Clear();
        }

        public bool ValuesAreUnique()
        {
            return MyKvp.Count == MyKvp.Values.Distinct().Count();
        }

        public bool ValuesAreNotUnique()
        {
            return !ValuesAreUnique();
        }
    }
}

The VBA code below illustrates the issue when passing a Kvp from one method to another.

Option Explicit

Sub AssignToKvpItem()

    Dim myKvp: Set myKvp = New Kvp

    myKvp.AddByIndex 42&
    myKvp.AddByIndex 84&

    Debug.Print myKvp.Item(CLng(1))
    Debug.Print myKvp.Item(CLng(2))

    myKvp.Item(1&) = 99

    Debug.Print myKvp.Item(1&)

    TestPassedKvpAssignment myKvp

End Sub

Sub TestPassedKvpAssignment(ByVal ipKvp As Kvp)

    Debug.Print ipKvp.Item(1&)
    'This line produces an error 424 'object required
    ipKvp.Item(1&) = 1001
    ' So 1001 is not assigned
    Debug.Print ipKvp.Item(1&)

    Dim myObj As Object: Set myObj = ipKvp
    'This line works fine
    myObj.Item(1&) = 2002
    Debug.Print ipKvp.Item(1&)

End Sub

Below are some Rubberduck unit tests from when I last updated VBA tests for the Kvp class (I hang my head in shame that I am somewhat behind in implementing tests for all methods in the c# code provided above).

Option Explicit
'@IgnoreModule
'@TestModule
'@Folder("VBASupport")


Private Assert                                  As Rubberduck.AssertClass
'Private Fakes                                   As Rubberduck.FakesProvider

'@ModuleInitialize
Private Sub ModuleInitialize()
    'this method runs once per module.
    Set Assert = New Rubberduck.AssertClass
    'Set Fakes = New Rubberduck.FakesProvider
End Sub

'@ModuleCleanup
Private Sub ModuleCleanup()
    'this method runs once per module.
    Set Assert = Nothing
    'Set Fakes = Nothing
End Sub

''@TestInitialize
'Private Sub TestInitialize()
''this method runs before every test in the module.
'End Sub
'
'
''@TestCleanup
'Private Sub TestCleanup()
''this method runs after every test in the module.
'End Sub


'@TestMethod("Kvp")
Private Sub IsObject()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp

    'Assert:
    Assert.AreEqual "Kvp", TypeName(myKvp)

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub IsEmpty()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp

    'Assert:
    Assert.AreEqual True, myKvp.IsEmpty

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub IsNotEmpty()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByIndex "Hello World"
    'Assert:
    Assert.AreEqual True, myKvp.IsNotEmpty

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Count()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByIndex "Hello World"
    'Assert:
    Assert.AreEqual 1&, myKvp.Count

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Add_ByIndex()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByIndex "Hello World"
    'Assert:

    Assert.AreEqual "Hello World", myKvp.Item(1&)

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Add_ByIndexFromArray()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                                      As Kvp
    Dim MyArray(3)                                 As Variant
    Dim myResult_Array()                           As Variant
    'Act:
    MyArray(0) = "Hello"
    MyArray(1) = True
    MyArray(2) = 42
    MyArray(3) = 3.142

    Set myKvp = New Kvp
    myKvp.AddByIndexFromArray MyArray
    myResult_Array = myKvp.GetValues
    'Assert:

    Assert.SequenceEquals MyArray, myResult_Array

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Add_ByKey()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByKey Key:=22&, Value:="Hello World"
    'Assert:
    'Debug.Print myKvp.Item(CLng(1))
    Assert.AreEqual "Hello World", myKvp.Item(22&)

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub HoldsKey()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByKey Key:=22&, Value:="Hello World"
    'Assert:
    'Debug.Print myKvp.Item(CLng(1))
    Assert.AreEqual True, myKvp.HoldsKey(22&)

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub HoldsValue()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByKey Key:=22&, Value:="Hello World"
    'Assert:
    'Debug.Print myKvp.Item(CLng(1))
    Assert.AreEqual True, myKvp.HoldsValue("Hello World")

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub LacksValue()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByKey Key:=22&, Value:="Hello World"
    'Assert:
    'Debug.Print myKvp.Item(CLng(1))
    Assert.AreEqual True, myKvp.LacksValue(22&)

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub LacksKey()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByKey Key:=22, Value:="Hello World"
    'Assert:
    'Debug.Print myKvp.Item(CLng(1))
    Assert.AreEqual True, myKvp.LacksKey(80)

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Remove()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByKey Key:=22&, Value:="Hello World"
    'Assert:
    'Debug.Print myKvp.Item(CLng(1))
    If myKvp.LacksKey(22&) Then GoTo TestFail
    myKvp.Remove 22&
    Assert.AreEqual True, myKvp.LacksKey(22&)

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub RemoveAll()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByKey Key:=22&, Value:="Hello World 1"
    myKvp.AddByKey Key:=23&, Value:="Hello World 2"
    myKvp.AddByKey Key:=25&, Value:="Hello World 3"
    'Assert:
    'Debug.Print myKvp.Item(CLng(1))

    myKvp.RemoveAll
    Assert.AreEqual True, myKvp.IsEmpty

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub MakeFromString()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp1                              As Kvp
    Dim myKvp2                             As Kvp
    Dim myResult1()                    As Variant
    Dim myResult2()                    As Variant
    'Act:
    Set myKvp1 = New Kvp
    myKvp1.AddByKey Key:=22&, Value:="Hello World 1"
    myKvp1.AddByKey Key:=23&, Value:="Hello World 2"
    myKvp1.AddByKey Key:=25&, Value:="Hello World 3"

    Set myKvp2 = New Kvp
    myResult1 = myKvp1.GetValues

    myKvp2.AddByIndexAsSubStr "Hello World 1,Hello World 2,Hello World 3"
    myResult2 = myKvp2.GetValues
    'Assert:

    Assert.SequenceEquals myResult1, myResult2

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub GetKeys()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                               As Kvp
    Dim myKvp_keys(2)                       As Long
    Dim myResult_keys                       As Variant

    'Act:
    Set myKvp = New Kvp
    myKvp.AddByKey Key:=22&, Value:="Hello World 1"
    myKvp.AddByKey Key:=23&, Value:="Hello World 2"
    myKvp.AddByKey Key:=25&, Value:="Hello World 3"

    myKvp_keys(0) = 22&
    myKvp_keys(1) = 23&
    myKvp_keys(2) = 25&

    myResult_keys = myKvp.GetKeys
    'Assert:
    Assert.SequenceEquals myKvp_keys, myResult_keys

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub GetValues()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp                              As Kvp
    ' Dynamicops integers rather than long
    Dim myitems(2)                          As String
    Dim myKvp_items()                       As Variant
    'Act:
    Set myKvp = New Kvp
    myKvp.AddByKey Key:=22&, Value:="Hello World 1"
    myKvp.AddByKey Key:=23&, Value:="Hello World 2"
    myKvp.AddByKey Key:=25&, Value:="Hello World 3"

    myitems(0) = "Hello World 1"
    myitems(1) = "Hello World 2"
    myitems(2) = "Hello World 3"
    myKvp_items = myKvp.GetValues
    'Assert:
    Assert.SequenceEquals myitems, myKvp_items

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Cohorts_all_A_with_B_not_input_A()
    'On Error GoTo TestFail

    'Arrange:
    Dim myKvp1                                 As Kvp
    Dim myKvp2                                 As Kvp
    Dim myResult_keys(7)                       As Long
    Dim myResult                               As Kvp


    Dim myCohortKeys()                          As Variant
    'Act:
    Set myKvp1 = New Kvp
    myKvp1.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp1.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp1.AddByKey Key:=3&, Value:="Hello World 3a"
    myKvp1.AddByKey Key:=4&, Value:="Hello World 4"
    myKvp1.AddByKey Key:=5&, Value:="Hello World 5"
    myKvp1.AddByKey Key:=6&, Value:="Hello World 6"

    Set myKvp2 = New Kvp
    myKvp2.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp2.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp2.AddByKey Key:=3&, Value:="Hello World 3b"
    myKvp2.AddByKey Key:=6&, Value:="Hello World 6"
    myKvp2.AddByKey Key:=7&, Value:="Hello World 7"
    myKvp2.AddByKey Key:=8&, Value:="Hello World 8"

    myResult_keys(0) = 1&
    myResult_keys(1) = 2&
    myResult_keys(2) = 3&
    myResult_keys(3) = 4&
    myResult_keys(4) = 5&
    myResult_keys(5) = 6&
    myResult_keys(6) = 7&
    myResult_keys(7) = 8&

    'Debug.Print myKvp1(1&)
    'Debug.Print myKvp1(2&)
    ' Debug.Print myKvp1(3&)
    ' Debug.Print myKvp1(4&)
    ' Debug.Print myKvp1(5&)
    ' Debug.Print myKvp1(6&)

    Set myResult = myKvp1.Cohorts(myKvp2)
    myCohortKeys = myResult.Item(1&).GetKeys
    '    myCohortKeys = myResult(1&).GetKeys
    '    myCohortKeys = myResult(2&).GetKeys
    '    myCohortKeys = myResult(3&).GetKeys
    '    myCohortKeys = myResult(4&).GetKeys
    '    myCohortKeys = myResult(5&).GetKeys
    'Assert:
    Assert.SequenceEquals myResult_keys, myCohortKeys

    Set myKvp1 = Nothing
    Set myKvp2 = Nothing
    Set myResult.Item(0) = Nothing
    Set myResult.Item(1) = Nothing
    Set myResult.Item(2) = Nothing
    Set myResult.Item(3) = Nothing
    Set myResult.Item(4) = Nothing
    Set myResult.Item(5) = Nothing

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Cohorts_B_input_A_with_different_value()
    'On Error GoTo TestFail

    'Arrange:
    Dim myKvp1                                 As Kvp
    Dim myKvp2                                 As Kvp
    Dim myResult_keys(0)                       As Long
    Dim myResult                             As Kvp

    Dim myCohortKeys()                        As Variant
    'Act:
    Set myKvp1 = New Kvp
    myKvp1.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp1.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp1.AddByKey Key:=3&, Value:="Hello World 3a"
    myKvp1.AddByKey Key:=4&, Value:="Hello World 4"
    myKvp1.AddByKey Key:=5&, Value:="Hello World 5"
    myKvp1.AddByKey Key:=6&, Value:="Hello World 6"

    Set myKvp2 = New Kvp
    myKvp2.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp2.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp2.AddByKey Key:=3&, Value:="Hello World 3b"
    myKvp2.AddByKey Key:=6&, Value:="Hello World 6"
    myKvp2.AddByKey Key:=7&, Value:="Hello World 7"
    myKvp2.AddByKey Key:=8&, Value:="Hello World 8"

    myResult_keys(0) = 3&

    Set myResult = myKvp1.Cohorts(myKvp2)
    myCohortKeys = myResult.Item(2&).GetKeys
    'Assert:
    Assert.SequenceEquals myResult_keys, myCohortKeys

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Cohorts_input_A_only_and_input_B_only()
    'On Error GoTo TestFail

    'Arrange:
    Dim myKvp1                                 As Kvp
    Dim myKvp2                                 As Kvp
    Dim myResult_keys(3)                       As Long
    Dim myResult                             As Kvp

    Dim myCohortKeys()                        As Variant
    'Act:
    Set myKvp1 = New Kvp
    myKvp1.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp1.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp1.AddByKey Key:=3&, Value:="Hello World 3a"
    myKvp1.AddByKey Key:=4&, Value:="Hello World 4"
    myKvp1.AddByKey Key:=5&, Value:="Hello World 5"
    myKvp1.AddByKey Key:=6&, Value:="Hello World 6"

    Set myKvp2 = New Kvp
    myKvp2.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp2.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp2.AddByKey Key:=3&, Value:="Hello World 3b"
    myKvp2.AddByKey Key:=6&, Value:="Hello World 6"
    myKvp2.AddByKey Key:=7&, Value:="Hello World 7"
    myKvp2.AddByKey Key:=8&, Value:="Hello World 8"


    myResult_keys(0) = 4&
    myResult_keys(1) = 5&
    myResult_keys(2) = 7&
    myResult_keys(3) = 8&

    Set myResult = myKvp1.Cohorts(myKvp2)
    myCohortKeys = myResult.Item(3&).GetKeys

    'Assert:
    Assert.SequenceEquals myResult_keys, myCohortKeys

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Cohorts_input_both_A_and_B()
    'On Error GoTo TestFail

    'Arrange:
    Dim myKvp1                                 As Kvp
    Dim myKvp2                                 As Kvp
    Dim myResult_keys(3)                       As Long
    Dim myResult                             As Kvp

    Dim myCohortKeys()                        As Variant
    'Act:
    Set myKvp1 = New Kvp
    myKvp1.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp1.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp1.AddByKey Key:=3&, Value:="Hello World 3a"
    myKvp1.AddByKey Key:=4&, Value:="Hello World 4"
    myKvp1.AddByKey Key:=5&, Value:="Hello World 5"
    myKvp1.AddByKey Key:=6&, Value:="Hello World 6"

    Set myKvp2 = New Kvp
    myKvp2.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp2.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp2.AddByKey Key:=3&, Value:="Hello World 3b"
    myKvp2.AddByKey Key:=6&, Value:="Hello World 6"
    myKvp2.AddByKey Key:=7&, Value:="Hello World 7"
    myKvp2.AddByKey Key:=8&, Value:="Hello World 8"

    myResult_keys(0) = 1&
    myResult_keys(1) = 2&
    myResult_keys(2) = 3&
    myResult_keys(3) = 6&

    Set myResult = myKvp1.Cohorts(myKvp2)
    myCohortKeys = myResult.Item(4&).GetKeys

    'Assert:
    Assert.SequenceEquals myResult_keys, myCohortKeys


TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Cohorts_input_A_only()
    'On Error GoTo TestFail

    'Arrange:
    Dim myKvp1                                 As Kvp
    Dim myKvp2                                 As Kvp
    Dim myResult_keys(1)                  As Long
    Dim myResult                             As Kvp

    Dim myCohortKeys()                        As Variant
    'Act:
    Set myKvp1 = New Kvp
    myKvp1.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp1.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp1.AddByKey Key:=3&, Value:="Hello World 3a"
    myKvp1.AddByKey Key:=4&, Value:="Hello World 4"
    myKvp1.AddByKey Key:=5&, Value:="Hello World 5"
    myKvp1.AddByKey Key:=6&, Value:="Hello World 6"

    Set myKvp2 = New Kvp
    myKvp2.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp2.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp2.AddByKey Key:=3&, Value:="Hello World 3b"
    myKvp2.AddByKey Key:=6&, Value:="Hello World 6"
    myKvp2.AddByKey Key:=7&, Value:="Hello World 7"
    myKvp2.AddByKey Key:=8&, Value:="Hello World 8"

    myResult_keys(0) = 4&
    myResult_keys(1) = 5&

    Set myResult = myKvp1.Cohorts(myKvp2)
    myCohortKeys = myResult.Item(5&).GetKeys

    'Assert:
    Assert.SequenceEquals myResult_keys, myCohortKeys

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub Cohorts_input_B_only()
    'On Error GoTo TestFail

    'Arrange:
    Dim myKvp1                                 As Kvp
    Dim myKvp2                                 As Kvp
    Dim myResult_keys(1)                       As Long
    Dim myResult                             As Kvp

    Dim myCohortKeys()                        As Variant
    'Act:
    Set myKvp1 = New Kvp
    myKvp1.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp1.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp1.AddByKey Key:=3&, Value:="Hello World 3a"
    myKvp1.AddByKey Key:=4&, Value:="Hello World 4"
    myKvp1.AddByKey Key:=5&, Value:="Hello World 5"
    myKvp1.AddByKey Key:=6&, Value:="Hello World 6"

    Set myKvp2 = New Kvp
    myKvp2.AddByKey Key:=1&, Value:="Hello World 1"
    myKvp2.AddByKey Key:=2&, Value:="Hello World 2"
    myKvp2.AddByKey Key:=3&, Value:="Hello World 3b"
    myKvp2.AddByKey Key:=6&, Value:="Hello World 6"
    myKvp2.AddByKey Key:=7&, Value:="Hello World 7"
    myKvp2.AddByKey Key:=8&, Value:="Hello World 8"

    myResult_keys(0) = 7&
    myResult_keys(1) = 8&

    Set myResult = myKvp1.Cohorts(myKvp2)
    myCohortKeys = myResult.Item(6&).GetKeys

    'Assert:
    Assert.SequenceEquals myResult_keys, myCohortKeys


TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")

Private Sub Mirror()
    'On Error GoTo TestFail

    'Arrange:
    Dim myKvp1                                     As Kvp
    Dim myKvp2                                     As Kvp
    'Act:
    Set myKvp1 = New Kvp
    myKvp1.AddByKey Key:=22&, Value:="Hello World 1"
    myKvp1.AddByKey Key:=23&, Value:="Hello World 2"
    myKvp1.AddByKey Key:=25&, Value:="Hello World 3"
    myKvp1.AddByKey Key:=26&, Value:="Hello World 4"
    myKvp1.AddByKey Key:=27&, Value:="Hello World 5"

    Set myKvp2 = myKvp1.Mirror

    'Assert:
    Assert.SequenceEquals myKvp1.GetKeys, myKvp2.Item(1&).GetValues

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub

'@TestMethod("Kvp")
Private Sub ItemsAreUnique()
    On Error GoTo TestFail

    'Arrange:
    Dim myKvp1                                 As Kvp

    'Act:
    Set myKvp1 = New Kvp
    myKvp1.AddByKey Key:=22&, Value:="Hello World 1"
    myKvp1.AddByKey Key:=23&, Value:="Hello World 2"
    myKvp1.AddByKey Key:=25&, Value:="Hello World 3"
    myKvp1.AddByKey Key:=26&, Value:="Hello World 4"
    myKvp1.AddByKey Key:=27&, Value:="Hello World 5"



    'Assert:
    Assert.AreEqual True, myKvp1.ValuesAreUnique

TestExit:
    Exit Sub
TestFail:
    Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description
End Sub
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.