我如何从Linq的TakeWhile中再拿一件物品?

时间:2010-02-11 04:42:54

标签: c# .net linq

(感兴趣的代码行是最后一行,其余代码仅用于完整表示)

使用以下代码,我希望选择VOTERS,直到我超过所需的最大票数,但它在达到最大投票数之前就停止了,所以我的选民投票人数比我少1通缉。

在LINQ中是否有一个干净的方式我可以在它达到最大票数之前获得投票?我知道我可以添加一个选民或者在循环中执行此操作但我很好奇是否有一个很好的方法可以用LINQ来做这个。

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 ));

5 个答案:

答案 0 :(得分:23)

在我想要执行一个函数直到并包含它达到最终条件的情况下我做了:

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;
    }
}

为我工作!我认为这是一个与Jason相似的实现无关的解决方案,但更简单。

答案 1 :(得分:17)

您正在寻找

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

如果你坚持使用单行,这将通过比较之前的值来实现:

voters.TakeWhile(p => (voicesSoFar += p.Voices) - p.Voices < voicesNeeded);

答案 2 :(得分:7)

只需编写自己的扩展方法:

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++;
        }
    }
}

用法:

var eligibleVoters = voters.TakeUntil(
                         p => (voicesSoFar += p.Voices) >= voicesNeeded
                     );

foreach(var voter in eligibleVoters) {
    Console.WriteLine(voter.Name);
}

输出:

Alice
Bob
Catherine

答案 3 :(得分:0)

Kobi答案的变化,但演示了(value, index)的使用。 index在解决类似问题时很有用,尽管不是OP。

voters.TakeWhile((value, index) => (voicesSoFar += value.Voices) - value.Voices < voicesNeeded);

答案 4 :(得分:0)

我遇到了同样的问题。 我使用了Union和Skip方法,所以直到它

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

并跳过

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

还有Take方法,它取了一些初步结果。