I currently have two collections: a Dictionary<Type,Func<int,bool>> Target to test against, and a List<(Type,int)> Items which is a value to test against the dictionary. The dictionary type is required to enforce unique entries elsewhere in code, but should otherwise behave as a collection in this test.
Let's say the dictionary has one item: {typeof(bool),(x)=>x==2}.
Let's say the List has one item: (typeof(bool),2).
I need to express that the above two collections are equal.
Let's reset and say the dictionary has two items: {typeof(bool), (x)=>x==2}, {typeof(int), (x)=>x==3}
...And the List has one item: (typeof(bool),3). This collection comparison is not equal because the Func evals to false.
...Change the one list item to : (typeof(bool),2) This is invalid because the Func evals to true, but missing an argument for the typeof(int) check.
...Change the list to have two items: (typeof(bool),2),(typeof(int),3). In this case, the two collections are equal.
How can I write an equality check for this in a performant manner?
I have tried these:
bool isEqual = Items.All(o => Target.TryGetValue(o.Type, out var f) && f(o.Count));
bool isEqual = Target.All(o => Items.Any(i => o.Key == i.Type && o.Value(i.Count)));
They both fail, for the following pseudo-explanation: they will be true for an unbalanced condition, i.e. the Target dict has more types than the Items, or the Items list has more types than Target.
Here is the code sample:
Dictionary<Type, Func<int, bool>> Target = new();
List<(Type Type, int Count)> Items = new();
Target.Add(typeof(bool), (x) => x == 2);
Items.Add((typeof(bool), 2));
Func<bool> test1 = () =>
Items.All(o => Target.TryGetValue(o.Type, out var f) && f(o.Count));
Func<bool> test2 = () =>
Target.All(o => Items.Any(i => o.Key == i.Type && o.Value(i.Count)));
var attempt1 = test1.Invoke();
var attempt2 = test2.Invoke();
Target.Add(typeof(int), (x) => x == 3);
// true, but wrong
var attempt3 = test1.Invoke();
// false, but returns true incorrectly if Target is one
// item and matches one item in a List of two items
var attempt4 = test2.Invoke();
Target is equal to Items if both have the same types with equal counts determined by evaluating the Func.
I think the above two Linq statements are bad because they are exponential, each iteration must iterate multiple times. Ideally, if I could match on type, return false if no matching types, then evaluate the Func, and return false if the count check fails. Repeat for each type. This would seem to be O(n) performance which would be better than exponential.
Do you have a good performant way to achieve the above?