Как проверить, все ли элементы списка имеют одинаковое значение и вернуть его, или вернуть «otherValue», если нет?


122

Если все элементы в списке имеют одно и то же значение, то мне нужно использовать это значение, в противном случае мне нужно использовать «otherValue». Я не могу придумать простой и понятный способ сделать это.

См. Также Аккуратный способ написания цикла со специальной логикой для первого элемента в коллекции.


Что касается вашего довольно дерзкого любителя внимания, я бы пошел с ответом Ани stackoverflow.com/questions/4390232/…
— Binary Worrier

5
Что делать, если первого значения нет, потому что список пуст? В этом случае верно, что «все элементы в списке имеют одинаковое значение» - если вы мне не верите, найдите мне тот, у которого нет! Вы не определяете, что делать в этой ситуации. Должно ли это вызвать исключение, вернуть «другое» значение или что?
— Эрик Липперт

@Eric, извините, когда список пуст, он должен вернуть "другое" значение
— Ян Рингроуз

Ответы:


153
var val = yyy.First().Value;
return yyy.All(x=>x.Value == val) ? val : otherValue; 

Самый чистый способ, который я могу придумать. Вы можете сделать его однострочным, вставив val, но First () будет оцениваться n раз, что удвоит время выполнения.

Чтобы включить поведение «пустой набор», указанное в комментариях, вы просто добавляете еще одну строку перед двумя указанными выше:

if(yyy == null || !yyy.Any()) return otherValue;

1
+1, позволит ли использование .Anyперечисления преждевременно завершить работу в случаях, когда есть разные значения?
— Джефф Огата

12
@adrift: All завершится, как только попадет в элемент xпоследовательности, для которой x.Value != val. Точно так же Any(x => x.Value != val)завершится, как только он попадет в элемент xпоследовательности, для которой x.Value != val. То есть оба Allи Anyдемонстрируют «короткое замыкание», аналогичное &&и ||(которое фактически является тем, что Allи Anyесть).
— Джейсон

@ Джейсон: точно. Все (условие) эффективно! Любое (! Условие), и оценка любого из них будет прекращена, как только станет известен ответ.
— KeithS

4
Микрооптимизация:return yyy.Skip(1).All(x=>x.Value == val) ? val : otherValue;
— Caltor

101

Хороший быстрый тест для всех равных:

collection.Distinct().Count() == 1

1
Это не будет работать ни с одним Class, хотя должно работать со структурами. Однако отлично подходит для списка примитивов.
— Эндрю Бэкер

2
+1 намного чище, чем решение KeithS IMO. Возможно, вы захотите использовать, collection.Distinct().Count() <= 1 если хотите разрешить пустые коллекции.
— 3dGrabber

4
Будьте осторожны, .Distinct()не всегда работает должным образом - особенно когда вы работаете с объектами, см. Этот вопрос. В таких случаях вам необходимо реализовать интерфейс IEquatable.
— Мэтт

16
Чище, да, но в среднем менее производительно; Distinct () гарантированно проходит через каждый отдельный элемент в коллекции один раз, а в худшем случае, когда каждый элемент отличается, Count () дважды пройдет полный список. Distinct () также создает HashSet, поэтому его поведение может быть линейным, а не NlogN или хуже, и это приведет к увеличению использования памяти. All () выполняет один полный проход в худшем случае, когда все элементы равны, и не создает никаких новых коллекций.
— KeithS

1
@KeithS Как я надеюсь, вы уже понимаете, Distinctне Countбудет вообще обходить коллекцию, а будет делать один обход через Distinctитератор.
— NetMage

22

Хотя вы, конечно, можете построить такое устройство из существующих операторов последовательности, в этом случае я был бы склонен написать это как пользовательский оператор последовательности. Что-то вроде:

// Returns "other" if the list is empty.
// Returns "other" if the list is non-empty and there are two different elements.
// Returns the element of the list if it is non-empty and all elements are the same.
public static int Unanimous(this IEnumerable<int> sequence, int other)
{
    int? first = null;
    foreach(var item in sequence)
    {
        if (first == null)
            first = item;
        else if (first.Value != item)
            return other;
    }
    return first ?? other;
}

Это довольно ясно, кратко, охватывает все случаи и не создает лишних итераций последовательности.

Превращение этого в общий метод, который работает, IEnumerable<T>остается в качестве упражнения. :-)


Скажем, например, у вас есть последовательность значений, допускающих значение NULL, и извлеченное значение также является допускающим значение NULL. В этом случае последовательность может быть пустой или каждый элемент в последовательности может иметь нулевое значение в извлеченном значении. Объединение в этом случае вернет, otherкогда nullдействительно был (предположительно) правильный ответ. Скажем, функция была T Unanimous<U, T>(this IEnumerable<U> sequence, T other)или какая-то такая подпись, это немного усложняет.
— Энтони Пеграм

