Como verificar se todos os itens da lista têm o mesmo valor e retorná-lo ou retornar um "otherValue" se não tiverem?


121

Se todos os itens em uma lista tiverem o mesmo valor, preciso usar esse valor, caso contrário, preciso usar um "otherValue". Não consigo pensar em uma maneira simples e clara de fazer isso.

Consulte também Maneira clara de escrever loop que possui lógica especial para o primeiro item de uma coleção.


Em sua atenção bastante atrevida, eu iria com a resposta de Ani stackoverflow.com/questions/4390232/…
Binary Worrier

5
O que você deseja que aconteça se não houver um primeiro valor porque a lista está vazia? Nesse caso, é verdade que "todos os itens da lista têm o mesmo valor" - se você não acredita em mim, encontre um que não! Você não define o que fazer nessa situação. Isso deve gerar uma exceção, retornar o valor "outro" ou o quê?
Eric Lippert

@Eric, desculpe, quando a lista estiver vazia, ele deve retornar o valor "outro"
Ian Ringrose

Respostas:


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

Maneira mais limpa que consigo pensar. Você pode transformá-lo em uma linha alinhando val, mas First () seria avaliado n vezes, dobrando o tempo de execução.

Para incorporar o comportamento "conjunto vazio" especificado nos comentários, basta adicionar mais uma linha antes das duas acima:

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

1
+1, o uso .Anypermitiria que a enumeração fosse encerrada mais cedo nos casos em que houver valores diferentes?
Jeff Ogata

12
@adrift: Allterminará assim que atingir um elemento xda sequência para o qual x.Value != val. Da mesma forma, Any(x => x.Value != val)terminaria assim que atingir um elemento xda sequência para o qual x.Value != val. Ou seja, ambos Alle Anyexibem "curto-circuito" análogo &&e ||(que é efetivamente o que é Alle o que Anyé).
jason

@ Jason: exatamente. Tudo (condição) é efetivamente! Qualquer (! Condição) e a avaliação de qualquer uma delas terminará assim que a resposta for conhecida.
Keiths

4
Microoptimização:return yyy.Skip(1).All(x=>x.Value == val) ? val : otherValue;
Caltor 13/12/19

100

Bom teste rápido para todos iguais:

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

1
Isso não funcionará com nenhum Class, embora deva funcionar com estruturas. Ótimo para uma lista de primitivos, no entanto.
22713 Andrew Backer

2
+1 muito mais limpo que a solução IMO da KeithS. Você pode usar collection.Distinct().Count() <= 1 se desejar permitir coleções vazias.
3dGrabber

4
Tenha cuidado, .Distinct()nem sempre funciona como esperado - especialmente quando você trabalha com objetos, consulte esta pergunta. Nesse caso, você precisa implementar a interface IEquatable.
Matt

16
Mais limpo, sim, mas com menos desempenho no caso médio; É garantido que Distinct () percorre todos os elementos da coleção uma vez e, no pior caso de cada elemento ser diferente, Count () percorre a lista completa duas vezes. Distinct () também cria um HashSet para que seu comportamento possa ser linear e não NlogN ou pior, e isso aumentará o uso de memória. All () faz uma passagem completa no pior caso de todos os elementos serem iguais e não cria novas coleções.
precisa saber é o seguinte

1
@KeithS Como eu espero que você perceba até agora, Distinctnão percorrerá a coleção e Countfará uma travessia Distinctpelo iterador.
NetMage 9/12/19

22

Embora você certamente possa construir esse dispositivo a partir de operadores de sequência existentes, nesse caso, eu estaria inclinado a escrevê-lo como um operador de sequência personalizado. Algo como:

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

Isso é bastante claro, curto, abrange todos os casos e não cria desnecessariamente iterações extras da sequência.

Transformar isso em um método genérico que funciona IEnumerable<T>é deixado como um exercício. :-)


