Как я мог взять еще 1 предмет из Linq TakeWhile?
(строка интересующего кода - последняя, остальное - только для полного представления)
Используя следующий код, я хотел взять 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 ));
Ответы
Ответ 1
Вы ищете
voters.TakeWhile(p => {
bool exceeded = voicesSoFar > voicesNeeded ;
voicesSoFar += p.Voices;
return !exceeded;
});
Если вы настаиваете на использовании одного лайнера, это будет работать, сравнивая предыдущее значение:
voters.TakeWhile(p => (voicesSoFar += p.Voices) - p.Voices < voicesNeeded);
Ответ 2
В ситуации, когда я хотел выполнить функцию до тех пор, пока она не попала в конечное условие:
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's, но более простое.
Ответ 3
Просто напишите свой собственный метод расширения:
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
Ответ 4
Изменение ответа Коби, но демонстрирует использование (value, index)
. index
полезен при решении подобных вопросов, хотя и не в OP.
voters.TakeWhile((value, index) => (voicesSoFar += value.Voices) - value.Voices < voicesNeeded);
Ответ 5
У меня была та же проблема.
Я использовал методы "Союз" и "Пропустить", поэтому до тех пор, пока не будет
IEnumerable<Something> newSomethings = somethings.TakeWhile(s => s != stop).Union(new List<Something>(){stop});
и пропустить до
IEnumerable<Something> newSomethings = somethings.SkipWhile(s => s != stop).Skip(1);
Существует также метод Take, который принимает некоторые int первых результатов.