I need help building a Linq query. I have this dictionary :
var dict = new Dictionary<string, IDictionary<int, double>>
{
{ "one", new Dictionary<int, double>
{
{ 1, 2.0 },
{ 2, 3.0 }
}},
{ "two", new Dictionary<int, double>
{
{ 1, 3.0 },
{ 2, 4.0 },
{ 3, 5.0 }
}},
{ "three", new Dictionary<int, double>
{
{ 1, 4.0 },
{ 2, 5.0}
}}
};
I want to select all "string"/"int" tuples whose associated value is 3.0. With Foreach loops, it looks like :
var res = new Dictionary<string, int>();
foreach (var elem in dict.Select (d => new { S = d.Key, I = d.Value }))
{
foreach (var val in elem.I)
{
if (val.Value == 3.0)
{
res.Add(elem.S, val.Key);
}
}
}
I'm trying to do the same with a single Linq query but with no success (I don't know how to "join" the key with the value from a subquery). How would you do this?
Thank you in advance!
You could do it this way:
var result = dict
// flatten to 3-value tuple
.SelectMany(kvp => kvp.Value
.Select(kvp2 => new // anonymous object
{
OuterKey = kvp.Key, // e.g. "one"
InnerKey = kvp2.Key, // e.g. 1
InnerValue = kvp2.Value // e.g. 2.0
})
).Where(o => o.InnerValue == 3.0) // limit to the 3.0 value
.ToDictionary(o => o.OuterKey, o => o.InnerKey)
;
The SelectMany flattens the dictionary of dictionaries into a structure that looks like this:
{ OuterKey = "one", InnerKey = 1, InnerValue = 2.0 },
{ OuterKey = "one", InnerKey = 2, InnerValue = 3.0 },
{ OuterKey = "two", InnerKey = 1, InnerValue = 3.0 },
{ OuterKey = "two", InnerKey = 2, InnerValue = 4.0 },
{ OuterKey = "two", InnerKey = 3, InnerValue = 5.0 },
{ OuterKey = "three", InnerKey = 1, InnerValue = 4.0 },
{ OuterKey = "three", InnerKey = 2, InnerValue = 5.0 }
The Where() limits it down to just the objects with InnerValue = 3.0:
{ OuterKey = "one", InnerKey = 2, InnerValue = 3.0 },
{ OuterKey = "two", InnerKey = 1, InnerValue = 3.0 }
The ToDictionary() looks like this:
{ "one", 2 },
{ "two", 1 }
If it's possible for there to be more than a single 3.0 under the same outer key, then you can use ToLookup() instead of ToDictionary().
Here's another butt-ugly way to do it:
var results = from key1 in dict.Keys
let valueDict = dict[key1]
from key2 in valueDict.Keys
where valueDict[key2] == 3d
select new { Key1 = key1, Key2 = key2 };
This would do the trick:
var res = dict
.Where(outer => outer.Value.Any(inner => inner.Value == 3.0))
.ToDictionary(outer => outer.Key, outer => outer.Value.First(x=>x.Value == 3.0).Key);
or to make the code a bit more generic:
var predicate = new Func<KeyValuePair<int, double>, bool>(inner => inner.Value == 3.0);
var res = dict
.Where(outer => outer.Value.Any(inner => predicate(inner)))
.ToDictionary(outer => outer.Key, outer => outer.Value.First(inner => predicate(inner)).Key);
Related
I have two lists of the following class:
public class KeyValues
{
public int Key { get;set;}
public List<double> DoubleList { get;set;}
}
var list1 = new List<KeyValues>();
list1.Add (new KeyValues(){Key = 33, DoubleList = new List<double>(){2.3,2.4,2.5}});
list1.Add (new KeyValues(){Key = 34, DoubleList = new List<double>(){3.3,3.4,3.5}});
list1.Add (new KeyValues(){Key = 35, DoubleList = new List<double>(){4.3,4.4,4.5}});
var list2 = new List<KeyValues>();
list2.Add (new KeyValues(){Key = 33, DoubleList = new List<double>(){20.3,20.4}});
list2.Add (new KeyValues(){Key = 34, DoubleList = new List<double>(){30.3,30.4}});
list2.Add (new KeyValues(){Key = 35, DoubleList = new List<double>(){40.3,40.4}});
I would like to combine those into a new list by mathing the keys and combining the sub lists. So the result should be:
list3 = [
[33, {2.3,2.4,2.5,20.3,20.4}]
[34, {3.3,3.4,3.5,30.3,30.4}]
[35, {4.3,4.4,4.5,40.3,40.4}]
]
Of course I can iterate over the keys, but isn't there a much better (faster) linq solution for this?
Using Concat and GroupBy methods can produce the expected result (after making a code compile, of course)
var result = list1.Concat(list2).GroupBy(kv => kv.Key, kv => kv.DoubleList)
.Select(g => new KeyValues { Key = g.Key, DoubleList = g.SelectMany(i => i).ToList() });
You can concatenate the two lists together and then use GroupBy, and then Select to project the output into the desired format. For example:
var list3 = list1.Concat(list2)
.GroupBy(x => x.Key)
.Select(x => new KeyValue
{
Key = x.Key,
DoubleList = x.SelectMany(x => x.DoubleList).ToList()
})
.ToList();
You're example is not syntactically correct:
Should be like this:
var list1 = new List<KeyValue>();
list1.Add(new KeyValue { Key = 33, DoubleList = new List<double>() { 2.3, 2.4, 2.5 } });
list1.Add(new KeyValue { Key = 34, DoubleList = new List<double>() { 3.3, 3.4, 3.5 } });
list1.Add(new KeyValue { Key = 35, DoubleList = new List<double>() { 4.3, 4.4, 4.5 } });
var list2 = new List<KeyValue>();
list2.Add(new KeyValue { Key = 33, DoubleList = new List<double>() { 20.3, 20.4 } });
list2.Add(new KeyValue { Key = 34, DoubleList = new List<double>() { 30.3, 30.4 } });
list2.Add(new KeyValue { Key = 35, DoubleList = new List<double>() { 40.3, 40.4 } });
list1.Zip(list2, (first, second) => (first.Key, first.DoubleList.Concat(second.DoubleList));
If the lists have the same element keys in the same order you can do this:
list1.Zip(list2, (first, second) =>
(first.Key, first.DoubleList.Concat(second.DoubleList)));
My first suggestion here is to use Dicitionary<int, List<double>> instead of creating a class KeyValues or if you want specifically to use Key-Value pair, then .net have that too, it's called: KeyValuePair.
Just in case if you are interested, here is the way to convert your class to Dictionary:
var list3 = list1.Concat(list2)
.ToDictionary(item => item.Key);
Note: this will actually fail because of key duplications, so in case if you don't want that to happen, make sure that Keys are different.
Update:
Or you can use Lookup<TKey, TElement> to make it work even with duplicate keys:
var list3 = list1
.Concat(list2)
.ToLookup(group => group.Key, group => group.DoubleList)
.ToDictionary(group => group.Key, group => group.First());
One suggestion: you can use a collection initializer instead of adding elements one by one:
var list1 = new List<KeyValues>
{
new KeyValues {Key = 33, DoubleList = new List<double>() {2.3, 2.4, 2.5}},
new KeyValues {Key = 34, DoubleList = new List<double>() {3.3, 3.4, 3.5}},
new KeyValues {Key = 35, DoubleList = new List<double>() {4.3, 4.4, 4.5}}
};
More information:
Dictionary<TKey,TValue> (official documentation here)
KeyValuePair VS DictionaryEntry (great post here)
I have two Dictionaries>
var test1 = await GetTest1();
var test2 = await GetTest2();
var groupedTest1 = test1.GroupBy(j => j.someField1)
.ToDictionary(k => k.Key, d => d.Select(s => s.someField2));
var groupedTest2 = test2.GroupBy(a => a.someField1)
.ToDictionary(k => k.Key, d => d.Select(s => s.someField2));
And I need two get the difference between them.
For example:
var result = groupedTest1.Except(groupedTest2);
If groupedTest1 contains something IEnumerable which contains in groupedTest2 I don't need to include this. Besides, I need to include check only for a similar key. My question is:
How can I do it?
groupedTest1 : { { Key: 1, IEnumerable: "test1, test2" },
{ Key: 2, IEnumerable: "test3, test4" } } groupedTest2 : {
{ Key: 2, IEnumerable: "test3, test4" } }
result should be Key: 1, IEnumerable: "test1, test2"
If I understand it you effectively have 2 actions; first, get all the keys that don't match and their entire entry should be in the result, and second for matching keys only get the values that don't match. I've put together a little console app to display this:
static void Main(string[] args)
{
//2 test dictionaries. Key 1 and it's values should be displayed and Key 3 with only "test5".
var test1 = new Dictionary<int, IEnumerable<string>>
{
{ 1, new List<string>{ "test1", "test2" } },
{ 2, new List<string>{ "test3", "test4" } },
{ 3, new List<string>{ "test3", "test4", "test5" } }
};
var test2 = new Dictionary<int, IEnumerable<string>>
{
{ 2, new List<string>{ "test3", "test4" } },
{ 3, new List<string>{ "test3", "test4" } }
};
//get non-matching keys first.
Console.WriteLine("Non-matching keys:");
var entriesWithNoMatch = test1.Where(x => !test2.ContainsKey(x.Key));
foreach (var entry in entriesWithNoMatch)
{
WriteResults(entry.Key, entry.Value.ToList());
}
//get matching keys and compare values
Console.WriteLine("Matching keys, non-matching values:");
var matchingKeys = test1.Keys.Intersect(test2.Keys);
foreach (var match in matchingKeys)
{
var result = new KeyValuePair<int, IEnumerable<string>>(match, test1[match].Except(test2[match]));
if (result.Value.Count() > 0)
{
WriteResults(match, result.Value.ToList());
}
}
Console.ReadLine();
}
//simple method to loop through results displaying key and results.
static void WriteResults(int key, IList<string> results)
{
var sb = new StringBuilder();
foreach (var r in results)
{
sb.Append(r + ",");
}
if (sb.Length > 1)
{
sb.Remove(sb.Length - 1, 1);
}
Console.WriteLine("Key: " + key.ToString() + ", results: " + sb.ToString());
sb.Clear();
}
Results:
For example:
var a = new { Key = new[] {1, 2} ... };
var b = new { Key = new[] {2, 3} ... };
var c = new { Key = new[] {3} ... };
I want a LINQ query that will get the result below, after new [] {a, b, c}.GroupByWithArray(x => x.Key)
Key 1: a
Key 2: a, b
Key 3: b, c
var a = new { Key = new[] { 1, 2 }};
var b = new { Key = new[] { 2, 3 }};
var c = new { Key = new[] { 3 }};
var result =
new[] { a, b, c }.SelectMany(item => item.Key.Select(key => (Item: item, Key: key)))
.GroupBy(pair => pair.Key)
.ToDictionary(group => group.Key,
group => group.Select(pair => pair.Item).ToArray());
var expected = new Dictionary<int, object[]>
{
{ 1, new[] { a }},
{ 2, new[] { a, b }},
{ 3, new[] { b, c }}
};
result.Should().BeEquivalentTo(expected); // Pass Ok
This seems the most logical to what you want:
var a = new { Key = new[] { 1, 2 } };
var b = new { Key = new[] { 2, 3 } };
var c = new { Key = new[] { 3 } };
var values = new[] { 'a', 'b', 'c' };
var result =
new[] { a, b, c }
.Zip(values, (keys, value) => new { keys, value })
.SelectMany(x => x.keys.Key, (x, key) => new { key, x.value })
.GroupBy(x => x.key, x => x.value);
var output =
String.Join(
Environment.NewLine,
result.Select(r => $"Key {r.Key}: {String.Join(", ", r)}"));
That gives:
Key 1: a
Key 2: a, b
Key 3: b, c
Now, I don't know what you meant by the ... in your question, but if I just assume you meant that the list can be longer, then that doesn't change anything.
string[] strLst = { "One", "Three" };
Dictionary<string, Customer> dicDocTypes = new Dictionary<string, Customer>();
dicDocTypes.Add("One", new Customer { Id = 1, Name = "Rabi" });
dicDocTypes.Add("Two", new Customer { Id = 2, Name = "Shuvankar" });
dicDocTypes.Add("Three", new Customer { Id = 3, Name = "Krishna" });
dicDocTypes.Add("Four", new Customer { Id = 4, Name = "Suresh" });
var rootNodes = from node in dicDocTypes
where node.Key.Contains(**strLst**)
select new KeyValuePair<string, Customer>(node.Key, node.Value);
Question: how to filter dictionary items if the keys matches with a string[]
Instead of trying to ask the key if it belongs to an array, you can ask the array if it contains the key:
var rootNodes = from node in dicDocTypes
where strLst.Contains(node.Key)
select new KeyValuePair<string, Customer>(node.Key, node.Value);
If you have a lot of items, it will be faster to use a HashSet instead of an array (O(1) lookups instead of O(n) lookups):
var strLst = new HashSet<string> { "One", "Three" };
Also, you can simplify the select to this:
var rootNodes = dicDocTypes.Where(n => strLst.Contains(n.Key));
Additionally, there's a cleaner way to initialize dictionaries:
var dicDocTypes = new Dictionary<string, Customer> {
{ "One", new Customer { Id = 1, Name = "Rabi" } },
{ "Two", new Customer { Id = 2, Name = "Shuvankar" } },
{ "Three", new Customer { Id = 3, Name = "Krishna" } },
{ "Four", new Customer { Id = 4, Name = "Suresh" } }
};
As Cameron has said, you're just asking the question the wrong way round. However, you can also make your code simpler - the "node" in a dictionary is already a KeyValuePair, so all you need is:
var rootNodes = dicDocTypes.Where(pair => strLst.Contains(pair.Key));
If you need a new dictionary at the end, you can use the ToDictionary method:
var rootNodes = dicDocTypes.Where(pair => strLst.Contains(pair.Key))
.ToDictionary(pair => pair.Key, pair => pair.Value);
If your list of strings to filter by becomes longer, you might want to consider making it a HashSet<string> instead of an array.
var rootNodes = dicDocTypes.Join(strList, kvp => kvp.Key, s => s, (kvp, str) => kvp);
I have 4 dictionaries of type
Dictionary<string,string>
dict1
k1 = v1
k2 = v2
k4 = v4
dict 2
k2 = vv2
k3 = v3
dict3
k2 = vvv2
dict 4
k4 = vvvv4
result dict of type Dictionary<string,List<string>>
k1 = v1,"","","" //combine 4 dict but put blank values in respective positions where values are not found
k2 = v2,vv2,vvv2,""
k3 = "",v3,"",""
k4 = v4,"","",vvvv4
Is this achievable? Any extension method??
I'm not convinced an extension method is the best design for this. Here is a standard method which will merge any number of dictionaries.
Dictionary<string,List<string>> Merge(params Dictionary<string,string>[] dicts)
{
var target = new Dictionary<string, List<string>>();
var allKeys = dicts.SelectMany(d => d.Keys).Distinct();
foreach(var key in allKeys)
{
foreach(var dict in dicts)
{
if(target.ContainsKey( key ) )
{
target[key].Add( dict.ContainsKey(key) ? dict[key] : "" );
}
else
{
target[key] = new[] {dict.ContainsKey(key) ? dict[key] : ""} .ToList();
}
}
}
return target;
}
This is much more flexible, and readable, than the other LINQ solutions posted here.
Here's a LINQPad test you can use to verify:
var d1 = new Dictionary<string,string> { {"k1","v1"}, {"k2","v2"}, {"k4","v4"} } ;
var d2 = new Dictionary<string,string> { {"k2","vv2"}, {"k3","v3"} } ;
var d3 = new Dictionary<string,string> { {"k2","vvv2"} } ;
var d4 = new Dictionary<string,string> { {"k4","vvvv4"} } ;
Merge(d1,d2,d3,d4).Dump();
Not inbuilt; but maybe something like:
var keys = dict1.Keys.Union(dict2.Keys).Union(dict3.Keys).Union(dict4.Keys);
var result = new Dictionary<string,List<string>>();
foreach(var key in keys) {
List<string> list = new List<string>();
list.Add(dict1.ContainsKey(key) ? dict1[key] : "");
list.Add(dict2.ContainsKey(key) ? dict2[key] : "");
list.Add(dict3.ContainsKey(key) ? dict3[key] : "");
list.Add(dict4.ContainsKey(key) ? dict4[key] : "");
result.Add(key, list);
}
Tested with LINQPad:
void Main()
{
var dict1 = new Dictionary<string, string>
{
{ "k1", "v1" },
{ "k2", "v2" },
{ "k4", "v4" },
};
var dict2 = new Dictionary<string, string>
{
{ "k2", "vv2" },
{ "k3", "v3" },
};
var dict3 = new Dictionary<string, string>
{
{ "k2", "vvv2" },
};
var dict4 = new Dictionary<string, string>
{
{ "k4", "vvvv4" },
};
var keys = dict1.Keys
.Union(dict2.Keys)
.Union(dict3.Keys)
.Union(dict4.Keys);
var table =
from key in keys
let v1 = dict1.ContainsKey(key) ? dict1[key] : ""
let v2 = dict2.ContainsKey(key) ? dict2[key] : ""
let v3 = dict3.ContainsKey(key) ? dict3[key] : ""
let v4 = dict4.ContainsKey(key) ? dict4[key] : ""
select new { key, values = new List<string> { v1, v2, v3, v4 } };
var result = table
.ToDictionary(r => r.key, r => r.values);
result.Dump();
}
You can combine the entire thing into just one Linq query:
var result =
(from key in dict1.Keys.Union(dict2.Keys).Union(dict3.Keys).Union(dict4.Keys)
let v1 = dict1.ContainsKey(key) ? dict1[key] : ""
let v2 = dict2.ContainsKey(key) ? dict2[key] : ""
let v3 = dict3.ContainsKey(key) ? dict3[key] : ""
let v4 = dict4.ContainsKey(key) ? dict4[key] : ""
select new { key, values = new List<string> { v1, v2, v3, v4 } })
.ToDictionary(r => r.key, r => r.values);