2

I have two List<Dictionary<string, string>>

List<Dictionary<string, string>> ListdicA = new List<Dictionary<string, string>>();
Dictionary<string, string> dic1 = new Dictionary<string, string>();
dic1.Add("Type1", "1");
dic1.Add("Type2", "A");
dic1.Add("Type3", "X");
Dictionary<string, string> dic2 = new Dictionary<string, string>();
dic2.Add("Type1", "2");
dic2.Add("Type2", "B");
dic2.Add("Type3", "Y");
ListdicA.Add(dic1);
ListdicA.Add(dic2);

List<Dictionary<string, string>> ListdicB = new List<Dictionary<string, string>>();
Dictionary<string, string> dic3 = new Dictionary<string, string>();
dic3.Add("Type1", "1");
dic3.Add("Type2", "C");
dic3.Add("Type3", "X");
Dictionary<string, string> dic4 = new Dictionary<string, string>();
dic4.Add("Type1", "2");
dic4.Add("Type2", "D");
dic4.Add("Type3", "Z");
ListdicB.Add(dic3);
ListdicB.Add(dic4);

I want to merge the two lists and group the values by their keys. The result is a List<Dictionary<string, List<string>>>. First, I want to merge the two lists:

Type1 1
Type2 A
Type3 X

Type1 2
Type2 B
Type3 Y

Type1 1
Type2 C
Type3 X

Type1 2
Type2 D
Type3 Z

And I want group it by the keys, the result look like:

Type1 1
Type2 A C
Type3 X X

Type1 2
Type2 B D
Type3 Y Z
Fernando Matsumoto
  • 2,697
  • 1
  • 18
  • 24
Vũ Bích
  • 51
  • 2
  • 6
  • I'm not sure I understand the logical conditions for grouping. Could you modify your question to include details on how the different groupings should be done ? – Luc Morin Oct 18 '15 at 13:24
  • I want to merge two List and grouping it by Type1 @Luc Morin – Vũ Bích Oct 18 '15 at 13:35
  • You need to join the keys of the two dictionary using a outer join since you need to include all keys of both dictionaries. See following webpage : https://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b – jdweng Oct 18 '15 at 13:56
  • Why `X X` (while you don't have `1 1`)? – Gert Arnold Oct 18 '15 at 21:43

2 Answers2

1

Based on Question Update:

This is very complex (at least it was for me). I showed the steps and also i put each LINQ inside parenthesis which do the part of that job.

1) You have to join two lists. (ListdicA.Concat(ListdicB))

2) Group all Dictionaries by Value of Type1 key in each dictionaries.(GroupBy(x => x["Type1"]))

3) Unwrap all Items IGrouping<string,Dictionary<string,string>> to KeyValuePair<string,string> (SelectMany(y => y))

4) Again Group key value pairs by Key so that you join values of same keys together (GroupBy(y => y.Key))

5) Unwrap IGrouping<string,KeyValuePair<string,string>> into IEnumerable<KeyValuePair<string,List<string>>> (Select(y => new KeyValuePair<string, List<string>>(y.Key, y.Select(z => z.Value).ToList())))

6) Convert IEnumerable<IEnumerable<KeyValuePair<string,List<string>>>> into IEnumerable<Dictionary<string,List<string>>> (Select(x => x.ToDictionary(y => y.Key, y => y.Value)))

7) Finally Convert IEnumerable<Dictionary<string,List<string>>> into List<Dictionary<string, List<string>>> (ToList())

string groupby = "Type1";

List<Dictionary<string, List<string>>> result =
    ListdicA.Concat(ListdicB).GroupBy(x => x[groupby]).Select(x =>
        x.SelectMany(y => y) // Put .Distinct() here to remove duplicates.(optional)
            .GroupBy(y => y.Key).Select(y => new KeyValuePair<string, List<string>>(y.Key, y.Select(z => z.Value).ToList())))
                .Select(x => x.ToDictionary(y => y.Key, y => y.Value)).ToList();

foreach (var d in result)
{
    foreach (var k in d)
    {
        Console.Write(k.Key + " : ");
        foreach (var l in k.Value)
        {
            Console.Write(l + " ");
        }
        Console.WriteLine();
    }
}

Outputs

Type1 1 1
Type2 A C
Type3 X X

Type1 2 2
Type2 B D
Type3 Y Z

As I noted in comment (inside code) if you remove that comment and put .Distinct() right there it will remove duplicates

List<Dictionary<string, List<string>>> result =
    ListdicA.Concat(ListdicB).GroupBy(x => x[groupby]).Select(x => x.SelectMany(y => y)
        .Distinct().GroupBy(y => y.Key)
            .Select(y => new KeyValuePair<string, List<string>>(y.Key, y.Select(z => z.Value).ToList())))
                .Select(x => x.ToDictionary(y => y.Key, y => y.Value)).ToList();

will output.

Type1 1
Type2 A C
Type3 X

Type1 2
Type2 B D
Type3 Y Z

If you dont want to loose X then you need a custom Distinct. You will need this class. (linq was taken and edited from here)

public static class SomeMoreLinq
{
    public static IEnumerable<TSource> DistinctIf<TSource>
        (this IEnumerable<TSource> source, Func<TSource, bool> keySelector)
    {
        HashSet<TSource> seenKeys = new HashSet<TSource>();
        foreach (TSource element in source)
        {
            if (seenKeys.Add(element) || !keySelector(element))
            {
                yield return element;
            }
        }
    }
}

And then use it in your query.

List<Dictionary<string, List<string>>> result =
    ListdicA.Concat(ListdicB).GroupBy(x => x[groupby]).Select( x => x.SelectMany(y => y)
        .DistinctIf(y => y.Key == groupby).GroupBy(y => y.Key)
            .Select(y => new KeyValuePair<string, List<string>>(y.Key, y.Select(z => z.Value).ToList())))
                .Select(x => x.ToDictionary(y => y.Key, y => y.Value)).ToList();

will output.

Type1 1
Type2 A C
Type3 X X

Type1 2
Type2 B D
Type3 Y Z

If you have any questions feel free to ask.

While this completely melted my mind but it was a good practice!.

Community
  • 1
  • 1
M.kazem Akhgary
  • 18,645
  • 8
  • 57
  • 118
0

If you do

ListdicA.Zip(ListdicB, (a,b) => a.Concat(b)
.GroupBy(x => x.Key)
.Select (g => new
              {
                   g.Key, 
                   Values = g.SelectMany(d => d.Value).Distinct()
              } ))

you get this result:

Type 1   1
Type 2   A
         C
Type 3   X

Type 1   2
Type 2   B
         D
Type 3   Y
         Z

I'm not sure whether your X X result is deliberate, or a typo. If it's intended I should add some more logic.

Gert Arnold
  • 105,341
  • 31
  • 202
  • 291