How could I take 1 more item from Linq's TakeWhile?

后端 未结 5 2014
难免孤独
难免孤独 2021-01-01 16:45

(line of code of interest is the last one, the rest is just for a full representation)

Using the following code, I wanted to take VOTERS until I exceeded

相关标签:
5条回答
  • 2021-01-01 17:24

    Variation of Kobi's answer but demonstrates use of (value, index). index is useful in solving similar issues albeit not OP's.

    voters.TakeWhile((value, index) => (voicesSoFar += value.Voices) - value.Voices < voicesNeeded);
    
    0 讨论(0)
  • 2021-01-01 17:25

    Just write your own extension method:

    static class IEnumerableExtensions {
        public static IEnumerable<T> TakeUntil<T>(
            this IEnumerable<T> elements,
            Func<T, bool> predicate
        ) {
            return elements.Select((x, i) => new { Item = x, Index = i })
                           .TakeUntil((x, i) => predicate(x.Item))
                           .Select(x => x.Item);
        }
    
        public static IEnumerable<T> TakeUntil<T>(
            this IEnumerable<T> elements,
            Func<T, int, bool> predicate
        ) {
            int i = 0;
            foreach (T element in elements) {
                if (predicate(element, i)) {
                    yield return element;
                    yield break;
                }
                yield return element;
                i++;
            }
        }
    }
    

    Usage:

    var eligibleVoters = voters.TakeUntil(
                             p => (voicesSoFar += p.Voices) >= voicesNeeded
                         );
    
    foreach(var voter in eligibleVoters) {
        Console.WriteLine(voter.Name);
    }
    

    Output:

    Alice
    Bob
    Catherine
    
    0 讨论(0)
  • 2021-01-01 17:39

    In a situation where I wanted to execute a function until and including it hit an end condition I did:

    public static IEnumerable<T> TakeUntilIncluding<T>(this IEnumerable<T> list, Func<T, bool> predicate)
    {
        foreach(T el in list)
        {
            yield return el;
            if (predicate(el))
                yield break;
        }
    }
    

    Worked for me! I think this is an implementation-agnostic solution like Jason's, but simpler.

    0 讨论(0)
  • 2021-01-01 17:40

    You're looking for

    voters.TakeWhile(p => {
       bool exceeded = voicesSoFar > voicesNeeded ;
       voicesSoFar += p.Voices;
       return !exceeded;
    });
    

    If you insist on a one-liner, this will work by comparing the previous value:

    voters.TakeWhile(p => (voicesSoFar += p.Voices) - p.Voices < voicesNeeded);
    
    0 讨论(0)
  • 2021-01-01 17:45

    I was facing the same problem. I have used Union and Skip methods, so to take until it was

       IEnumerable<Something> newSomethings  = somethings.TakeWhile(s => s != stop).Union(new List<Something>(){stop});  
    

    and for skip until

       IEnumerable<Something> newSomethings  = somethings.SkipWhile(s => s != stop).Skip(1);  
    

    There is also Take method, which takes some int of first results.

    0 讨论(0)
提交回复
热议问题