como verificar se o objeto já existe em uma lista

102

Eu tenho uma lista

  List<MyObject> myList

e estou adicionando itens a uma lista e quero verificar se esse objeto já está na lista.

então antes de fazer isso:

 myList.Add(nextObject);

Quero ver se nextObject já está na lista.

O objeto "MyObject" possui várias propriedades, mas a comparação é baseada na correspondência de duas propriedades.

Qual é a melhor maneira de fazer uma verificação antes de adicionar um novo "MyObject" a esta lista de "MyObject" s.

A única solução que pensei foi mudar de uma lista para um dicionário e, em seguida, transformar a chave em uma string concatenada das propriedades (isso parece um pouco deselegante).

Qualquer outra solução mais limpa usando lista ou LINQ ou outra coisa?

Leora
fonte

Respostas:

153

Depende das necessidades da situação específica. Por exemplo, a abordagem do dicionário seria muito boa assumindo:

  1. A lista é relativamente estável (não há muitas inserções / exclusões, para as quais os dicionários não são otimizados)
  2. A lista é muito grande (caso contrário, o overhead do dicionário não terá sentido).

Se o acima não for verdade para sua situação, basta usar o método Any():

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

Isso irá enumerar através da lista até encontrar uma correspondência ou até chegar ao fim.

Rex M
fonte
O uso de um delegado de predicado para a lista.existe é outra solução, veja abaixo, mas se você tiver listas enormes e o valor da chave com um dicionário será muito mais rápido, pois é uma tabela hash! Aproveite
Doug
1
Como verificar vários valores?
Nitin Karale
80

Basta usar o método Contains . Observe que funciona com base na função de igualdadeEquals

bool alreadyExist = list.Contains(item);
Ahmad
fonte
5
Isso não funcionou para mim, sempre disse que não existe
Si8
4
@ Si8 Se você está tentando comparar objetos, deve ter certeza de que a implementação IEquatable <T> .Equals está implementada corretamente para o tipo de seu objeto. Caso contrário, você não comparará o conteúdo do objeto. Consulte o link Contém indicado por Ahmad para obter um exemplo de como implementar isso.
Doug Knudsen
56

Se for possível usar essas 2 propriedades, você pode:

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

Tem certeza de que precisa de uma lista neste caso? Se você estiver preenchendo a lista com muitos itens, o desempenho será prejudicado com myList.Containsou myList.Any; o tempo de execução será quadrático. Você pode querer considerar o uso de uma estrutura de dados melhor. Por exemplo,

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

Você pode usar um HashSet da seguinte maneira:

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

Claro, se esta definição de igualdade para MyClassfor 'universal', você não precisa escrever uma IEqualityComparerimplementação; você pode simplesmente substituir GetHashCodee Equalsna própria classe.

Ani
fonte
Sim, bool para V era meu favorito. Por falar nisso, não faz muito tempo (eh, cerca de 3 semanas) que o HashSet não estava disponível para mim porque eu estava trabalhando no código 2.0 e abandonei a implementação Mono do HashSet porque é muito útil :)
Jon Hanna
4

Outro ponto a ser mencionado é que você deve garantir que sua função de igualdade seja a esperada. Você deve substituir o método equals para configurar quais propriedades do seu objeto devem corresponder para que duas instâncias sejam consideradas iguais.

Então você pode apenas fazer mylist.contains (item)

Fiona - myaccessible.website
fonte
3

Aqui está um aplicativo de console rápido para descrever o conceito de como resolver seu 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]);
                }
            }
        }
    }
}

Aproveitar!

Doug
fonte
3

Edit: Eu disse primeiro:


O que é deselegante na solução do dicionário. Parece perfeitamente elegante para mim, especialmente porque você só precisa definir o comparador na criação do dicionário.


É claro que não é elegante usar algo como uma chave quando também é o valor.

Portanto, eu usaria um HashSet. Se as operações posteriores exigissem indexação, eu criaria uma lista a partir dela quando a adição fosse concluída, caso contrário, apenas use o hashset.

Jon Hanna
fonte
Eu só usaria isso se a lista de objetos fosse enorme, já que é uma tabela hash e eles são ótimos para pesquisas rápidas.
Doug
0

Simples mas funciona

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

ou

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

Se você usar EF core, adicione

 .UseSerialColumn();

Exemplo

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

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