Search code examples
c#linqsequencemorelinq

LINQ start and stop segments in list of List<T> elements


Given a list of objects of one class (List<Type1>)

item1.isbool = true, beat = 1
item2.isbool = true, beat = 1.333
item3.isbool = true, beat = 1.666
item4.isbool = false, beat = 2
item5.isbool = false, beat = 3
item6.isbool = false, beat = 4
item7.isbool = false, beat = 5
item8.isbool = true, beat = 5.333
item9.isbool = true, beat = 5.666

I would like to get the starting and stopping values in the tuplet group (of variable sizes) to add to a list of a second class (List<Type2>.Add())

item1.tuplet = start
item3.tuplet = stop
item8.tuplet = start
item9.tuplet = stop

The order of the elements do matter. MoreLinq as an option, but not certain of the type of operation I need to perform to get those values. It could be either a Partition or some sort of GroupBy, but that doesn't seem to work.

My end results I need at the end are going to be

List2.Add(new Object() {Tuplet = TupletType.Start});
List2.Add(new Object() {Tuplet = TupletType.None});
List2.Add(new Object() {Tuplet = TupletType.Stop});
List2.Add(new Object() {Tuplet = TupletType.None});
List2.Add(new Object() {Tuplet = TupletType.None});
List2.Add(new Object() {Tuplet = TupletType.None});
List2.Add(new Object() {Tuplet = TupletType.None});
List2.Add(new Object() {Tuplet = TupletType.Start});
List2.Add(new Object() {Tuplet = TupletType.Stop});

Solution

  • You can use an extension method to do the grouping, then you can use:

    List<Type1> runs = ...
    
    List<Type2> grouped = new List<Type2>();
    grouped.AddRange(runs.GroupByRuns());
    

    Here is a possible solution for the extension method:

    public static class GroupType
    {
        public static IEnumerable<Type2> GroupByRuns(this IEnumerable<Type1> sequence)
        {
            if (sequence.Count() == 0)
                yield break;
    
            List<bool> next_items = sequence.Select(s => s.isbool).ToList();
            next_items.Add(false);
    
            bool previous_item = false;
            int idx = 1;
            foreach (var item in sequence)
            {
                if (item.isbool)
                {
                    if (previous_item == false)
                    {
                        yield return new Type2 { Tuple = Type2.TupletType.Start };
                    }
                    else if (next_items.ElementAt(idx) == true)
                    {
                        yield return new Type2 { Tuple = Type2.TupletType.None };
                    }
                    else
                    {
                        yield return new Type2 { Tuple = Type2.TupletType.Stop };
                    }
                }
                else
                {
                    yield return new Type2 { Tuple = Type2.TupletType.None };
                }
                previous_item = item.isbool;
                idx++;
            }
        }
    }