cómo comprobar si el objeto ya existe en una lista

102

Tengo una lista

  List<MyObject> myList

y estoy agregando elementos a una lista y quiero verificar si ese objeto ya está en la lista.

así que antes de hacer esto:

 myList.Add(nextObject);

Quiero ver si nextObject ya está en la lista.

El objeto "MyObject" tiene varias propiedades, pero la comparación se basa en la coincidencia de dos propiedades.

¿Cuál es la mejor manera de hacer una verificación antes de agregar un nuevo "MyObject" a esta lista de "MyObject"?

La única solución que pensé fue cambiar de una lista a un diccionario y luego convertir la clave en una cadena concatenada de las propiedades (esto parece un poco poco elegante).

¿Alguna otra solución más limpia usando list o LINQ o algo más?

leora
fuente

Respuestas:

153

Depende de las necesidades de la situación específica. Por ejemplo, el enfoque del diccionario sería bastante bueno asumiendo:

  1. La lista es relativamente estable (no hay muchas inserciones / eliminaciones, para las que los diccionarios no están optimizados)
  2. La lista es bastante grande (de lo contrario, la sobrecarga del diccionario no tiene sentido).

Si lo anterior no es cierto para su situación, simplemente use el método Any():

Item wonderIfItsPresent = ...
bool containsItem = myList.Any(item => item.UniqueProperty == wonderIfItsPresent.UniqueProperty);

Esto enumerará a través de la lista hasta que encuentre una coincidencia o hasta que llegue al final.

Rex M
fuente
El uso de un predicado delegado para list.exists es otra solución que se muestra a continuación, pero si tiene listas enormes y un valor clave con un diccionario será mucho más rápido ya que es una tabla hash. Disfrute
Doug
1
¿Cómo comprobar el valor múltiple?
Nitin Karale
80

Simplemente use el método Contiene . Tenga en cuenta que funciona en función de la función de igualdadEquals

bool alreadyExist = list.Contains(item);
Ahmad
fuente
5
Esto no funcionó para mí, siempre decía que no existe
Si8
4
@ Si8 Si está intentando comparar objetos, debe asegurarse de que la implementación de IEquatable <T> .Equals esté implementada correctamente para el tipo de su objeto. De lo contrario, no comparará el contenido del objeto. Consulte el enlace Contiene que Ahmad indicó para ver un ejemplo de cómo implementar esto.
Doug Knudsen
56

Si se puede mantener el uso de esas 2 propiedades, podría:

bool alreadyExists = myList.Any(x=> x.Foo=="ooo" && x.Bar == "bat");
p.campbell
fuente
7

¿Está seguro de que necesita una lista en este caso? Si está completando la lista con muchos elementos, el rendimiento se verá afectado con myList.Containso myList.Any; el tiempo de ejecución será cuadrático. Es posible que desee considerar el uso de una mejor estructura de datos. Por ejemplo,

 public class MyClass
    {
        public string Property1 { get; set; }
        public string Property2 { get; set; }

    }

    public class MyClassComparer : EqualityComparer<MyClass>
    {
        public override bool Equals(MyClass x, MyClass y)
        {
            if(x == null || y == null)
               return x == y;

            return x.Property1 == y.Property1 && x.Property2 == y.Property2;
        }

        public override int GetHashCode(MyClass obj)
        {
            return obj == null ? 0 : (obj.Property1.GetHashCode() ^ obj.Property2.GetHashCode());
        }
    }

Puede usar un HashSet de la siguiente manera:

  var set = new HashSet<MyClass>(new MyClassComparer());
  foreach(var myClass in ...)
     set.Add(myClass);

Por supuesto, si esta definición de igualdad para MyClasses "universal", no es necesario escribir una IEqualityComparerimplementación; podría simplemente anular GetHashCodey Equalsen la propia clase.

Y yo
fuente
Sí, bool for V era mi favorito. De hecho, no fue hace tanto tiempo (eh, alrededor de 3 semanas) que HashSet no estaba disponible para mí porque estaba trabajando en el código 2.0, y dejé caer la implementación Mono de HashSet porque es muy útil :)
Jon Hanna
4

Otro punto a mencionar es que debe asegurarse de que su función de igualdad sea la que espera. Debe anular el método equals para configurar qué propiedades de su objeto deben coincidir para que dos instancias se consideren iguales.

Entonces puedes simplemente hacer mylist.contains (item)

Fiona - myaccessible.website
fuente
3

Aquí hay una aplicación de consola rápida para representar el concepto de cómo resolver su problema.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication3
{
    public class myobj
    {
        private string a = string.Empty;
        private string b = string.Empty;

        public myobj(string a, string b)
        {
            this.a = a;
            this.b = b;
        }

        public string A
        {
            get
            {
                return a;
            }
        }

        public string B
        {
            get
            {
                return b;
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            List<myobj> list = new List<myobj>();
            myobj[] objects = { new myobj("a", "b"), new myobj("c", "d"), new myobj("a", "b") };


            for (int i = 0; i < objects.Length; i++)
            {
                if (!list.Exists((delegate(myobj x) { return (string.Equals(x.A, objects[i].A) && string.Equals(x.B, objects[i].B)) ? true : false; })))
                {
                    list.Add(objects[i]);
                }
            }
        }
    }
}

¡Disfrutar!

Doug
fuente
3

Editar: primero dije:


¿Qué tiene de elegante la solución del diccionario? Me parece perfectamente elegante, especialmente porque solo necesita configurar el comparador en la creación del diccionario.


Sin embargo, por supuesto, no es elegante usar algo como clave cuando también es el valor.

Por lo tanto, usaría un HashSet. Si las operaciones posteriores requerían indexación, crearía una lista a partir de ella cuando se realizara la adición; de lo contrario, solo use el hashset.

Jon Hanna
fuente
Solo usaría esto si la lista de objetos fuera enorme, ya que es una tabla hash y son excelentes para búsquedas rápidas.
Doug
0

Simple pero funciona

MyList.Remove(nextObject)
MyList.Add(nextObject)

o

 if (!MyList.Contains(nextObject))
    MyList.Add(nextObject);
Opt Prutal
fuente
-1

Si usa EF core, agregue

 .UseSerialColumn();

Ejemplo

modelBuilder.Entity<JobItem>(entity =>
        {
            entity.ToTable("jobs");

            entity.Property(e => e.Id)
                .HasColumnName("id")
                .UseSerialColumn();
});
mdimai666
fuente