web-dev-qa-db-fra.com

Existe-t-il une méthode AddUnique similaire à Addrange () pour alist en C #

J'ai une liste en C #:

       var list = new List<Car>();
       list.AddRange(GetGreenCars());
       list.AddRange(GetBigCars());
       list.AddRange(GetSmallCars());

le problème est que certaines des mêmes voitures sont rendues dans des fonctions différentes et je ne les veux pas plus d'une fois dans la liste. Chaque voiture a un attribut de nom unique. Y at-il de toute façon je peux avoir quelque chose comme ceci ci-dessus mais j'ajouterai des articles s'ils sont uniques?

23
leora

Un List<T> ne semble pas être la collection appropriée ici. Vous voulez probablement une implémentation ISet<T> telle que HashSet<T> (ou SortedSet<T> si vous avez besoin de commander).

Pour permettre cela, vous devrez écrire une implémentation IEqualityComparer<T> qui définit l'égalité entre les voitures en fonction de la propriété Name. S'il s'agit de la définition «canonique» de car -égal, vous pouvez également envisager de construire directement cette définition dans le type Car (object.Equals, object.GetHashCode et, idéalement, implémenter IEquatable<T> également).

15
Ani

Un choix consiste à les ajouter et supprimer les répétées:

var list = new List<Car>();
list.AddRange(GetGreenCars());
list.AddRange(GetBigCars());
list.AddRange(GetSmallCars());
list = list.Distinct().ToList();
38
Ivo

Une autre option consiste à faire quelque chose comme:

public static void AddUnique<T>( this IList<T> self, IEnumerable<T> items )
{
    foreach(var item in items)
        if(!self.Contains(item))
            self.Add(item)
}


var list = new List<Car>();
list.AddUnique(GetGreenCars());
list.AddUnique(GetBigCars());
list.AddUnique(GetSmallCars());
21
Ivo

Si vous substituez la méthode .Equals() à Car pour déterminer si un objet de voiture est identique à un autre objet de voiture, les opérations suivantes devraient fonctionner sans écrire de méthode d'extension.

    var list = new List<Car>();
    list.AddRange(GetGreenCars()?.Except(list));
    list.AddRange(GetBigCars()?.Except(list));
    list.AddRange(GetSmallCars()?.Except(list));
8
Josh Gust

Encore une autre option utilisant Linq:

public static void AddUnique<T>(this IList<T> self, IEnumerable<T> items)
{
  self.AddRange(
    items.Where(x => self.FirstOrDefault(y => y.Name == x.Name) ==
    null).ToList());
}

var list = new List<Car>();
list.AddUnique(GetGreenCars());
list.AddUnique(GetBigCars());
list.AddUnique(GetSmallCars());
5
Tim Robbins

Une option différente au cas où le programmeur ne pourrait pas ou ne voudrait pas utiliser Linq.

var list = new List<Car>();
list.AddRange(GetGreenCars().FindAll((x) => !list.Contains(x)));
list.AddRange(GetBigCars().FindAll((x) => !list.Contains(x)));
list.AddRange(GetSmallCars().FindAll((x) => !list.Contains(x)));

Si la liste initiale est vide comme dans l'exemple ci-dessus, vous pouvez réellement éviter d'utiliser FindAll (...) sur le premier AddRange ().

1
Novack

J'ai créé une méthode d'extension qui ajoute uniquement des valeurs uniques à tout ce qui implémente ICollection<T> (y compris un List<T>) à partir d'un IEnumerable<T>. Contrairement aux implémentations qui utilisent List<T>.Contains(), cette méthode vous permet de spécifier une expression lambda qui détermine si deux éléments sont identiques. 

/// <summary>
/// Adds only items that do not exist in source.  May be very slow for large collections and some types of source.
/// </summary>
/// <typeparam name="T">Type in the collection.</typeparam>
/// <param name="source">Source collection</param>
/// <param name="predicate">Predicate to determine whether a new item is already in source.</param>
/// <param name="items">New items.</param>
public static void AddUniqueBy<T>(this ICollection<T> source, Func<T, T, bool> predicate, IEnumerable<T> items)
{
    foreach (T item in items)
    {
        bool existsInSource = source.Where(s => predicate(s, item)).Any();
        if (!existsInSource) source.Add(item);
    }
}

Usage:

source.AddUniqueBy<Foo>((s, i) => s.Id == i.Id, items);
1
Eric J.

et si vous voulez comparer une propriété (id dans ce cas), cela devrait fonctionner

var list = new List<string>();
list.AddRange(GetGreenCars().Where(greencar => !list.Contains(greencar, car => car.id == greencar.id)));
list.AddRange(GetBigCars().Where(bigcar => !list.Contains(bigcar, car => car.id == bigcar.id)));
list.AddRange(GetSmallCars().Where(smallcar => !list.Contains(smallcar, car => car.id == smallcar.id)));
0
kanzo