score:1

I'm really not sure this is much better than your original, but for the purpose of another solution the general process is

  1. Use Select to project a list working out a grouping
  2. Use GroupBy to group by the above
  3. Use Select again to project the grouped items to an array of Item
  4. Use ToList to project the result to a list

public static List<Item[]> GetContiguousSequences2(Item []items)
{
    var currIdx = 1;
    return items.Select( (item,index) =>  new {
            item = item,
            index = index == 0 || items[index-1].Stop == item.Start ? currIdx : ++currIdx
        })
        .GroupBy(x => x.index, x => x.item)
        .Select(x => x.ToArray())
        .ToList();      
}

Live example: https://dotnetfiddle.net/mBfHru


Another way is to do an aggregation using Aggregate. This means maintaining a final Result list and a Curr list where you can aggregate your sequences, adding them to the Result list as you find discontinuities. This method looks a little closer to your original

public static List<Item[]> GetContiguousSequences3(Item []items)
{
    var res = items.Aggregate(new {Result = new List<Item[]>(), Curr = new List<Item>()}, (agg, item) => {
            if(!agg.Curr.Any() || agg.Curr.Last().Stop == item.Start) {
                agg.Curr.Add(item);
            } else {
                agg.Result.Add(agg.Curr.ToArray());
                agg.Curr.Clear();   
                agg.Curr.Add(item);
            }
        return agg;
    });     
    res.Result.Add(res.Curr.ToArray()); // Remember to add the last group
    return res.Result;
}

Live example: https://dotnetfiddle.net/HL0VyJ

score:0

Your solution is okay. I don't think that LINQ adds any simplification or clarity in this situation. Here is a fast solution that I find intuitive:

static List<Item[]> GetContiguousSequences(Item[] items)
{
    var result = new List<Item[]>();
    int start = 0;
    while (start < items.Length) {
        int end = start + 1;
        while (end < items.Length && items[end].Start == items[end - 1].Stop) {
            end++;
        }

        int len = end - start;
        var a = new Item[len];
        Array.Copy(items, start, a, 0, len);
        result.Add(a);
        start = end;
    }
    return result;
}

score:1

You can implement ContiguousSplit as a corutine: let's loop over source and either add item into current range or return it and start a new one.

  private static IEnumerable<Item[]> ContiguousSplit(IEnumerable<Item> source) {
    List<Item> current = new List<Item>();

    foreach (var item in source) {
      if (current.Count > 0 && current[current.Count - 1].Stop != item.Start) {
        yield return current.ToArray();

        current.Clear();
      }

      current.Add(item);
    }  

    if (current.Count > 0)
      yield return current.ToArray();
  }

then if you want materialization

List<Item[]> GetContiguousSequences(Item []items) => ContiguousSplit(items).ToList();

Related Articles