@Anthony: Действительно, здесь много сложностей, но их довольно легко обойти. Я использую int, допускающий значение NULL, для удобства, так что мне не нужно объявлять флаг «Я уже видел первый элемент». Вы легко можете просто объявить флаг. Также я использую int вместо T, потому что я знаю, что вы всегда можете сравнить два int на равенство, что не относится к двум Ts. Это скорее набросок решения, чем полностью функциональное универсальное решение.
— Эрик Липперт

13
return collection.All(i => i == collection.First())) 
    ? collection.First() : otherValue;.

Или, если вы беспокоитесь о выполнении First () для каждого элемента (что может быть допустимой проблемой производительности):

var first = collection.First();
return collection.All(i => i == first) ? first : otherValue;

@KeithS - Вот почему я добавил вторую часть своего ответа. В небольших коллекциях вызов First () тривиален. В больших коллекциях это может стать проблемой.
— Джастин Нисснер

1
«В небольших коллекциях вызов First () тривиален». - Это зависит от источника коллекции. Что касается списка или массива простых объектов, вы абсолютно правы. Но некоторые перечисления не являются конечными наборами примитивов в кэшированной памяти. Набор делегатов или перечислитель, который вычисляется посредством алгоритмического вычисления ряда (например, Фибоначчи), будет стоить очень дорого для вычисления First () каждый раз.
— KeithS

5
Или, что еще хуже, если запрос является запросом к базе данных и при вызове «Первый» каждый раз база данных попадает в базу заново.
— Эрик Липперт

1
Становится хуже, когда у вас есть одноразовая итерация, такая как чтение из файла ... Так что ответ Ани из другого потока выглядит лучше всего.
— Алексей Левенков

@Eric - Давай. Нет ничего плохого в том, чтобы трижды попасть в базу данных для каждого элемента ... :-P
— Джастин Нисснер

3

Это может быть поздно, но расширение, которое работает как для значений, так и для ссылочных типов, на основе ответа Эрика:

public static partial class Extensions
{
    public static Nullable<T> Unanimous<T>(this IEnumerable<Nullable<T>> sequence, Nullable<T> other, IEqualityComparer comparer = null)  where T : struct, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (Nullable<T>)first ?? other;
    }

    public static T Unanimous<T>(this IEnumerable<T> sequence, T other, IEqualityComparer comparer = null)  where T : class, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (T)first ?? other;
    }
}

1
public int GetResult(List<int> list){
int first = list.First();
return list.All(x => x == first) ? first : SOME_OTHER_VALUE;
}

1

Альтернатива использованию LINQ:

var set = new HashSet<int>(values);
return (1 == set.Count) ? values.First() : otherValue;

Я обнаружил, что использование HashSet<T>списков до ~ 6000 целых чисел выполняется быстрее, чем:

var value1 = items.First();
return values.All(v => v == value1) ? value1: otherValue;

Во-первых, это может привести к образованию большого количества мусора. Также он менее ясен, чем другие ответы LINQ, но медленнее, чем отвечает метод расширения.
— Ян Рингроуз

Правда. Однако это не будет большим мусором, если мы говорим об определении того, является ли небольшой набор значений одинаковым. Когда я запустил это и оператор LINQ в LINQPad для небольшого набора значений, HashSet был быстрее (рассчитан с использованием класса Stopwatch).
— Ɖiamond ǤeezeƦ

Если вы запустите его в сборке релиза из командной строки, вы можете получить другие результаты.
— Ян Рингроуз

Создал консольное приложение и обнаружил, что HashSet<T>оно изначально быстрее, чем использование операторов LINQ в моем ответе. Однако если я сделаю это в цикле, LINQ будет быстрее.
— Ɖiamond ǤeezeƦ

Большая проблема с этим решением является то , что если вы используете свои собственные классы, вы должны реализовать свои собственные GetHashCode(), что трудно сделать правильно Смотрите: stackoverflow.com/a/371348/2607840 для более подробной информации.
— Кэмерон

0

Небольшая вариация вышеупомянутого упрощенного подхода.

var result = yyy.Distinct().Count() == yyy.Count();


3
Это как раз наоборот. Это проверит, что каждый элемент в списке уникален.
— Марио Галеа

-1

Если массив имеет многомерный тип, как показано ниже, мы должны написать linq для проверки данных.

Пример: здесь элементы равны 0, и я проверяю, что все значения равны 0 или нет.
ip1 =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0

    var value=ip1[0][0];  //got the first index value
    var equalValue = ip1.Any(x=>x.Any(xy=>xy.Equals()));  //check with all elements value 
    if(equalValue)//returns true or false  
    {  
    return "Same Numbers";  
    }else{  
    return "Different Numbers";   
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.