comment vérifier si l'objet existe déjà dans une liste

102

J'ai une liste

  List<MyObject> myList

et j'ajoute des éléments à une liste et je veux vérifier si cet objet est déjà dans la liste.

donc avant de faire ça:

 myList.Add(nextObject);

Je veux voir si nextObject est déjà dans la liste.

L'objet "MyObject" a un certain nombre de propriétés mais la comparaison est basée sur la correspondance sur deux propriétés.

Quelle est la meilleure façon de faire une vérification avant d'ajouter un nouveau "MyObject" à cette liste de "MyObject" s.

La seule solution que j'ai imaginée était de passer d'une liste à un dictionnaire, puis de faire de la clé une chaîne concaténée des propriétés (cela semble un peu peu élégant).

D'autres solutions plus propres utilisant la liste ou LINQ ou autre chose?

Léora
la source

Réponses:

153

Cela dépend des besoins de la situation spécifique. Par exemple, l'approche par dictionnaire serait assez bonne en supposant:

  1. La liste est relativement stable (pas beaucoup d'insertions / suppressions, pour lesquelles les dictionnaires ne sont pas optimisés)
  2. La liste est assez longue (sinon la surcharge du dictionnaire est inutile).

Si ce qui précède n'est pas vrai pour votre situation, utilisez simplement la méthode Any():

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

Cela énumérera dans la liste jusqu'à ce qu'il trouve une correspondance, ou jusqu'à ce qu'il atteigne la fin.

Rex M
la source
L'utilisation d'un délégué de prédicat pour la liste.exists est une autre solution voir ci-dessous, mais si vous avez des listes énormes et une valeur clé avec un dictionnaire sera beaucoup plus rapide car il s'agit d'une table de hachage! Enjoy
Doug
1
Comment vérifier plusieurs valeurs?
Nitin Karale
80

Utilisez simplement la méthode Contains . Notez que cela fonctionne basé sur la fonction d'égalitéEquals

bool alreadyExist = list.Contains(item);
Ahmad
la source
5
Cela n'a pas fonctionné pour moi, il a toujours dit que cela n'existait pas
Si8
4
@ Si8 Si vous essayez de comparer des objets, vous devez vous assurer que l'implémentation IEquatable <T> .Equals est correctement implémentée pour le type de votre objet. Sinon, vous ne comparerez pas le contenu de l'objet. Voir le lien Contains indiqué par Ahmad pour un exemple de mise en œuvre.
Doug Knudsen
56

S'il est maintenable d'utiliser ces 2 propriétés, vous pouvez:

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

Êtes-vous sûr d'avoir besoin d'une liste dans ce cas? Si vous remplissez la liste avec de nombreux éléments, les performances en souffriront avec myList.Containsou myList.Any; le temps d'exécution sera quadratique. Vous voudrez peut-être envisager d'utiliser une meilleure structure de données. Par exemple,

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

Vous pouvez utiliser un HashSet de la manière suivante:

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

Bien sûr, si cette définition de l'égalité pour MyClassest «universelle», vous n'avez pas besoin d'écrire une IEqualityComparerimplémentation; vous pouvez simplement remplacer GetHashCodeet Equalsdans la classe elle-même.

Ani
la source
Oui, bool pour V était mon préféré. D'ailleurs, il n'y a pas si longtemps (hein, environ 3 semaines) que HashSet n'était pas disponible pour moi parce que je travaillais sur du code 2.0, et j'ai abandonné l'implémentation Mono de HashSet parce que c'est tellement utile :)
Jon Hanna
4

Un autre point à mentionner est que vous devez vous assurer que votre fonction d'égalité est conforme à vos attentes. Vous devez remplacer la méthode equals pour définir les propriétés de votre objet devant correspondre pour que deux instances soient considérées comme égales.

Ensuite, vous pouvez simplement faire mylist.contains (item)

Fiona - myaccessible.website
la source
3

Voici une application de console rapide pour illustrer le concept de résolution de votre problème.

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

Prendre plaisir!

Doug
la source
3

Edit: j'avais d'abord dit:


Ce qui est inélégant dans la solution du dictionnaire. Cela me semble parfaitement élégant, d'autant plus qu'il suffit de paramétrer le comparateur lors de la création du dictionnaire.


Bien sûr, cependant, il est inélégant d'utiliser quelque chose comme clé alors que c'est aussi la valeur.

Par conséquent, j'utiliserais un HashSet. Si des opérations ultérieures nécessitaient une indexation, je créerais une liste à partir de celle-ci lorsque l'ajout était terminé, sinon, utilisez simplement le hashset.

Jon Hanna
la source
Je n'utiliserais cela que si la liste des objets était énorme car il s'agit d'une table de hachage et qu'ils sont parfaits pour les recherches rapides.
Doug
0

Simple mais ça marche

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

ou

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

Si vous utilisez EF core, ajoutez

 .UseSerialColumn();

Exemple

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

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