user101464
user101464

Reputation: 35

Combining values of two dictionaries in C# with distinct keys

I am having difficulty combining two dictionaries into a dictionary with two values combined to a list for identical keys. For example, having D1 and D2

D1 =    {2:"a", 
         3:"b", 
         4: "c"}

D2 =   {1:"e", 
        2:"f",      
        4:"h", 
        5:"i"}

I would like to create D3.

D3= {   1:["", "e"]
        2:["a", "f"]
        3:["b", ""]
        4:["c":"h"]
        5:["", "i"]}

Thank you.

Upvotes: 0

Views: 888

Answers (6)

Surbhi Khandelwal
Surbhi Khandelwal

Reputation: 11

Please see, here is another approach for this query -

Input -

Dictionary<int, string> first_dict = new Dictionary<int, string>()
        {
            { 2,"a" },
            { 3,"b" },
            { 4, "c"}
        };

        Dictionary<int, string> second_dict = new Dictionary<int, string>()
        {
            { 1,"e" },
            { 2,"f" },
            { 4, "h"},
            { 5, "i"}
        };

First, I got common keys from both dictionaries like this -

var allKeys = first_dict.Concat(second_dict).OrderBy(b => b.Key).Select(b => b.Key).Distinct().ToList();

and then I created two another dictionaries and inserted data into them like this -

Dictionary<int, string> first_dict_res = new Dictionary<int, string>();
        Dictionary<int, string> second_dict_res = new Dictionary<int, string>();

        foreach (var keyItem in allKeys)
        {
            var first_dict_res_value = (first_dict.ContainsKey(keyItem)) ? first_dict[keyItem] : null;
            first_dict_res.Add(keyItem, first_dict_res_value);

            var second_dict_res_value = (second_dict.ContainsKey(keyItem)) ? second_dict[keyItem] : null;
            second_dict_res.Add(keyItem, second_dict_res_value);
        }

and then I concatenated the result from both dictionaries to get the desired result-

 var res_dict = first_dict_res.Concat(second_dict_res).GroupBy(b => b.Key)
                        .Select(c => new { key = c.Key, values = string.Join(",", c.Select(b => b.Value)) }).ToList();

Upvotes: 0

Dai
Dai

Reputation: 155726

This can be done in a single Linq expression like so:

  1. Flatten and concatenate both d1 and d2 to a single flat sequence of (Int32,String) value-tuples.
  2. Re-group them by the Int32 key (this is the main step).
  3. Then convert each group into a separate output dictionary entry.
Dictionary<Int32,String> d1 = new Dictionary<Int32,String>()
{
    { 2, "a" },
    { 3, "b" },
    { 4, "c" },
};

Dictionary<Int32,String> d2 = new Dictionary<Int32,String>()
{
    { 1, "e" },
    { 2, "f" },
    { 4, "h" },
    { 5, "i" },
};

Dictionary<Int32,List<String>> d3 = Array
    .Empty<( Int32 k, String v )>()
// Step 1:
    .Concat( d1.Select( kvp => ( k: kvp.Key, v: kvp.Value ) ) )
    .Concat( d2.Select( kvp => ( k: kvp.Key, v: kvp.Value ) ) )
// Step 2:
    .GroupBy( t => t.k )
// Step 3:
    .ToDictionary(
        grp => grp.Key,
        grp => grp.Select( t => t.v ).OrderBy( v => v ).ToList()
    );

An advantage of this approach is that it works for any number of duplicated values (not just two). Also, the use of ValueTuple means this approach should have fewer heap-allocations.

Screenshot proof of it working in LinqPad:

enter image description here


The expression can be made more succint - I use a more verbose style myself, but if you want to be cryptic about it by re-using KeyValuePair instead of ValueTuple, and if you don't care about ordering, then you can do this:

var d3 = d1
    .Concat( d2 )
    .GroupBy( kvp => kvp.Key )
    .ToDictionary( g => g.Key, g => g.Select( kvp => kvp.Value ).ToList() );

