LINQ: Convert Dictionary<int, List<MyObject>> to List<List<string>> - c#

My Dictionary<int, List<MyObject>> result has..
Key Value
1 {"Chicago", 100}
1 {"Newyork", 200}
2 {"Minneapolis", 300}
want to convert it to List<List<string>> in below format.
{"Index", "City","Value"},
{1, "Chicago", 100},
{1, "Newyork", 200}
{2, "Minneapolis", 300}
This is what I have achieved so far
var list = result.Select(rec => new
{
Index = rec.Key,
City = rec.Value.Select(rec1 => rec1.City),
Value = rec.Value.Select(rec1 => rec1.Value)
}).ToList();
What I am getting is this..
{"Index", "City", "Value"},
{1, System.Linq.Enumerable.WhereSelectListIterator<MyObject, string>, System.Linq.Enumerable.WhereSelectListIterator<MyObject, int>},
{1, System.Linq.Enumerable.WhereSelectListIterator<MyObject, string>, System.Linq.Enumerable.WhereSelectListIterator<MyObject, int>},
{2, System.Linq.Enumerable.WhereSelectListIterator<MyObject, string>, System.Linq.Enumerable.WhereSelectListIterator<MyObject, int>}
May be I am missing Where condition. Please suggest.
public class MyObject
{
public MyObject(){}
public string City{get;set;}
public int Value{get;set;}
}

This is what you need:
var Result = result.SelectMany(r => r.Value.Select(x => new[] { r.Key.ToString(), x.City, x.Value.ToString() }.ToList()));
To prepend column names as the first element of the outer list:
Result.Insert(0, {"Index", "City","Value"}.ToList());

Do you need the output like this?
I have a solution for you. Try it.
Dictionary<int, List<MyObject>> result = new Dictionary<int, List<MyObject>>();
result.Add(1, new List<MyObject>() { new MyObject() { City = "Chicago", Value = 100 }, new MyObject() { City = "Newyork", Value = 200 } });
result.Add(2, new List<MyObject>() { new MyObject() { City = "Minneapolis", Value = 300 } });
var resultYouWant = result.SelectMany(p => p.Value.Select(a => new { Index = p.Key, a.City, a.Value })).ToList();

below code is work for you but not getting what is your usecase.
var list = result.Select(rec => new
{
Index = rec.Key,
City = rec.Value.City),
Value = rec.Value.Value)
}).ToList();

City = rec.Value.Select(rec1 => rec1.City),
That is creating an IEnumerable, not a string. Which is why you get System.Linq.Enumerable.WhereSelectListIterator out of it.
You may be better off using for loops here.
foreach(var kvp in result)
foreach(var value in kvp)
//Create string here and add it to your list.

Related

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.
This can be done in a single Linq expression like so:
Flatten and concatenate both d1 and d2 to a single flat sequence of (Int32,String) value-tuples.
Re-group them by the Int32 key (this is the main step).
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:
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() );
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
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)}");
}
}
}
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,"") };
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
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();

Aggregating Lists of Users (Strings)

I have a Dictionary that looks like the following, with the key being an Integer and the value being a List of strings:
var x = new Dictionary<int, List<string>>;
I would like to see if any of those Lists match each other (without being in order) so that I can group them together in a role.
The final solution will look like
var y = new Dictionary<string, List<int>>
Where the List<int> is the keys from var x. The string key will be a machine generated string such as a guid, etc.
You can map all values to their keys and then group them by value and then apply ToDictionary, for expected result.
var data = new Dictionary<int, List<string>>
{
{ 1, new List<string> { "Adam", "Lucie" } },
{ 2, new List<string> { "Adam", "Hannah" } },
{ 3, new List<string> { "John", "Rachel" } },
{ 4, new List<string> { "Bill", "Hannah" } },
};
var result = data.SelectMany(p => p.Value.Select(v => new {Key = p.Key, Value = v}))
.GroupBy(o => o.Value)
.ToDictionary(g => g.Key, g => g.Select(v => v.Key));
foreach (var keyValues in result)
{
Console.WriteLine(keyValues.Key + ": " + string.Join(", ", keyValues.Value));
}

Combining lists in linq

