Question

Quelqu'un a-t-il une méthode rapide pour dédupliquer une liste générique en C# ?

Était-ce utile?

La solution

Peut-être devriez-vous envisager d'utiliser un Jeu de hachage.

Depuis le lien MSDN :

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        HashSet<int> evenNumbers = new HashSet<int>();
        HashSet<int> oddNumbers = new HashSet<int>();

        for (int i = 0; i < 5; i++)
        {
            // Populate numbers with just even numbers.
            evenNumbers.Add(i * 2);

            // Populate oddNumbers with just odd numbers.
            oddNumbers.Add((i * 2) + 1);
        }

        Console.Write("evenNumbers contains {0} elements: ", evenNumbers.Count);
        DisplaySet(evenNumbers);

        Console.Write("oddNumbers contains {0} elements: ", oddNumbers.Count);
        DisplaySet(oddNumbers);

        // Create a new HashSet populated with even numbers.
        HashSet<int> numbers = new HashSet<int>(evenNumbers);
        Console.WriteLine("numbers UnionWith oddNumbers...");
        numbers.UnionWith(oddNumbers);

        Console.Write("numbers contains {0} elements: ", numbers.Count);
        DisplaySet(numbers);
    }

    private static void DisplaySet(HashSet<int> set)
    {
        Console.Write("{");
        foreach (int i in set)
        {
            Console.Write(" {0}", i);
        }
        Console.WriteLine(" }");
    }
}

/* This example produces output similar to the following:
 * evenNumbers contains 5 elements: { 0 2 4 6 8 }
 * oddNumbers contains 5 elements: { 1 3 5 7 9 }
 * numbers UnionWith oddNumbers...
 * numbers contains 10 elements: { 0 2 4 6 8 1 3 5 7 9 }
 */

Autres conseils

Si vous utilisez .Net 3+, vous pouvez utiliser Linq.

List<T> withDupes = LoadSomeData();
List<T> noDupes = withDupes.Distinct().ToList();

Que diriez-vous:-

var noDupes = list.Distinct().ToList();

Dans .net 3.5 ?

Initialisez simplement un HashSet avec une List du même type :

var noDupes = new HashSet<T>(withDupes);

Ou, si vous souhaitez qu'une liste soit renvoyée :

var noDupsList = new HashSet<T>(withDupes).ToList();

Triez-le, puis cochez-en deux et deux l'un à côté de l'autre, car les doublons s'agglutineront.

Quelque chose comme ça:

list.Sort();
Int32 index = list.Count - 1;
while (index > 0)
{
    if (list[index] == list[index - 1])
    {
        if (index < list.Count - 1)
            (list[index], list[list.Count - 1]) = (list[list.Count - 1], list[index]);
        list.RemoveAt(list.Count - 1);
        index--;
    }
    else
        index--;
}

Remarques:

  • La comparaison se fait de l'arrière vers l'avant, pour éviter d'avoir à recourir à la liste après chaque suppression
  • Cet exemple utilise désormais des tuples de valeurs C# pour effectuer l'échange, remplacez-les par le code approprié si vous ne pouvez pas l'utiliser
  • Le résultat final n'est plus trié

Cela a fonctionné pour moi.utilisez simplement

List<Type> liIDs = liIDs.Distinct().ToList<Type>();

Remplacez "Type" par le type souhaité, par ex.int.

J'aime utiliser cette commande :

List<Store> myStoreList = Service.GetStoreListbyProvince(provinceId)
                                                 .GroupBy(s => s.City)
                                                 .Select(grp => grp.FirstOrDefault())
                                                 .OrderBy(s => s.City)
                                                 .ToList();

J'ai ces champs dans ma liste :ID, Storename, ville, code postal que je voulais afficher la liste des villes dans une liste déroulante qui a des valeurs en double.solution:Regroupez par ville puis choisissez la première de la liste.

J'espère que ça aide :)

Comme Kronoz l'a dit dans .Net 3.5, vous pouvez utiliser Distinct().

