-1

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'.

  • 2
    There's probably a solution using the `TakeWhile` LINQ method, but (in my opinion) you'll be trading readability. If it were me, I'd be using a traditional `foreach` – Luke Aug 22 '22 at 17:12
  • Does this answer your question? [Removing strings from a list line by line](https://stackoverflow.com/questions/73374347/removing-strings-from-a-list-line-by-line) – gunr2171 Aug 22 '22 at 17:13
  • MoreLinq had a `Split` method that might be useful – juharr Aug 22 '22 at 17:14

2 Answers2

0

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();
Matt Bommicino
  • 309
  • 1
  • 5
  • Beautiful. Just what i needed. I was first looping to get the distance from the first '1' to the second '1', and then loop that list of distance to do a .GetRange(). This even works better and cleaner when i could use 'var' since my actual problem uses complex structs/anonymous type. List of int was just for simplicity. – user2759713 Aug 23 '22 at 19:20
0

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).

BurnsBA
  • 4,347
  • 27
  • 39