in linq, is it possible to combine many lists (of the same type), such that two lists,
list 1 = {a,b,c} and list 2 = {x,y,z}
turns into {[1,a] , [1,b] , [1,c] , [2,x] , [2,y] , [2,z] }
where [] represents a pair containing a "list identifier"
The problem is from having decks of arbitrary cards, where each deck is a list in a collection of lists.
I'm trying to create a query such that I can select only cards in a certain deck, or cards similar to 2 or more decks.
This is probably a duplicate question, but I don't know how to search for the question further then I already have.
List<List<int>> lists;
var combined = lists.Select((l, idx) => new { List = l, Idx = idx })
.SelectMany(p => p.List.Select(i => Tuple.Create(p.Idx + 1, i)));
var list1 = new List<string>() {a,b,c};
var list2 = new List<string>() {x,y,z};
var combined = list1.Select(x => new { id = 1, v = x }).Concat(list2.Select(x => new { id = 2, v = x }));
Normally I'd suggest Enumerable.Zip for combining multiple lists, however you seem to actually want to concatenate multiple lists with a list counter.
public IEnumerable<Tuple<int,T>> Combine<T>(params IEnumerable<T>[] lists) {
return lists.Select((x,i) => x.Select(y => Tuple.Create(i+1,y))).SelectMany (l =>l);
}
UPDATE
Completely missed that SelectMany has the index option so the above code can be written as
public IEnumerable<Tuple<int,T>> Combine<T>(params IEnumerable<T>[] lists) {
return lists.SelectMany((x,i) => x.Select(y => Tuple.Create(i+1,y)));
}
Then you can do
var list1 = new List<string> { "a", "b", "c" };
var list2 = new List<string> { "x", "y", "z" };
var combined = Combine(list1,list2);
Combined will be enumerable of tuples, with Item1 being the list index identifier (starting at 1) and Item2 being the value.
This method will handle multiple lists so you could just as easily call it with:
var list3 = new List<string> { "f", "g" };
var combined = Combine(list1,list2,list3);
You can merge the lists like:
var first = new List<string> {"a","b","c"};
var second = new List<string> {"x","y","z"};
var merged = first.Select(item => new { ListIndex = 1, Value = item}).ToList();
merged.AddRange(second.Select(item => new { ListIndex = 2, Value = item});
//or use concat
var merged = first.Select(item => new { ListIndex = 1, Value = item});
.Concat(second.Select(item => new { ListIndex = 2, Value = item});
Alternatively if you have the sources in something like:
List<List<string>> lists = new List<List<string>>
{
new List<string> {"a","b","c"},
new List<string> {"x","y","z"}
};
you can do:
var merged = lists.SelectMany((item, index) =>
item.Select(s => new { ListIndex = index, Value = s}));
Note that this will produce a 0-based list, so if you really need a 1-base list, just do ListIndex = index +1.
Also, if you will use this a lot, I would create it as an specific entity, something like
struct ListIdentValue
{
public int ListIndex {get; private set;}
public string Value {get; private set;}
public ListIdentValue(int listIndex, string value) {...}
}
Try using Concat
new[] {'a','b','c'}
.Select(v=>new Tuple<int,char>(1, v))
.Concat(
new[] {'x','y','z'}.Select(v=>new Tuple<int,char>(2, v))
)
string[] a = { "a", "b", "c" };
string[] b = { "x", "z", "y" };
var t =
(
from ai in a
select new { listNo = 1, Item = ai }
).Union
(
from bi in b
select new { listNo = 2, Item = bi }
);
or
var t =
(
from ai in a
select new object[] { 1, ai }
).Union
(
from bi in b
select new object[] { 2, bi }
);

Generate a new list with key

i would like to create a new list with key and values
List<object> r = new List<object>();
r.Add("apple");
r.Add("John");
return r;
when u Addwatch the r, you will see
[1] = apple
[2] = John
Questions: How do i make the [1] and [2] to be new key? When i addwatch the r, i would like to see [1] is replaced by Name. something as below:
Name = apple
TeacherName = John
Do you mean you want to use something like Dictionary<TKey, TValue>
example:
Dictionary<string, string> d = new Dictionary<string, string>();
d.Add("Name", "Apple");
d.Add("Teacher", "John");
or do you want an object to more strongly typed?
in this case you have to use your one class / struct
class MyObject
{
public string Name {get; set;}
public string Teacher {get; set;}
}
Then
var list = new List<MyObject>();
list.Add(new MyObject { Name = "Apple", Teacher = "John" });
list.Add(new MyObject { Name = "Banana", Teacher = "Setphan" });
then you can all it
var item = list[0];
var name = item.Name;
var teacher = item.Teacher;
It is completely incorrect to use a list for this kind of a data structure. You need to use use Dictionary , NameValueCollection or similar type.
You can transform your list:
List<object> r = new List<object>();
r.Add("apple");
r.Add("John");
r.Add("orange");
r.Add("Bob");
var dict = r.Where((o, i) => i % 2 == 0)
.Zip(r.Where((o, i) => i % 2 != 0), (a, b) => new { Name = a.ToString(), TeacherName = b.ToString() });
foreach (var item in dict)
{
Console.WriteLine(item);
}
Output:
{ Name = apple, TeacherName = John }
{ Name = orange, TeacherName = Bob }
And then transform to dictionary:
var result = dict.ToDictionary(d => d.Name, d => d.TeacherName);
You will need to use a Dictionary to do this. Not a List.
See http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
I hope i dont make any syntax mistakes here...
Dictionary <string, int> r = new Dictionary<string,int>();
r.add("apple",1);
r.add("John",2)
console.WriteLine(r["apple"]);//returns value 1
Your question is not clear and hard to understand.
Do you mean to say you want keys instead of indexes ? Like Name instead of 1
Well then as Aliza and Bumble Bee have said you need to use a Dictionary instead of a List.
Here's a small example
IDictionary<string, Interval> store = new Dictionary<string, string>();
store.Add("Name","apple");
store.Add("TeacherName ", John);
foreach(KeyValuePair<string, string> e in store)
Console.WriteLine("{0} => {1}", e.Key, e.Value);

Combining Dictionary<A,B> + Dictionary<B,C> to create Dictionary<A,C>

Any cool quick ways to take two dictionaries to create a third that maps the key of the first to the value of the second in an inner-join style?
Dictionary<A,B> dic1 = new Dictionary<A,B> {{a1,b1},{a2,b2},{a3,b3}};
Dictionary<B,C> dic2 = new Dictionary<B,C> {{b1,c1},{b2,c2},{b4,c4}};
Dictionary<A,C> dic3 = SomeFunction(dic1,dic2);
// dic3 = {{a1,c1},{a2,c2}}
You could do something like this to join on the inner value
Dictionary<int, string> first = new Dictionary<int, string> { {1, "hello"}, {2, "world"}};
Dictionary<string, bool> second =
new Dictionary<string, bool> { { "hello", true }, {"world", false}};
var result = (from f in first
join s in second on f.Value equals s.Key
select new { f.Key, s.Value }).ToDictionary(x => x.Key, y => y.Value);
If you dump out result you'll see it is a Dictionary with the value {1: true, 2: false}
try this -
Dictionary<string, string> a, b, c;
//code to initialize the 3 dictionaries. a,b are original dictionaries and c is the new dictionary
c = ( from ak in a.Keys
where b.ContainsKey( ak )
select new KeyValuePair<string, string>( ak, b[ ak ] ) ).ToDictionary( d => d.Key, d=> d.Value );
Maybe something with ToDictionary
dic1.Where(d1=>dic2.ContainsKey(d1.Value)).ToDictionary(d1=>d1.Key,d1=>dic2[d1.Value]);
Dictionary<int, string> dic1 = new Dictionary<int,string>();
Dictionary<string, decimal> dic2 = new Dictionary<string,decimal>();
dic1.Add(1, "one");
dic1.Add(2, "two");
dic1.Add(3, "three");
dic1.Add(4, "four");
dic1.Add(5, "five");
dic2.Add("one",1.0m);
dic2.Add("two", 2.0m);
dic2.Add("three", 3.0m);
dic2.Add("four", 4.0m);
dic2.Add("five", 5.0m);
Dictionary<int, decimal> result = (from d1 in dic1
from d2 in dic2
where d1.Value == d2.Key
select new { d1.Key, d2.Value }).ToDictionary(p=>p.Key, p=>p.Value);
public Dictionary<A,C> SomeFunction(dic1, dic2)
{
var dic3 = new Dictionary<A,C>();
foreach (var item in dic1)
{
var item2 = dic2.Where(m=>m.Key == item.Value).FirstOrDefault();
if (item2 != null)
{
dic3.Add(item.Key, item2.Value);
}
}
return dic3
}
I believe this will work for what you want
public IDictionary<A, C> SomeFunction<A, B, C>(IDictionary<A, B> dic1, IDictionary<B, C> dic2)
{
var dic3 = new Dictionary<A, C>();
foreach (var item in dic1)
{
var a = item.Key;
var b = item.Value;
if (dic2.ContainsKey(b))
{
var c = dic2[b];
dic3.Add(a, c);
}
}
return dic3;
}
Handles the case of dic2 not containing keys corresponding to dic1s value without fake null values being stored, and IMO is pretty clear. I do like some LINQ, but I thought I'd give a procedural answer for once...
The simplest solution:
var dict3 = dict1.ToDictionary(p => p.Key, p => dict2[p.Value]);

Categories

Resources