Iterate on union of several dictionaries

时间:2017-10-12 09:37:28

标签: c# linq

I have five dictionaries of type Dictionary<ThingId, Thing> and Dictionary<ThingId, List<Thing>>. I want to iterate on all of them with the following rules:

  1. Iterate on all the ThingId without duplicate
  2. For each key (aka each Id), get the list of Thing from all dictionaries without mixing them (they do not have the same functional meaning).

For now, I do this:

void DoSomething(Dictionary<ThingId, Thing> dic1, Dictionary<ThingId, List<Thing>> dic2, Dictionary<ThingId, List<Thing>> dic3) // only 3 to not clutter the code
{
    var ids = new HashSet<ThingId>(dic1.Keys).AddRange(dic2.Keys).AddRange(dic3.Keys);

    foreach (var id in ids)
    {
        Thing thing1;
        List<Thing> things2;
        List<Thing> things3;

        if (!dic1.TryGetValue(id, out thing1)
        {
            //default
            thing1 = new Thing(id);
        }
        if (!dic2.TryGetValue(id, out things2)
        {
            //default
            things2 = new List<Thing>();
        }
        if (!dic3.TryGetValue(id, out things3)
        {
            //default
            things3 = new List<Thing>();
        }
        DoSomethingElse(thing1, things2, things3);
    }
}

Is it possible to do this with Linq? For example do an union of the keys of the dictionaries and build anonymous class from the values (with the "default" values when needed)?

I looked at the Union method, but it does not what I want.

1 个答案:

答案 0 :(得分:1)

This one is pretty inefficient performance-wise, but with Linq:

    void DoSomething(Dictionary<ThingId, Thing> dic1, Dictionary<ThingId, List<Thing>> dic2, Dictionary<ThingId, List<Thing>> dic3) // only 3 to not clutter the code
    {
         dic1.Keys.Union(dic2.Keys).Union(dic3.Keys).Distinct().ToList().ForEach(id =>
            DoSomethingElse(
                dic1.FirstOrDefault(d => d.Key == id).Value ?? new Thing(id),
                dic2.FirstOrDefault(d => d.Key == id).Value ?? new List<Thing>(),
                dic3.FirstOrDefault(d => d.Key == id).Value ?? new List<Thing>())
        );
    }

Another way would be to extend your Dictionary and then use that extension:

    public static class DictionaryExtension
    {
        public static VType GetSafeValue<KType, VType>(this Dictionary<KType, VType> dic, KType key) where VType : class
        {
            VType v;
            if (!dic.TryGetValue(key, out v))
            {
                return null;
            }
            return v;
        }
    }

then you may use

dic1.Keys.Union(dic2.Keys).Union(dic3.Keys).Distinct().ToList().ForEach(id =>
            DoSomethingElse(
                dic1.GetSafeValue(id) ?? new Thing(id),
                dic2.GetSafeValue(id) ?? new List<Thing>(),
                dic3.GetSafeValue(id) ?? new List<Thing>())
        );