Upvotes: 2

TheGeneral
TheGeneral

Reputation: 81593

This doesn't need another answer the others answers are plenty good enough and well done, however here is another (convoluted) approach

Given

var d1 = new Dictionary<int, string> {{2, "a"}, {3, "b"}, {4, "c"}};
var d2 = new Dictionary<int, string> { { 1, "e" }, { 2, "f" }, { 4, "h" }, { 5, "i" } };

Usage

static string[] Stuff((string, string)[] v) =>
   new[] {v[0].Item1 ?? v.ElementAtOrDefault(1).Item1 ?? "", v[0].Item2 ?? v.ElementAtOrDefault(1).Item2 ?? "" };

var result = d1
   .Select(x => (x.Key, (x.Value,(string)null)))
   .Concat(d2.Select(x => (x.Key, ((string)null, x.Value ))))
   .GroupBy(element => element.Key)
   .ToDictionary(x => x.Key, x => Stuff(x.Select(y =>y.Item2).ToArray()))

Output

foreach (var item in result.OrderBy(x => x.Key))
   Console.WriteLine($"{item.Key}: {string.Join(",", item.Value)}");

---

1: ,e
2: a,f
3: b,
4: c,h
5: ,i

Upvotes: 0

PawZaw
PawZaw

Reputation: 1438

Simplest solution would be with Dictionary.Keys

var D1 = new Dictionary<int,string>(){{2,"a"}, {3,"b"},{4,"c"}};
var D2 = new Dictionary<int,string>(){{1,"e"},{2,"f"}, {4,"h"},{5,"i"}};

var keys = D1.Keys.Union(D2.Keys).OrderBy(key => key);
var test = keys.Select(key => new {Key = key, Value= new string[]    {D1.ContainsKey(key) ? D1[key] : "", D2.ContainsKey(key) ? D2[key] : ""} });
Console.WriteLine(test);

Interactive: https://rextester.com/UXQ51844

Alternatively, you could do something similar to this: LINQ - Full Outer Join

Upvotes: 1

Caius Jard
Caius Jard

Reputation: 74740

I think this is simpler without LINQ (LINQ is a hammer, not every problem is a nail)

Let's loop from 1 to 5, putting a new List for each int. The list is inited with d1's value and d2's value

var d3 = new Dictionary<int, List<string>>();

for(int x=1;x<6;x++)
  d3[x] = new() { d1.GetValueOrDefault(x,""), d2.GetValueOrDefault(x,"") };

If your ints aren't always contiguous you could (use a bit of LINQ 😀 and..)

foreach(int x in d1.Keys.Union(d2.Keys))
  d3[x] = new() { d1.GetValueOrDefault(x,""), d2.GetValueOrDefault(x,"") };

Upvotes: 0

Oliver
Oliver

Reputation: 45109

Sounds to be a job for LINQ. Here is one possibility to solve this issue:

public class Element
{
    public int Index { get; set; }
    public string Value { get; set; }
}

public class GroupedElement
{
    public int Index { get; set; }
    public IReadOnlyList<string> Values { get; set; }
}

public static class Program
{
    public static void Main(string[] args)
    {
        var d1 = new[]
        {
            new Element { Index = 2, Value = "a" },
            new Element { Index = 3, Value = "b" },
            new Element { Index = 4, Value = "c" },
        };

        var d2 = new[]
        {
            new Element { Index = 1, Value = "e" },
            new Element { Index = 2, Value = "f" },
            new Element { Index = 4, Value = "h" },
            new Element { Index = 5, Value = "i" },
        };

        var result = d1.Concat(d2)
            .GroupBy(element => element.Index)
            .Select(group => new GroupedElement { Index = group.Key, Values = group.Select(g => g.Value).ToList() })
            .ToList();

        foreach (var item in result)
        {
            Console.WriteLine($"{item.Index}: {string.Join(",", item.Values)}");
        }
    }
}

Upvotes: 1

Related Questions