come verificare se l'oggetto esiste già in un elenco


102

Ho una lista

  List<MyObject> myList

e sto aggiungendo elementi a un elenco e desidero controllare se quell'oggetto è già nell'elenco.

quindi prima di farlo:

 myList.Add(nextObject);

Voglio vedere se nextObject è già nell'elenco.

L'oggetto "MyObject" ha un numero di proprietà ma il confronto si basa sulla corrispondenza su due proprietà.

Qual è il modo migliore per eseguire un controllo prima di aggiungere un nuovo "MyObject" a questo elenco di "MyObject".

L'unica soluzione che ho pensato è stata quella di passare da un elenco a un dizionario e quindi rendere la chiave una stringa concatenata delle proprietà (questo sembra un po 'poco elegante).

Altre soluzioni più pulite usando list o LINQ o qualcos'altro?

Risposte:


153

Dipende dalle esigenze della situazione specifica. Ad esempio, l'approccio del dizionario sarebbe abbastanza buono assumendo:

  1. L'elenco è relativamente stabile (non molti inserimenti / eliminazioni, per i quali i dizionari non sono ottimizzati)
  2. L'elenco è piuttosto ampio (altrimenti l'overhead del dizionario è inutile).

Se quanto sopra non è vero per la tua situazione, usa il metodo Any():

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

Questo enumererà l'elenco finché non trova una corrispondenza o finché non raggiunge la fine.


L'uso di un delegato predicato per list.exists è un'altra soluzione vedi sotto, ma se hai liste enormi e valore chiave con un dizionario sarà molto più veloce in quanto è una tabella hash! Buon divertimento
— Doug

1
Come controllare più valori?
— Nitin Karale

80

Usa semplicemente il metodo Contiene . Nota che funziona in base alla funzione di uguaglianzaEquals

bool alreadyExist = list.Contains(item);

5
Questo non ha funzionato per me, ha sempre detto che non esiste
— Si8

4
@ Si8 Se stai cercando di confrontare oggetti, devi essere sicuro che l'implementazione di IEquatable <T> .Equals sia implementata correttamente per il tipo di oggetto. Altrimenti, non confronterai il contenuto dell'oggetto. Vedere il collegamento Contiene indicato da Ahmad per un esempio di come implementarlo.
— Doug Knudsen

56

Se è possibile mantenere queste 2 proprietà, potresti:

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

7

Sei sicuro di aver bisogno di un elenco in questo caso? Se si popola l'elenco con molti elementi, le prestazioni ne risentiranno con myList.Containso myList.Any; il tempo di esecuzione sarà quadratico. Potresti prendere in considerazione l'utilizzo di una struttura dati migliore. Per esempio,

 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());
        }
    }

Puoi usare un HashSet nel modo seguente:

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

Naturalmente, se questa definizione di uguaglianza per MyClassè "universale", non è necessario scrivere IEqualityComparerun'implementazione; potresti semplicemente sovrascrivere GetHashCodee Equalsnella classe stessa.


Sì, bool per V era il mio preferito. Del resto, non è passato molto tempo (eh, circa 3 settimane) che HashSet non era disponibile per me perché stavo lavorando su codice 2.0, e ho lasciato l'implementazione Mono di HashSet perché è così dannatamente utile :)
— Jon Hanna

4

Un altro punto da menzionare è che dovresti assicurarti che la tua funzione di uguaglianza sia come ti aspetti. È necessario sovrascrivere il metodo equals per impostare le proprietà dell'oggetto che devono corrispondere affinché due istanze siano considerate uguali.

Quindi puoi semplicemente fare mylist.contains (item)


3

Ecco una rapida app per console per descrivere il concetto di come risolvere il tuo 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]);
                }
            }
        }
    }
}

Godere!


3

Modifica: prima avevo detto:


Cosa c'è di inelegante nella soluzione del dizionario. Mi sembra perfettamente elegante, specialmente dal momento che devi solo impostare il comparatore nella creazione del dizionario.


Ovviamente, però, non è elegante usare qualcosa come chiave quando è anche il valore.

Quindi userei un HashSet. Se le operazioni successive richiedessero l'indicizzazione, creerei un elenco da esso quando l'aggiunta è stata eseguita, altrimenti, usa semplicemente l'hashset.


Lo userei solo se l'elenco di oggetti fosse enorme poiché è una tabella hash e sono ottimi per ricerche veloci.
— Doug

0

Semplice ma funziona

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

o

 if (!MyList.Contains(nextObject))
    MyList.Add(nextObject);

-1

Se usi EF core aggiungi

 .UseSerialColumn();

Esempio

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

            entity.Property(e => e.Id)
                .HasColumnName("id")
                .UseSerialColumn();
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.