Section / Partition / Section IEnumerable <T> in IEnumerable <IEnumerable <T>> based on function using LINQ?

I would like to split a sequence in C # into a sequence of sequences using LINQ. I did some research, and the next SO article I found is slightly related to this .

However, this question asks only the question of how to split the original sequence based on a constant value. I would like to break my sequence based on the operation.

In particular, I have a list of objects that contain a decimal property.

public class ExampleClass
{
    public decimal TheValue { get; set; }
}

Say I have a sequence ExampleClass, and the corresponding sequence of values TheValueis:

{0,1,2,3,1,1,4,6,7,0,1,0,2,3,5,7,6,5,4,3,2,1}

IEnumerable<IEnumerable<ExampleClass>> TheValue, :

{{0,1,2,3}, {1,1,4,6,7}, {0,1}, {0,2,3,5,7}, {6,5,4,3,2,1}}

, . , ?

, , LINQ .

+5
3

, , ...

public static IEnumerable<IEnumerable<TElement>>
    PartitionMontonically<TElement, TKey>
    (this IEnumerable<TElement> source,
     Func<TElement, TKey> selector)
{
    // TODO: Argument validation and custom comparisons
    Comparer<TKey> keyComparer = Comparer<TKey>.Default;

    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
        {
            yield break;
        }
        TKey currentKey = selector(iterator.Current);
        List<TElement> currentList = new List<TElement> { iterator.Current };
        int sign = 0;
        while (iterator.MoveNext())
        {
            TElement element = iterator.Current;
            TKey key = selector(element);
            int nextSign = Math.Sign(keyComparer.Compare(currentKey, key));

            // Haven't decided a direction yet
            if (sign == 0)
            {
                sign = nextSign;
                currentList.Add(element);
            }
            // Same direction or no change
            else if (sign == nextSign || nextSign == 0)
            {
                currentList.Add(element);
            }
            else // Change in direction: yield current list and start a new one
            {
                yield return currentList;
                currentList = new List<TElement> { element };
                sign = 0;
            }
            currentKey = key;
        }
        yield return currentList;
    }
}

, , ...

+6

linq .net .

public static IEnumerable<IEnumerable<T>> Monotonic<T>(this IEnumerable<T> enumerable)
{
  var comparator = Comparer<T>.Default;
  int i = 0;
  T last = default(T);
  return enumerable.GroupBy((value) => { i = comparator.Compare(value, last) > 0 ? i : i+1; last = value; return i; }).Select((group) => group.Select((_) => _));
}

IEnumerable . , Select - , .

+1

LINQ, . :

  • xs: .
  • func: , "" :
    • a bool, , "" ;
    • , func.
  • initialState: , func .

(, yield return, -, ):

public static IEnumerable<IEnumerable<T>> Split<T, TState>(
                  this IEnumerable<T> xs,
                  Func<T, TState, Tuple<bool, TState>> func, 
                  TState initialState)
{
    using (var splitter = new Splitter<T, TState>(xs, func, initialState))
    {
        while (splitter.HasNext)
        {
            yield return splitter.GetNext();
        }
    }
}
internal sealed class Splitter<T, TState> : IDisposable
{
    public Splitter(IEnumerable<T> xs, 
                    Func<T, TState, Tuple<bool, TState>> func, 
                    TState initialState)
    {
        this.xs = xs.GetEnumerator();
        this.func = func;
        this.state = initialState;
        this.hasNext = this.xs.MoveNext();
    }

    private readonly IEnumerator<T> xs;
    private readonly Func<T, TState, Tuple<bool, TState>> func;
    private bool hasNext;
    private TState state;

    public bool HasNext { get { return hasNext; } }

    public IEnumerable<T> GetNext()
    {
        while (hasNext)
        {
            Tuple<bool, TState> decision = func(xs.Current, state);
            state = decision.Item2;
            if (decision.Item1) yield break;
            yield return xs.Current;
            hasNext = xs.MoveNext();
        }
    }

    public void Dispose() { xs.Dispose(); }
}

Note. Here are some constructive solutions that went into the method Split:

  • He must make only one run over the sequence.
  • The condition is made explicit so that side effects can be excluded from func.
+1
source

All Articles