Prueba si todos los valores de una lista son únicos

90

Tengo una pequeña lista de bytes y quiero probar que todos son valores diferentes. Por ejemplo, tengo esto:

List<byte> theList = new List<byte> { 1,4,3,6,1 };

¿Cuál es la mejor forma de comprobar si todos los valores son distintos o no?

Frenchie
fuente
2
Como esta es una pregunta típica de la sala de clase, responderé con una pregunta. ¿Cómo lo haría si estuviera ordenado?
ctrl-alt-delor

Respuestas:

168
bool isUnique = theList.Distinct().Count() == theList.Count();
juergen d
fuente
Solo curiosidad: ¿cuáles son los requisitos de espacio y tiempo de esto?
dtb
10
@dtb debería ser aproximadamente O (N) . Por supuesto, considerando que esta es una "lista pequeña", será increíblemente rápida con casi cualquier algoritmo. En mi opinión, esto gana en legibilidad y concisión, y dado que la velocidad no es un problema, eso lo hace perfecto.
Tim S.
2
Esto es menos eficiente de lo que podría ser
Jodrell
74

Aquí hay otro enfoque que es más eficiente que Enumerable.Distinct+ Enumerable.Count(más si la secuencia no es un tipo de colección). Utiliza un HashSet<T>que elimina los duplicados, es muy eficiente en las búsquedas y tiene una propiedad de recuento:

var distinctBytes = new HashSet<byte>(theList);
bool allDifferent = distinctBytes.Count == theList.Count;

u otro enfoque, más sutil y eficiente:

var diffChecker = new HashSet<byte>();
bool allDifferent = theList.All(diffChecker.Add);

HashSet<T>.Adddevuelve falsesi el elemento no se pudo agregar porque ya estaba en el HashSet. Enumerable.Allse detiene en el primer "falso".

Tim Schmelter
fuente
1
tan simple y obvio, ¿por qué no lo pensé primero :) Usé esta línea en una prueba unitaria para confirmar que 10 millones de elementos generados por mi increíble código son realmente únicos Assert.IsTrue(samples.Add(AwesomeClass.GetUnique()));. Fueron y son :) +1 para ti Tim :)
grapkulec
1
He intentado su respuesta a esta pregunta pero no funciona señor: stackoverflow.com/questions/34941162/…
Learning-Overthinker-Confused
Debería ser esto:bool allDifferent = theList.All(s => diffChecker.Add(s))
mike nelson
2
No, no es necesario. En este caso, puede pasar al delegado directamente
Tim Schmelter
1
@ AndréReichelt - Acabo de abrir su código y el tercer escenario ( List.All(HashSet.Add)) parece ser mucho más rápido que los otros dos en casi todos los casos
Kyle Delaney
6

Bien, aquí está el método más eficiente que se me ocurre usando .Net estándar

using System;
using System.Collections.Generic;

public static class Extension
{
    public static bool HasDuplicate<T>(
        this IEnumerable<T> source,
        out T firstDuplicate)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        var checkBuffer = new HashSet<T>();
        foreach (var t in source)
        {
            if (checkBuffer.Add(t))
            {
                continue;
            }

            firstDuplicate = t;
            return true;
        }

        firstDuplicate = default(T);
        return false;
    }
}

Básicamente, ¿cuál es el punto de enumerar la secuencia completa dos veces si todo lo que quieres hacer es encontrar el primer duplicado?

Podría optimizar esto más colocando una carcasa especial en secuencias vacías y de un solo elemento, pero eso se depreciaría de la legibilidad / mantenibilidad con una ganancia mínima.

Jodrell
fuente
Agradable agregar un valor duplicado a la devolución, bastante útil para la validación
Pac0
He probado 3 soluciones aquí y esta es de hecho la más eficiente en esta página. Sin embargo, hay algunos errores tipográficos (por ejemplo, sequencedebería ser source). Pero funciona muy bien una vez que se arreglan
mike nelson
@mikenelson, eso debería ser mejor
Jodrell
2
Para mayor legibilidad, creo que debería estar if (!checkBuffer.Add(t)) { firstDuplicate = t; return true }al día.
tia
2

