Have a collection of objects. Schematically:
[
{ A = 1, B = 1 }
{ A = 1, B = 2 }
{ A = 2, B = 3 }
{ A = 2, B = 4 }
{ A = 1, B = 5 }
{ A = 3, B = 6 }
]
Need:
[
{ A = 1, Bs = [ 1, 2 ] }
{ A = 2, Bs = [ 3, 4 ] }
{ A = 1, Bs = [ 5 ] }
{ A = 3, Bs = [ 6 ] }
]
Is it possible to LINQ such?
Note: Ordering is important. So Bs = [5]
can't be merged with Bs = [1, 2]
Given these simplistic classes:
class C {
public int A;
public int B;
}
class R {
public int A;
public List<int> Bs = new List<int>();
}
You can do it like this:
var cs = new C[] {
new C() { A = 1, B = 1 },
new C() { A = 1, B = 2 },
new C() { A = 2, B = 3 },
new C() { A = 2, B = 4 },
new C() { A = 1, B = 5 },
new C() { A = 3, B = 6 }
};
var rs = cs.
OrderBy(o => o.B).
ThenBy(o => o.A).
Aggregate(new List<R>(), (l, o) => {
if (l.Count > 0 && l.Last().A == o.A) {
l.Last().Bs.Add(o.B);
}
else {
l.Add(new R { A = o.A, Bs = { o.B } });
}
return l;
});
Note: In the above I assume that the Bs and then the As have to be sorted. If that's not the case, it's a simple matter of removing the sorting instructions:
var rs = cs.
Aggregate(new List<R>(), (l, o) => {
if (l.Count > 0 && l.Last().A == o.A) {
l.Last().Bs.Add(o.B);
}
else {
l.Add(new R { A = o.A, Bs = { o.B } });
}
return l;
});
So basically you want to group together what has the same A
-value and is consecutive.
You need to tranform the list of objects to an anonymous type which contains the previous/next element. I've used two Selects
to make it more redable. Then you need to check if the two elements are consecutive(adjacent indices).
Now you have all you need to GroupBy
, the value and the bool
.
Your objects:
var list = new System.Collections.Generic.List<Foo>(){
new Foo(){ A = 1, B = 1 },
new Foo(){ A = 1, B = 2 },
new Foo(){ A = 2, B = 3 },
new Foo(){ A = 2, B = 4 },
new Foo(){ A = 1, B = 5 },
new Foo(){ A = 3, B = 6 }
};
The query:
var groups = list
.Select((f, i) => new
{
Obj = f,
Next = list.ElementAtOrDefault(i + 1),
Prev = list.ElementAtOrDefault(i - 1)
})
.Select(x => new
{
A = x.Obj.A,
x.Obj,
Consecutive = (x.Next != null && x.Next.A == x.Obj.A)
|| (x.Prev != null && x.Prev.A == x.Obj.A)
})
.GroupBy(x => new { x.Consecutive, x.A });
Output the result:
foreach (var abGroup in groups)
{
int aKey = abGroup.Key.A;
var bList = string.Join(",", abGroup.Select(x => x.Obj.B));
Console.WriteLine("A = {0}, Bs = [ {1} ] ", aKey, bList);
}
Here's the working demo: http://ideone.com/fXgQ3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With