jak sprawdzić, czy obiekt już istnieje na liście

102

Mam listę

  List<MyObject> myList

i dodaję pozycje do listy i chcę sprawdzić, czy ten obiekt jest już na liście.

więc zanim to zrobię:

 myList.Add(nextObject);

Chcę zobaczyć, czy nextObject jest już na liście.

Obiekt „MyObject” ma wiele właściwości, ale porównanie opiera się na dopasowaniu dwóch właściwości.

Jaki jest najlepszy sposób sprawdzenia przed dodaniem nowego „MyObject” do tej listy „MyObject”.

Jedynym rozwiązaniem, które wymyśliłem, było przejście z listy do słownika, a następnie uczynienie klucza połączonym ciągiem właściwości (wydaje się to trochę nieeleganckie).

Jakieś inne czystsze rozwiązania wykorzystujące listę lub LINQ lub coś innego?

leora
źródło

Odpowiedzi:

153

To zależy od potrzeb konkretnej sytuacji. Na przykład podejście słownikowe byłoby całkiem dobre przy założeniu:

  1. Lista jest względnie stabilna (niewiele wstawek / usunięć, dla których słowniki nie są zoptymalizowane)
  2. Lista jest dość obszerna (w przeciwnym razie obciążenie słownika nie ma sensu).

Jeśli powyższe nie są prawdziwe w Twojej sytuacji, po prostu użyj metody Any():

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

Spowoduje to wyliczenie listy, dopóki nie znajdzie dopasowania lub do końca.

Rex M
źródło
Użycie delegata predykatu dla listy.exists to kolejne rozwiązanie, które można zobaczyć poniżej, ale jeśli masz ogromne listy i wartość klucza ze słownikiem, będzie znacznie szybsze, ponieważ jest to tabela skrótów! Ciesz się
Doug
1
Jak sprawdzić wiele wartości?
Nitin Karale
80

Po prostu użyj metody Zawiera . Zauważ, że działa w oparciu o funkcję równościEquals

bool alreadyExist = list.Contains(item);
Ahmad
źródło
5
To nie działało dla mnie, zawsze mówiło, że nie istnieje
Si8
4
@ Si8 Jeśli próbujesz porównać obiekty, musisz mieć pewność, że implementacja IEquatable <T> .Equals została zaimplementowana prawidłowo dla typu Twojego obiektu. W przeciwnym razie nie będziesz porównywać zawartości obiektu. Zobacz wskazany link Zawiera Ahmad, aby zobaczyć przykład, jak to zaimplementować.
Doug Knudsen,
56

Jeśli użycie tych dwóch właściwości jest możliwe do utrzymania, możesz:

bool alreadyExists = myList.Any(x=> x.Foo=="ooo" && x.Bar == "bat");
p.campbell
źródło
7

Czy na pewno potrzebujesz listy w tym przypadku? Jeśli wypełniasz listę wieloma elementami, wydajność spadnie z myList.Containslub myList.Any; czas wykonywania będzie kwadratowy. Warto rozważyć zastosowanie lepszej struktury danych. Na przykład,

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

Możesz użyć HashSet w następujący sposób:

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

Oczywiście, jeśli ta definicja równości dla MyClassjest „uniwersalna”, nie musisz pisać IEqualityComparerimplementacji; możesz po prostu zastąpić GetHashCodei Equalsw samej klasie.

Ani
źródło
Tak, bool dla V był moim ulubionym. Jeśli o to chodzi, to nie tak dawno temu (eh, około 3 tygodnie) HashSet nie był dla mnie dostępny, ponieważ pracowałem nad kodem 2.0 i porzuciłem implementację HashSet Mono, ponieważ jest tak cholernie przydatna :)
Jon Hanna
4

Inną kwestią, o której warto wspomnieć, jest to, że powinieneś upewnić się, że Twoja funkcja równości jest zgodna z oczekiwaniami. Należy nadpisać metodę equals, aby ustawić, jakie właściwości obiektu muszą pasować, aby dwa wystąpienia były uznawane za równe.

Następnie możesz po prostu zrobić mylist.contains (item)

Fiona - myaccessible.website
źródło
3

Oto szybka aplikacja konsoli, która przedstawia koncepcję rozwiązania problemu.

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

Cieszyć się!

Doug
źródło
3

Edycja: najpierw powiedziałem:


Co jest nieeleganckie w rozwiązaniu słownikowym. Wydaje mi się to całkowicie eleganckie, zwłaszcza że podczas tworzenia słownika wystarczy ustawić komparator.


Oczywiście nieeleganckie jest używanie czegoś jako klucza, gdy jest to również wartość.

Dlatego użyłbym HashSet. Jeśli późniejsze operacje wymagałyby indeksowania, utworzyłbym z niego listę po zakończeniu dodawania, w przeciwnym razie po prostu użyj skrótu.

Jon Hanna
źródło
Użyłbym tego tylko wtedy, gdyby lista obiektów była ogromna, ponieważ jest to tabela skrótów i świetnie nadają się do szybkiego wyszukiwania.
Doug
0

Proste, ale działa

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

lub

 if (!MyList.Contains(nextObject))
    MyList.Add(nextObject);
Opt Prutal
źródło
-1

Jeśli używasz EF core, dodaj

 .UseSerialColumn();

Przykład

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

            entity.Property(e => e.Id)
                .HasColumnName("id")
                .UseSerialColumn();
});
mdimai666
źródło