Как проверить, все ли элементы списка имеют одинаковое значение и вернуть его, или вернуть «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;
Keiths
источник
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;
}
hackerhasid
источник
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;
Iamond eezeƦ
источник
Во-первых, это может привести к образованию большого количества мусора. Также он менее ясен, чем другие ответы 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";   
    }
Прадип Кумар Дас
источник