3

I need to check if there has been a change in a certain part of the application and therefore I make "copies" of the data after loading them and then compare them. One part of the comparison function involves checking keys in dictionaries like lDict1.Keys.EqualsTo(lDict2.Keys).

Although the dictionaries do not rely on the order of the elements, I didn't realize that even if I fill two dictionaries with the same data, they won't be created the same and the order of elements may change, so the previous function does not work properly because it relies on the elements order that may not match when using any of the following methods. (I'm not sure why)

var
  lDict1, lDict2 : IDictionary<Integer, TObject>;
  lKey : Integer;
begin
  lDict1 := TCollections.CreateDictionary<Integer, TObject>;
  lDict1.Add(5, nil); // Keys.First = 5, Keys.Last = 5
  lDict1.Add(6, nil); // Keys.First = 5, Keys.Last = 6
  lDict2 := TCollections.CreateDictionary<Integer, TObject>;
  lDict2.AddRange(lDict1); // Keys.First = 6, Keys.Last = 5
  lDict2.Clear;
  for lKey in lDict1.Keys do // Keys.First = 6, Keys.Last = 5
    lDict2.Add(lKey, nil);
end;

Is there any way to make an exact copy of the dictionary so I can compare them? One way to work around this problem is to create my own comparison function, but I'd like to avoid that.

function ContainsSameValues<T>(AEnumerable1, AEnumerable2: IEnumerable<T>): Boolean;
var
  lValue : T;
begin
  Result := AEnumerable1.Count = AEnumerable2.Count;
  if Result then
  begin
    for lValue in AEnumerable1 do
    begin
      Result := AEnumerable2.Contains(lValue);
      if not Result then
        Exit;
    end;
  end;
end;

usage

ContainsSameValues<Integer>(lDict1.Keys, lDict2.Keys);
Triber
  • 1,525
  • 2
  • 21
  • 38
  • 2
    There is no guaranteed way to make an "exact copy" (i.e. preserving the key order). – Olivier Apr 22 '20 at 13:09
  • 2
    The dictionary implementation in the latest release of spring is ordered. I did the original work on that new dictionary implementation, modelled on the Python ordered dictionary. That said, I'm not sure that using an ordered dictionary would be enough to solve your problem. After all, do you really want to know whether or not the items were added in the same order, or do you actually want to know whether the dictionaries contain the same key/value pairs? – David Heffernan Apr 22 '20 at 13:09
  • 4
    So, for me, if you want to check equality, just enumerate one dictionary, and check if the key/value pair matches in the other dictionary. If the two dictionaries have the same number of items, and all the key/value pairs from one can be found in the other, then they are the same. – David Heffernan Apr 22 '20 at 13:13
  • See also [Sorting TDictionary by a key of Integer in ascending order](https://stackoverflow.com/a/31257382/576719). – LU RD Apr 22 '20 at 13:15
  • @DavidHeffernan I guess you're right, I just wanted simple and quick solution, which is why i used this approach. – Triber Apr 22 '20 at 13:41
  • @LURD hmm, I think sorting is the last thing that you'd want to do. – David Heffernan Apr 22 '20 at 13:51
  • @DavidHeffernan, ordering or sorting is related, I think. Anyway, matching pairs, as you suggested, would be the ultimate solution. Why not make that an answer? – LU RD Apr 22 '20 at 15:46
  • @LURD sorting typically involves mutating one of the collections which is unlikely to be desirable. – David Heffernan Apr 22 '20 at 17:58

1 Answers1

4

Checking for equality of a unordered dictionaries is a relatively simple algorithm. I will outline it here. Suppose we have two dictionaries, A and B.

  1. Compare the number of elements of A and B. If this differs, the dictionaries are not equal.
  2. Enumerate each key/value pair k,v in A. If k is not in B, or B[k] is not equal to v, then the dictionaries are not equal.
  3. If you reach the end of the enumeration, then you know that the dictionaries are equal.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490