Dans .Net 2, vous pouvez l'imiter :

public IEnumerable<T> DedupCollection<T> (IEnumerable<T> input) 
{
    var passedValues = new HashSet<T>();

    // Relatively simple dupe check alg used as example
    foreach(T item in input)
        if(passedValues.Add(item)) // True if item is new
            yield return item;
}

Cela pourrait être utilisé pour dédoublonner n’importe quelle collection et renverra les valeurs dans l’ordre d’origine.

Il est normalement beaucoup plus rapide de filtrer une collection (car les deux Distinct() et cet exemple le fait) que ce serait le cas pour en supprimer des éléments.

Une méthode d'extension pourrait être une bonne solution...quelque chose comme ça:

public static List<T> Deduplicate<T>(this List<T> listToDeduplicate)
{
    return listToDeduplicate.Distinct().ToList();
}

Et puis appelez comme ceci, par exemple :

List<int> myFilteredList = unfilteredList.Deduplicate();

En Java (je suppose que C# est plus ou moins identique) :

list = new ArrayList<T>(new HashSet<T>(list))

Si vous vouliez vraiment muter la liste d'origine :

List<T> noDupes = new ArrayList<T>(new HashSet<T>(list));
list.clear();
list.addAll(noDupes);

Pour préserver l'ordre, remplacez simplement HashSet par LinkedHashSet.

Utilisez Linq syndicat méthode.

Note:Cette solution ne nécessite aucune connaissance de Linq, mis à part le fait qu'elle existe.

Code

Commencez par ajouter ce qui suit en haut de votre fichier de classe :

using System.Linq;

Maintenant, vous pouvez utiliser ce qui suit pour supprimer les doublons d'un objet appelé, obj1:

obj1 = obj1.Union(obj1).ToList();

Note:Renommer obj1 au nom de votre objet.

Comment ça fonctionne

  1. La commande Union répertorie une de chaque entrée de deux objets source.Puisque obj1 est à la fois des objets source, cela réduit obj1 à un de chaque entrée.

  2. Le ToList() renvoie une nouvelle liste.C'est nécessaire, car les commandes Linq comme Union renvoie le résultat sous forme de résultat IEnumerable au lieu de modifier la liste d'origine ou de renvoyer une nouvelle liste.

Si la commande ne vous intéresse pas, vous pouvez simplement placer les articles dans un HashSet, si tu faire souhaitez maintenir l'ordre, vous pouvez faire quelque chose comme ceci :

var unique = new List<T>();
var hs = new HashSet<T>();
foreach (T t in list)
    if (hs.Add(t))
        unique.Add(t);

Ou à la manière Linq :

var hs = new HashSet<T>();
list.All( x =>  hs.Add(x) );

Modifier: Le HashSet la méthode est O(N) temps et O(N) espace tout en triant puis en rendant unique (comme suggéré par @lassevk et autres) est O(N*lgN) temps et O(1) l'espace donc il n'est pas si clair pour moi (comme c'était le cas à première vue) que la méthode de tri est inférieure (mes excuses pour le vote négatif temporaire...)

Voici une méthode d'extension pour supprimer les doublons adjacents in situ.Appelez Sort() d’abord et transmettez le même IComparer.Cela devrait être plus efficace que Lasse V.La version de Karlsen qui appelle RemoveAt à plusieurs reprises (entraînant des déplacements de mémoire de plusieurs blocs).

public static void RemoveAdjacentDuplicates<T>(this List<T> List, IComparer<T> Comparer)
{
    int NumUnique = 0;
    for (int i = 0; i < List.Count; i++)
        if ((i == 0) || (Comparer.Compare(List[NumUnique - 1], List[i]) != 0))
            List[NumUnique++] = List[i];
    List.RemoveRange(NumUnique, List.Count - NumUnique);
}

En tant que méthode d'assistance (sans Linq) :

public static List<T> Distinct<T>(this List<T> list)
{
    return (new HashSet<T>(list)).ToList();
}

Installation du PlusLINQ package via Nuget, vous pouvez facilement distinguer la liste d'objets par une propriété

IEnumerable<Catalogue> distinctCatalogues = catalogues.DistinctBy(c => c.CatalogueCode); 

Cela prend distinct (les éléments sans éléments dupliqués) et le reconvertit en liste :

List<type> myNoneDuplicateValue = listValueWithDuplicate.Distinct().ToList();

Il serait peut-être plus facile de simplement s'assurer que les doublons ne sont pas ajoutés à la liste.

if(items.IndexOf(new_item) < 0) 
    items.add(new_item)

Une autre façon dans .Net 2.0

    static void Main(string[] args)
    {
        List<string> alpha = new List<string>();

        for(char a = 'a'; a <= 'd'; a++)
        {
            alpha.Add(a.ToString());
            alpha.Add(a.ToString());
        }

        Console.WriteLine("Data :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t); });

        alpha.ForEach(delegate (string v)
                          {
                              if (alpha.FindAll(delegate(string t) { return t == v; }).Count > 1)
                                  alpha.Remove(v);
                          });

        Console.WriteLine("Unique Result :");
        alpha.ForEach(delegate(string t) { Console.WriteLine(t);});
        Console.ReadKey();
    }

Il existe de nombreuses façons de résoudre le problème des doublons dans la liste, ci-dessous en voici une :

List<Container> containerList = LoadContainer();//Assume it has duplicates
List<Container> filteredList = new  List<Container>();
foreach (var container in containerList)
{ 
  Container duplicateContainer = containerList.Find(delegate(Container checkContainer)
  { return (checkContainer.UniqueId == container.UniqueId); });
   //Assume 'UniqueId' is the property of the Container class on which u r making a search

    if(!containerList.Contains(duplicateContainer) //Add object when not found in the new class object
      {
        filteredList.Add(container);
       }
  }

Bravo Ravi Ganesan

Voici une solution simple qui ne nécessite aucun LINQ difficile à lire ni aucun tri préalable de la liste.

   private static void CheckForDuplicateItems(List<string> items)
    {
        if (items == null ||
            items.Count == 0)
            return;

        for (int outerIndex = 0; outerIndex < items.Count; outerIndex++)
        {
            for (int innerIndex = 0; innerIndex < items.Count; innerIndex++)
            {
                if (innerIndex == outerIndex) continue;
                if (items[outerIndex].Equals(items[innerIndex]))
                {
                    // Duplicate Found
                }
            }
        }
    }

La réponse de David J. est une bonne méthode, pas besoin d'objets supplémentaires, de tri, etc.Il peut cependant être amélioré :

for (int innerIndex = items.Count - 1; innerIndex > outerIndex ; innerIndex--)

Ainsi, la boucle externe va du haut vers le bas pour toute la liste, mais la boucle interne va vers le bas "jusqu'à ce que la position de la boucle externe soit atteinte".

La boucle externe s'assure que la liste entière est traitée, la boucle interne trouve les doublons réels, ceux-ci ne peuvent se produire que dans la partie que la boucle externe n'a pas encore traitée.

Ou si vous ne voulez pas faire de bas en haut pour la boucle interne, vous pouvez faire démarrer la boucle interne à externalIndex + 1.

Vous pouvez utiliser Union

obj2 = obj1.Union(obj1).ToList();

Si vous avez des cours de remorquage Product et Customer et nous voulons supprimer les éléments en double de leur liste

public class Product
{
    public int Id { get; set; }
    public string ProductName { get; set; }

}

public class Customer
{
    public int Id { get; set; }
    public string CustomerName { get; set; }

}

Vous devez définir une classe générique dans le formulaire ci-dessous

public class ItemEqualityComparer<T> : IEqualityComparer<T> where T : class
{
    private readonly PropertyInfo _propertyInfo;

    public ItemEqualityComparer(string keyItem)
    {
        _propertyInfo = typeof(T).GetProperty(keyItem, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
    }

    public bool Equals(T x, T y)
    {
        var xValue = _propertyInfo?.GetValue(x, null);
        var yValue = _propertyInfo?.GetValue(y, null);
        return xValue != null && yValue != null && xValue.Equals(yValue);
    }

    public int GetHashCode(T obj)
    {
        var propertyValue = _propertyInfo.GetValue(obj, null);
        return propertyValue == null ? 0 : propertyValue.GetHashCode();
    }
}

Ensuite, vous pouvez supprimer les éléments en double dans votre liste.

var products = new List<Product>
            {
                new Product{ProductName = "product 1" ,Id = 1,},
                new Product{ProductName = "product 2" ,Id = 2,},
                new Product{ProductName = "product 2" ,Id = 4,},
                new Product{ProductName = "product 2" ,Id = 4,},
            };
var productList = products.Distinct(new ItemEqualityComparer<Product>(nameof(Product.Id))).ToList();

var customers = new List<Customer>
            {
                new Customer{CustomerName = "Customer 1" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
                new Customer{CustomerName = "Customer 2" ,Id = 5,},
            };
var customerList = customers.Distinct(new ItemEqualityComparer<Customer>(nameof(Customer.Id))).ToList();

ce code supprime les éléments en double en Id si vous souhaitez supprimer les éléments en double par une autre propriété, vous pouvez modifier nameof(YourClass.DuplicateProperty) même nameof(Customer.CustomerName) puis supprimez les éléments en double en CustomerName Propriété.

  public static void RemoveDuplicates<T>(IList<T> list )
  {
     if (list == null)
     {
        return;
     }
     int i = 1;
     while(i<list.Count)
     {
        int j = 0;
        bool remove = false;
        while (j < i && !remove)
        {
           if (list[i].Equals(list[j]))
           {
              remove = true;
           }
           j++;
        }
        if (remove)
        {
           list.RemoveAt(i);
        }
        else
        {
           i++;
        }
     }  
  }

Une mise en œuvre simple et intuitive :

public static List<PointF> RemoveDuplicates(List<PointF> listPoints)
{
    List<PointF> result = new List<PointF>();

    for (int i = 0; i < listPoints.Count; i++)
    {
        if (!result.Contains(listPoints[i]))
            result.Add(listPoints[i]);
        }

        return result;
    }

Toutes les réponses copient des listes, ou créent une nouvelle liste, ou utilisent des fonctions lentes, ou sont tout simplement terriblement lentes.

D'après ma compréhension, c'est le méthode la plus rapide et la moins chère Je sais (également soutenu par un programmeur très expérimenté spécialisé dans l'optimisation physique en temps réel).

// Duplicates will be noticed after a sort O(nLogn)
list.Sort();

// Store the current and last items. Current item declaration is not really needed, and probably optimized by the compiler, but in case it's not...
int lastItem = -1;
int currItem = -1;

int size = list.Count;

// Store the index pointing to the last item we want to keep in the list
int last = size - 1;

// Travel the items from last to first O(n)
for (int i = last; i >= 0; --i)
{
    currItem = list[i];

    // If this item was the same as the previous one, we don't want it
    if (currItem == lastItem)
    {
        // Overwrite last in current place. It is a swap but we don't need the last
       list[i] = list[last];

        // Reduce the last index, we don't want that one anymore
        last--;
    }

    // A new item, we store it and continue
    else
        lastItem = currItem;
}

// We now have an unsorted list with the duplicates at the end.

// Remove the last items just once
list.RemoveRange(last + 1, size - last - 1);

// Sort again O(n logn)
list.Sort();

Le coût final est de :

nlogn + n + nlogn = n + 2nlogn = O (nlogn) ce qui est plutôt sympa.

Remarque concernant RemoveRange :Comme nous ne pouvons pas définir le nombre de la liste et éviter d'utiliser les fonctions Supprimer, je ne connais pas exactement la vitesse de cette opération mais je suppose que c'est le moyen le plus rapide.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top