Digamos, por exemplo, que você tenha uma sequência de anuláveis ​​e o valor extraído também seja anulável. Nesse caso, a sequência pode estar vazia ou todos os itens na sequência podem ter um valor nulo no valor extraído. A coalescência, nesse caso, retornaria otherquando a nullresposta estivesse realmente correta (presumivelmente). Digamos que a função tenha sido T Unanimous<U, T>(this IEnumerable<U> sequence, T other)ou algo assim, isso complica um pouco.
Anthony Pegram

@ Anthony: De fato, existem muitas complicações aqui, mas elas são facilmente contornadas. Estou usando um int nulo como uma conveniência, para não precisar declarar um sinalizador "Eu já vi o primeiro item". Você pode facilmente declarar a bandeira. Também estou usando "int" em vez de T porque sei que você sempre pode comparar duas entradas para igualdade, o que não é o caso de dois Ts. Este é mais um esboço de uma solução do que uma solução genérica totalmente funcional.
Eric Lippert

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

Ou se você estiver preocupado em executar o First () para cada elemento (o que pode ser uma preocupação válida de desempenho):

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

@ KeithS - É por isso que adicionei a segunda parte da minha resposta. Em coleções pequenas, chamar First () é trivial. Em coleções grandes, isso pode começar a ser um problema.
Justin Niessner

1
"Em pequenas coleções, chamar First () é trivial." - Isso depende da fonte da coleção. Para uma lista ou matriz de objetos simples, você está absolutamente certo. Porém, alguns enumeráveis ​​não são conjuntos finitos de primitivas armazenadas em cache na memória. Uma coleção de delegados, ou um enumerador que produz através de um cálculo algorítmico de séries (por exemplo, Fibonacci), ficaria muito caro avaliar First () sempre.
Keiths

5
Ou pior, se a consulta for uma consulta ao banco de dados e a chamada "Primeiro" atingir o banco de dados novamente todas as vezes.
Eric Lippert

1
Fica pior quando você tem uma iteração única, como a leitura do arquivo ... Portanto, a resposta da Ani de outro segmento parece a melhor.
Alexei Levenkov

@ Eric - Vamos lá. Não há nada de errado com bater o banco de dados três vezes para cada elemento ... :-P
Justin Niessner

3

Pode ser tarde, mas uma extensão que funciona tanto para tipos de valor quanto de referência com base na resposta de Eric:

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

Uma alternativa ao uso do LINQ:

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

Eu descobri que o uso HashSet<T>é mais rápido para listas de até 6.000 números inteiros em comparação com:

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

Em primeiro lugar, isso pode criar muito lixo. Também é menos claro que o outro LINQ responde, mas mais lento que o método de extensão responde.
Ian Ringrose

Verdade. No entanto, não será muito lixo se estivermos falando sobre determinar se um pequeno conjunto de valores é o mesmo. Quando eu executei isso e uma instrução LINQ no LINQPad para um pequeno conjunto de valores, o HashSet foi mais rápido (cronometrado usando a classe Stopwatch).
Ɖiamond ǤeezeƦ

Se você executá-lo em uma versão compilada a partir da linha de comando, poderá obter resultados diferentes.
Ian Ringrose

Criei um aplicativo de console e descubra que HashSet<T>é inicialmente mais rápido do que usar as instruções LINQ na minha resposta. No entanto, se eu fizer isso em um loop, o LINQ será mais rápido.
Ɖiamond ǤeezeƦ

O grande problema dessa solução é que, se você estiver usando suas classes personalizadas, precisará implementar suas próprias GetHashCode(), o que é difícil de executar corretamente. Consulte: stackoverflow.com/a/371348/2607840 para obter mais detalhes.
Cameron #

0

Uma ligeira variação na abordagem simplificada acima.

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


3
Este é exatamente o contrário. Isso verificará se todos os elementos da lista são exclusivos.
Mario Galea

-1

Se uma matriz é do tipo multidimensional como abaixo, precisamos escrever abaixo linq para verificar os dados.

exemplo: aqui os elementos são 0 e eu estou verificando todos os valores são 0 ou não.
ip1 =
0 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";   
    }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.