La lógica similar a Distinctusar GroupBy:

var isUnique = theList.GroupBy(i => i).Count() == theList.Count;
Vitali Kuzniatsou
fuente
Esto es útil si desea verificar la unicidad con respecto a una propiedad theList.GroupBy(o => o.SomeProperty).Count() == theList.Count;mientras Distinct () no lo permite.
Rev1.0
1

También se puede hacer: Usar Hashset

var uniqueIds = new HashSet<long>(originalList.Select(item => item.Id));

            if (uniqueIds.Count != originalList.Count)
            {
            }
Gauravsa
fuente
0

Hay muchas soluciones.

Y sin duda más bellas con el uso de LINQ como "juergen d" y "Tim Schmelter" mencionado.

Pero, si descubres "Complejidad" y velocidad, la mejor solución será implementarla tú mismo. Una de las soluciones será crear una matriz de tamaño N (para el byte es 256). Y repita la matriz, y en cada iteración probará el índice del número coincidente si el valor es 1 si lo hace, eso significa que ya incrementé el índice de la matriz y, por lo tanto, la matriz no es distinta, de lo contrario, incrementaré la celda de la matriz y continuaré verificando .

Orel Eraki
fuente
2
puede utilizar un vector de bits con 256 bits = 32 bytes = 8 enteros. Pero su Big O = O (n) seguirá siendo el mismo que usar un Hashet propuesto en la otra respuesta.
BrokenGlass
Esto es O (n) así que quizás el más rápido, (pruébelo). ¿Verificar cuenta a medida que avanza o al final sería lo más rápido? Sospecho que al final mejorará en el peor de los casos, pero a medida que avanza puede mejorar la media y el mejor de los casos). Si no hay duplicados, este será el peor de los casos. Además, para tipos de datos más grandes, esto no funcionará bien, para un tipo de 16 bits tendría que usar 64k de recuento, bueno 64k bits (8k bytes), pero para algo más grande el uso de memoria comenzará a volverse tonto. Sin embargo, me gusta esta respuesta para valores de 8 bits.
ctrl-alt-delor
1
@TamusJRoyce si desea almacenar 4294967296 posibilidades, necesita 4 GB, no 42 MB (o 512 MB de uso de enmascaramiento de bits)
tigrou
No estoy seguro de lo que estaba pensando. "Asigne 42 MB + de memoria para almacenar todas las posibilidades 4294967296. Y use contadores de cubos simples. O incluso use el enmascaramiento de bits xor y verifique si algún bit ha cambiado de verdadero a falso. 42 MB + / 8 = 5 MB + El gasto parece demasiado grande con el hardware actual. Pero algún día esto puede tener mérito ". no es realmente un comentario relevante. Hashset sería lo mejor. Si está tratando con matrices extremadamente grandes, espera una cantidad de memoria extremadamente grande. Pero en un caso tan extraño, sería mejor un método heredado con un algoritmo CRC. Asignelo a un polinomio. Si está cerca, evalúe. ¡Gracias @tigrou!
TamusJRoyce
0

Y otra solución, si desea encontrar valores duplicados.

var values = new [] { 9, 7, 2, 6, 7, 3, 8, 2 };

var sorted = values.ToList();
sorted.Sort();
for (var index = 1; index < sorted.Count; index++)
{
    var previous = sorted[index - 1];
    var current = sorted[index];
    if (current == previous)
        Console.WriteLine(string.Format("duplicated value: {0}", current));
}

Salida:

duplicated value: 2
duplicated value: 7

http://rextester.com/SIDG48202

Kevin Struillou
fuente
0

Verifico si un IEnumerable (aray, lista, etc.) es único como este:

var isUnique = someObjectsEnum.GroupBy(o => o.SomeProperty).Max(g => g.Count()) == 1;
Namik Hajiyev
fuente