Как спросить "Есть ли один элемент, удовлетворяющий условию" в LINQ?

Быстрый вопрос, какой предпочтительный способ запрограммировать вопрос: "Существует ли ровно один элемент в этой последовательности, который удовлетворяет условию Х?" используя Linq?

то есть.

// Pretend that the .OneAndOnlyOne() method exists
int[] sequence = new int[] { 1, 1, 2, 3, 5, 8 };
Assert.IsTrue(sequence.OneAndOnlyOne(x => x == 2);
Assert.IsFalse(sequence.OneAndOnlyOne(x => x == 1);

что-то вроде этого можно сделать с помощью:

sequence.SingleOrDefault(x => x == 2) != null;

но это немного неуклюже.

Я полагаю, что мог бы использовать собственный метод расширения, но это, по-видимому, обычная модель в моем коде, и я хочу убедиться, что есть хороший чистый способ сделать это. Есть ли способ использовать встроенные методы LINQ?

Ответ 1

Вы можете сделать:

bool onlyOne = source.Where(/*condition*/).Take(2).Count() == 1

Это не позволит счету перечислить большую последовательность без необходимости в случае нескольких совпадений.

Ответ 2

Самый простой способ - просто использовать Count. Единый не будет работать для вас, потому что он генерирует исключение, если есть не только один элемент.

Л. Бушкин предлагает (в комментариях) использовать SequenceEqual для сравнения последовательности с другой. Вы можете использовать это, пропустив первый элемент с помощью Skip (1) и сравнив полученную последовательность с пустой последовательностью, например, что вы можете получить из Empty

Ответ 3

Вы можете использовать этот метод расширения, который, я думаю, должен был быть включен в стандартные методы расширения для linq:

public static int CountUpTo<T>(this IEnumerable<T> sequence, int maxCount) {
    if (sequence == null) throw new ArgumentNullException("sequence");
    if (maxCount < 0) throw new ArgumentOutOfRangeException("maxCount");

    var count = 0;
    var enumerator = sequence.GetEnumerator();
    while (count < maxCount && enumerator.MoveNext())
        count += 1;
    return count;
}

Используется так:

return sequence.CountUpTo(2) == 1;