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.
Related
I have a big list of objects and in this object there is a category ID something like:
var list = new List<Example>
{
new Example {CatId = 1, Value = new { }},
new Example {CatId = 1, Value = new { }},
new Example {CatId = 1, Value = new { }},
new Example {CatId = 2, Value = new { }},
new Example {CatId = 2, Value = new { }},
new Example {CatId = 3, Value = new { }}
// and so on
};
So I am looking for making this complicated list more organized like list of lists of unique elements
something like:
var result = new List<List<Example>>
{
new List<Example>
{
new Example {CatId = 1, Value = new { }},
new Example {CatId = 2, Value = new { }},
new Example {CatId = 3, Value = new { }}
},
new List<Example>
{
new Example {CatId = 1, Value = new { }},
new Example {CatId = 2, Value = new { }}
},
new List<Example>
{
new Example {CatId = 1, Value = new { }}
}
}
Problem is I do not what to use, group by will not fix my case, so how to do this in most efficient way.
So this is about partitioning, it's the sort of thing that is easy to do in a database query, but in c# you need to create some key with a partition number that you can then use to .GroupBy.
The partitioning itself is a grouping
var projected = list.GroupBy(x => x.CatId)
.SelectMany( g => g.Select( ( x, i ) => new { Item = x, rn = i + 1 } ) );
This gives you records that look like:
{"Item":{"CatId":1,"Value":{}},"rn":1}
{"Item":{"CatId":1,"Value":{}},"rn":2}
{"Item":{"CatId":1,"Value":{}},"rn":3}
{"Item":{"CatId":2,"Value":{}},"rn":1}
{"Item":{"CatId":2,"Value":{}},"rn":2}
{"Item":{"CatId":3,"Value":{}},"rn":1}
As you can see that rn ("row number") value can be used to group by:
var result = projected.GroupBy(x => x.rn, x => x.Item);
This gives us:
[{"CatId":1,"Value":{}},{"CatId":2,"Value":{}},{"CatId":3,"Value":{}}]
[{"CatId":1,"Value":{}},{"CatId":2,"Value":{}}]
[{"CatId":1,"Value":{}}]
So, all in 1 go:
var result = list.GroupBy(x => x.CatId)
.SelectMany( g => g.Select( ( x, i ) => new { Item = x, rn = i + 1 } ) )
.GroupBy(x => x.rn, x => x.Item);
Live example: https://dotnetfiddle.net/AlTfk8
var x = new int[] { 1, 2 };
var y = x switch {
{ 1, 2 } => "yea",
_ => "nay"
};
fails to compile.
How can I pattern-match arrays?
You have to expand the elements of the array yourself like so
var x = new int[] { 1, 2 };
var y = (x[0], x[1]) switch {
(1, 2) => "yea",
_ => "nay"
};
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:
I would like to create a
Dictionary<string, int[]> dict
out of two arrays:
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
the result:
["A"] = {1,5}
["B"] = {2}
["D"] = {2}
Is there a way i can do this with LINQ?
I have read about Zip but I don't think I can use since I need to add values to an existing key.value array.
Use .Zip to bind the two collections together and then GroupBy to group the keys.
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
var result = keys.Zip(values, (k, v) => new { k, v })
.GroupBy(item => item.k, selection => selection.v)
.ToDictionary(key => key.Key, value => value.ToArray());
Then to add these items into the dictionary that you already have:
I changed the int[] to List<int> so it is easier to handle Add/AddRange
Dictionary<string, List<int>> existingDictionary = new Dictionary<string, List<int>>();
foreach (var item in result)
{
if (existingDictionary.ContainsKey(item.Key))
existingDictionary[item.Key].AddRange(item.Value);
else
existingDictionary.Add(item.Key, item.Value.ToList());
}
Linq solution:
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
Dictionary<string, int[]> dict = keys
.Zip(values, (k, v) => new {
key = k,
value = v })
.GroupBy(pair => pair.key, pair => pair.value)
.ToDictionary(chunk => chunk.Key,
chunk => chunk.ToArray());
Test:
string report = String.Join(Environment.NewLine, dict
.Select(pair => $"{pair.Key} [{string.Join(", ", pair.Value)}]"));
Console.Write(report);
Outcome:
A [1, 5]
B [2]
D [2]
Try this :
string[] keys = { "A", "B", "A", "D" };
int[] values = { 1, 2, 5, 2 };
Dictionary<string, int[]> dict = keys.Select((x, i) => new { key = x, value = values[i] }).GroupBy(x => x.key, y => y.value).ToDictionary(x => x.Key, y => y.ToArray());
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);