How to break a list based on an condition?
[1,2,3,4,5,6,1,2,3,4,5,6,5,6]
into a List<List<T>>
[1,2,3,4,5,6]
[1,2,3,4,5,6,5,6]
for every '1', select all the items after it until the next '1'.
CodePudding user response:
That's a fun question. I couldn't think of a way to do it without declaring one extra variable.
var ints = new [] {1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 5, 6};
int groupNumber = 0;
List<List<int>> groups = ints
.GroupBy(i => i == 1 ? groupNumber : groupNumber, i => i, (gn, enumerable) => enumerable.ToList())
.ToList();
CodePudding user response:
You can use Linq method Aggregate
.
var listy = new List<int>() { 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 5, 6 };
listy.Aggregate<int, List<List<int>>>(new List<List<int>>(), (existing, item) => {
if (item == 1 || !existing.Any())
{
existing.Add(new List<int>());
}
existing.Last().Add(item);
return existing;
})
Interactive console output:
List<List<int>>(2) { List<int>(6) { 1, 2, 3, 4, 5, 6 }, List<int>(8) { 1, 2, 3, 4, 5, 6, 5, 6 } }
Things to note:
The Aggregate method specifies a "seed" value, and this will be the return type List<List<int>>
. Because the type is different than the list type, the seed type must be explicitly listed. That means this pseudo-function signature is used:
Aggregate<int, TAccumulate>(TAccumulate, (x, y) => { return x; })
The seed is given as a new List<List<int>>
. The first iteration this collection will be empty, so a check for that condition is added (!existing.Any()
). This happens to require the same logic from the problem (if item == 1), so that condition is also added. Then it's just a matter of appending the current item to the current (i.e., last) list from the result (list of lists).