1

I have 2 dictionaries of type Dictionary<int, (int, int)>, first int being their key, (int, int) being their values.

Using var intersections = dict1.Values.Intersect(dict2.Values); I can compare values and return an IEnumerable of all values that coincide between the two, however that doesn't give me the keys. And using var intersections = dict1.Keys.Intersect(dict2.Keys); will return keys appearing in both dictionaries, which is... every single key because the keys just start at 1 and increment by 1 for each entry, so it's useless.

I want to compare the entries by their values, and then have access to their keys. So for example, if the entry (12, 36) is present at Key 20 in dict1 and at Key 45 in dict2, I'd like to have a way to access 20 and 45.

I'm at a complete loss. I'm able to compare values and return values, I'm able to compare keys and return keys, but I'm unable to compare values and return keys. How do?

Thanks!

SebC
  • 13
  • 2
  • 2
    Describe the problem you're attempting to solve instead of describing your solution to that problem that you can't implement. – Moho Dec 03 '19 at 20:49
  • 1
    Is it impossible for any value to appear in either dictionary more than once? – NetMage Dec 03 '19 at 22:06
  • The values of a `Dictionary` are not guaranteed to be unique. So, considering that the two dictionaries could have matched duplicate values, what is the expected output of the intersect operation? For example `dict1: {{1 => "A"}, {2 => "A"}}`, `dict2: {{11 => "A"}, {12 => "A"}}`. The output should associate the value `"A"` with the keys `1, 2` of the first dictionary, and `11, 12` of the second dictionary. Do you want an output of type `Dictionary, List)>`? – Theodor Zoulias Dec 04 '19 at 04:00

3 Answers3

1
dict1.Union(dict2)
    .GroupBy(kvp => kvp.Value)
    .Where(g => g.Count() > 1) // but this doesn't account for equal values in same dictionary if that's important
    .Select(g => new
    {
        Value = g.Key,
        Keys = g.Select(kvp => kvp.Key),
    });

Alternatively, you can join on the Value property of the KeyValuePair<TKey,TValue> objects of each dictionary.

Moho
  • 15,457
  • 1
  • 30
  • 31
0

You can create your own IEqualityComparer and use the Intersect overload which asks for it.

static void Main(string[] args)
{
    var dict1 = new Dictionary<int, (int, int)>();
    dict1.Add(1, (1, 1));
    dict1.Add(2, (2, 2));
    dict1.Add(3, (3, 3));
    var dict2 = new Dictionary<int, (int, int)>();
    dict2.Add(4, (2, 2));
    dict2.Add(5, (3, 3));
    dict2.Add(6, (4, 4));

    var intersection = dict1.Intersect(dict2, new eq());

    foreach (var i in intersection)
        Console.WriteLine($"Key: {i.Key}, Value: {i.Value}");
    Console.ReadLine();
}

class eq : IEqualityComparer<KeyValuePair<int, (int, int)>>
{
    public bool Equals(KeyValuePair<int, (int, int)> x, KeyValuePair<int, (int, int)> y)
    {
        return x.Value == y.Value;
    }

    public int GetHashCode(KeyValuePair<int, (int, int)> obj)
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 23 + obj.Value.Item1;
            hash = hash * 23 + obj.Value.Item2;
            return hash;
        }
    }
}

Key: 2, Value: (2, 2)
Key: 3, Value: (3, 3)

Hash from this answer from Jon Skeet

djv
  • 15,168
  • 7
  • 48
  • 72
  • Ooh this is almost perfect! However this looks like it's also comparing keys, while I only want to compare the values only then return the keys attached to it. In your example, if (2,2) was set to Key 2 in dict1, but to Key 5 in dict2, I would still like to find this pair, and obtain 2 + 5 as a result. – SebC Dec 03 '19 at 21:21
  • @SebC reading the comment makes it clear intersect is not right. Maybe with some more work... I'll think about it – djv Dec 03 '19 at 21:40
0

You can simply use a Where filter and check if the other dictionary contains the value:

var matches = dict1.Where(d => dict2.ContainsValue(d.Value));

This will return an enumerable that you can then use ToDictionary() or ToList() as needed.

EDIT:

Use this Union to return both sides of the match:

dict1.Where(d => dict2.ContainsValue(d.Value))
     .Union(dict2.Where(d => dict1.ContainsValue(d.Value)));

HTH

Canica
  • 2,650
  • 3
  • 18
  • 34
  • @SebC this should work as desired. see this fiddle: https://dotnetfiddle.net/k2WBLB – Canica Dec 03 '19 at 21:54
  • Seems like the best option yes, and is working great for a small sample, thanks a lot! :) For bigger samples (where thousands of matches are found) this seems to freeze everything though, although maybe it's just taking so much time to compute it's essentially looking like a freeze.. What kind of variable is this var turning into? It's just saying "(local variable) ?". Without knowing what kind of variable I'm working with, it'll be hard to filter out only a portion of the results before sending them into a foreach (I'm looking for the smaller keys of the bunch). – SebC Dec 03 '19 at 22:39
  • Update: Unfroze after around 10 minutes of computing ahah. But I did get the list I wanted, so thanks a lot for the help guys! :) – SebC Dec 04 '19 at 01:43