¿Cómo comprobar si todos los elementos de la lista tienen el mismo valor y devolverlo, o devolver un "otherValue" si no es así?


122

Si todos los elementos de una lista tienen el mismo valor, entonces necesito usar ese valor; de lo contrario, necesito usar un "otherValue". No puedo pensar en una forma simple y clara de hacer esto.

Consulte también Forma ordenada de escribir un bucle que tiene una lógica especial para el primer elemento de una colección.


En su captador de atención bastante descarado, iría con la respuesta de Ani stackoverflow.com/questions/4390232/…
Binary Worrier

5
¿Qué quiere que suceda si no hay un primer valor porque la lista está vacía? En ese caso, es cierto que "todos los elementos de la lista tienen el mismo valor". Si no me cree, ¡búsqueme uno que no lo tenga! No define qué hacer en esta situación. ¿Debería lanzar una excepción, devolver el valor "otro" o qué?
Eric Lippert

@Eric, lo siento cuando la lista está vacía, debería devolver el valor "otro"
Ian Ringrose

Respuestas:


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

La forma más limpia que se me ocurre. Puede convertirlo en una línea introduciendo val, pero First () se evaluaría n veces, duplicando el tiempo de ejecución.

Para incorporar el comportamiento de "conjunto vacío" especificado en los comentarios, simplemente agregue una línea más antes de las dos anteriores:

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

1
+1, ¿el uso .Anypermitiría que la enumeración salga temprano en los casos en que hay valores diferentes?
Jeff Ogata

12
@adrift: Allterminará tan pronto como llegue a un elemento xde la secuencia para la cual x.Value != val. De manera similar, Any(x => x.Value != val)terminaría tan pronto como golpee un elemento xde la secuencia para la cual x.Value != val. Es decir, ambos Ally Anyexhiben "cortocircuito" análogo a &&y ||(que es efectivamente lo que Ally Anyson).
Jason

@Jason: exactamente. ¡Todo (condición) es efectivamente! Cualquier (! Condición), y la evaluación de cualquiera de ellos terminará tan pronto como se conozca la respuesta.
KeithS

4
Microoptimización:return yyy.Skip(1).All(x=>x.Value == val) ? val : otherValue;
Caltor

101

Buena prueba rápida para todos iguales:

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

1
Esto no funcionará con cualquiera Class, aunque debería funcionar con estructuras. Sin embargo, genial para una lista de primitivas.
Andrew Backer

2
+1 mucho más limpio que la solución de KeithS en mi opinión. Es posible que desee utilizar collection.Distinct().Count() <= 1 si desea permitir colecciones vacías.
3dGrabber

4
Tenga cuidado, .Distinct()no siempre funciona como se esperaba, especialmente cuando trabaja con objetos, consulte esta pregunta. En esos casos, debe implementar la interfaz IEquatable.
Matt

16
Más limpio, sí, pero menos eficaz en el caso medio; Se garantiza que Distinct () recorrerá cada elemento de la colección una vez, y en el peor de los casos en que cada elemento sea diferente, Count () recorrerá la lista completa dos veces. Distinct () también crea un HashSet para que su comportamiento pueda ser lineal y no NlogN o algo peor, y eso inflará el uso de la memoria. All () realiza una pasada completa en el peor de los casos en que todos los elementos sean iguales y no crea ninguna colección nueva.
KeithS

1
@KeithS Como espero que se dé cuenta a estas alturas, Distinctno atravesará la colección en absoluto y Counthará un recorrido a través Distinctdel iterador.
NetMage

22

Aunque ciertamente puede construir un dispositivo de este tipo a partir de operadores de secuencia existentes, en este caso me inclinaría a escribir este como un operador de secuencia 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;
}

Eso es bastante claro, breve, cubre todos los casos y no crea innecesariamente iteraciones adicionales de la secuencia.

Convertir esto en un método genérico que funcione IEnumerable<T>se deja como ejercicio. :-)


