Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

.net

linq

(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 the maximum votes needed, but it stops right before reaching that maximum number of votes, so my voters pool has 1 fewer voter than I wanted.

Is there a clean way in LINQ where I could have made it take votes UNTIL it reached the maximum numbers of votes? I know I could add one more voter or do this in a loop but I am curious if there was a good way to do this with LINQ instead.

var voters = new List<Person>
                             {
                                 new Person("Alice", Vote.Yes ),
                                 new Person("Bob", Vote.Yes),
                                 new Person("Catherine", Vote.No),
                                 new Person("Denzel", Vote.Yes),
                                 new Person("Einrich", Vote.Abstain),
                                 new Person("Frederica", Vote.Abstain),
                                 new Person("Goeffried", Vote.Abstain),
                             };
            voters.Single(c => c.Name == "Alice").Voices = 100;
            voters.Single(c => c.Name == "Bob").Voices = 150;
            voters.Single(c => c.Name == "Catherine").Voices = 99;
            voters.Single(c => c.Name == "Denzel").Voices = 24;
            voters.Single(c => c.Name == "Einrich").Voices = 52;
            voters.Single(c => c.Name == "Frederica").Voices = 39;
            voters.Single(c => c.Name == "Goeffried").Voices = 99;

// this takes voters until we are BEFORE reaching X voices...
int voicesSoFar = 0;
int voicesNeeded = 300;
var eligibleVoters = voters.TakeWhile((p => (voicesSoFar += p.Voices) < voicesNeeded ));
like image 441
PRINCESS FLUFF Avatar asked Feb 11 '10 04:02

PRINCESS FLUFF


People also ask

How do you use TakeWhile?

TakeWhile method:The TakeWhile (Func...) will return elements starting from the beginning of an IEnumerable collection until it satisfies the condition specified. If the condition is false then it will return the collection immediately. Example I: int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };

What is the function of the TakeWhile method?

The TakeWhile<TSource>(IEnumerable<TSource>, Func<TSource,Boolean>) method tests each element of source by using predicate and yields the element if the result is true . Enumeration stops when the predicate function returns false for an element or when source contains no more elements.

What is Take () in C#?

C# Queryable Take() MethodGet specified number of elements from the beginning using the Take() method. The following is our array. int[] marks = { 35, 72, 50, 90, 95, 85, 52, 67 }; Now, use OrderByDescending to order the elements in Descending order. Then use the Take() method to get the elements.


2 Answers

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.

like image 124
Gert Arnold Avatar answered Oct 14 '22 15:10

Gert Arnold


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);
like image 33
Kobi Avatar answered Oct 14 '22 13:10

Kobi