Digamos, por ejemplo, que tiene una secuencia de valores nulos y el valor extraído también es un valor anulable. En cuyo caso, la secuencia podría estar vacía o todos los elementos de la secuencia podrían tener un valor nulo en el valor extraído. La fusión, en este caso, devolvería el othercuando en nullrealidad era la respuesta (presumiblemente) correcta. Digamos que la función era T Unanimous<U, T>(this IEnumerable<U> sequence, T other)o alguna firma similar, eso lo complica un poco.
Anthony Pegram

@Anthony: De hecho, hay muchas complicaciones aquí, pero se solucionan con bastante facilidad. Estoy usando un int que acepta valores NULL como una conveniencia para no tener que declarar un indicador "Ya he visto el primer elemento". Fácilmente podría declarar la bandera. También estoy usando "int" en lugar de T porque sé que siempre puedes comparar dos ints para la igualdad, que no es el caso de dos Ts. Esto es más un boceto de una solución que una solución genérica completamente funcional.
Eric Lippert

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

O si le preocupa ejecutar First () para cada elemento (lo que podría ser un problema de rendimiento válido):

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

@KeithS - Por eso agregué la segunda parte de mi respuesta. En colecciones pequeñas, llamar a First () es trivial. En colecciones grandes, eso podría comenzar a ser un problema.
Justin Niessner

1
"En colecciones pequeñas, llamar a First () es trivial". - Eso depende de la fuente de la colección. Para una lista o matriz de objetos simples, tiene toda la razón. Sin embargo, algunos enumerables no son conjuntos finitos de primitivos almacenados en memoria caché. Una colección de delegados, o un enumerador que rinde a través de un cálculo algorítmico en serie (por ejemplo, Fibonacci), sería muy costoso evaluar First () cada vez.
KeithS

5
O peor aún, si la consulta es una consulta de base de datos y llamar a "Primero" llega a la base de datos nuevamente cada vez.
Eric Lippert

1
Se pone peor cuando tiene una iteración única, como leer desde un archivo ... Entonces, la respuesta de Ani de otro hilo se ve mejor.
Alexei Levenkov

@Eric - Vamos. No hay nada de malo en acceder a la base de datos tres veces para cada elemento ...
:-P

3

Esto puede llegar tarde, pero es una extensión que funciona tanto para los tipos de valor como de referencia según la respuesta 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

Una alternativa al uso de LINQ:

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

He descubierto que el uso HashSet<T>es más rápido para listas de hasta ~ 6000 enteros en comparación con:

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

En primer lugar, esto puede generar mucha basura. Además, es menos claro que las otras respuestas LINQ, pero más lento que las respuestas del método de extensión.
Ian Ringrose

Cierto. Sin embargo, no habrá mucha basura si hablamos de determinar si un pequeño conjunto de valores son todos iguales. Cuando ejecuté esto y una declaración LINQ en LINQPad para un pequeño conjunto de valores, HashSet fue más rápido (cronometrado usando la clase Stopwatch).
Ɖiamond ǤeezeƦ

Si lo ejecuta en una versión de versión desde la línea de comandos, puede obtener resultados diferentes.
Ian Ringrose

Creé una aplicación de consola y descubrí que HashSet<T>inicialmente es más rápido que usar las declaraciones LINQ en mi respuesta. Sin embargo, si hago esto en un bucle, LINQ es más rápido.
Ɖiamond ǤeezeƦ

El gran problema con esta solución es que si está utilizando sus clases personalizadas, debe implementar las suyas propias GetHashCode(), lo cual es difícil de hacer correctamente. Consulte: stackoverflow.com/a/371348/2607840 para obtener más detalles.
Cameron

0

Una ligera variación del enfoque simplificado anterior.

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


3
Esto es exactamente al revés. Esto comprobará que todos los elementos de la lista sean únicos.
Mario Galea

-1

Si una matriz es de tipo multidimensional como a continuación, tenemos que escribir a continuación linq para verificar los datos.

ejemplo: aquí los elementos son 0 y estoy comprobando que todos los valores son 0 o no.
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";   
    }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.