44 votes

Éliminer les doublons consécutifs d'éléments de liste

Y a-t-il un moyen "sympa" d'éliminer duplicatas consécutifs d'éléments de liste ?

Exemple :

["red"; "red"; "blue"; "green"; "green"; "red"; "red"; "yellow"; "white"; "white"; "red"; "white"; "white"] 

devrait devenir

["red"; "blue"; "green"; "red"; "yellow"; "white"; "red"; "white"]

--Par "agréable", j'entends le plus lisible et compréhensible pour un nouvel utilisateur et une exécution rapide :)

0 votes

Je pense que stackoverflow.com/questions/47752/ vous répondrait

0 votes

vous avez quand même obtenu "blanc" 2 fois dans votre exemple

70voto

Simon Bartlett Points 1395

Une solution simple et très lisible :

List<string> results = new List<string>();
foreach (var element in array)
{
    if(results.Count == 0 || results.Last() != element)
        results.Add(element);
}

3 votes

Je pose cette question lors des entretiens et personne n'a eu une solution aussi élégante. Ayez un upvote.

0 votes

C'est extrêmement inefficace. Last énumère tous les éléments, et vous l'appelez à chaque itération.

3 votes

@TheodorZoulias Il est efficace avec une List<T> . Plus précisément, Last() est rapide si la séquence met en œuvre IList<T> . Et List<T> fait .

19voto

Alex J Points 4142

Vous pouvez créer le vôtre, à la manière de linq.

// For completeness, this is two methods to ensure that the null check 
// is done eagerly while the loop is done lazily. If that's not an issue, 
// you can forego the check and just use the main function.

public static IEnumerable<T> NonConsecutive<T>(this IEnumerable<T> input)
{
  if (input == null) throw new ArgumentNullException("input");
  return NonConsecutiveImpl(input);
}

static IEnumerable<T> NonConsecutiveImpl<T>(this IEnumerable<T> input)
{
   bool isFirst = true;
   T last = default(T);
   foreach (var item in input) {
      if (isFirst || !object.Equals(item, last)) {
          yield return item;
          last = item;
          isFirst = false;
      }
   }
}

Et utiliser comme

array.NonConsecutive().ToArray()

L'avantage est qu'elle est évaluée paresseusement, donc vous pouvez l'utiliser sur n'importe quelle énumération sans avoir à la consommer dans son intégralité, et l'enchaîner avec d'autres méthodes linq (ex : array.Where(i => i != "red").NonConsecutive().Skip(1).ToArray() ). Si vous n'avez pas cette exigence et que vous souhaitez simplement travailler avec des tableaux, une solution comme celle de Simon Bartlett pourrait être légèrement plus performante.

Pour plus d'informations sur la raison pour laquelle il doit s'agir de deux méthodes, voir ici

0 votes

L'idée générale semble correcte, mais que se passe-t-il si le premier élément est null ?

2 votes

Noté, un drapeau a été ajouté pour répondre à ce problème.

1 votes

Bonne solution. Puisque c'est très similaire à la Distinct je pourrais envisager de la nommer en conséquence, à savoir ConsecutiveDistict .

8voto

Anton Semenov Points 3375

Vous pouvez créer une méthode générique simple à cette fin, comme ci-dessous :

[EDIT 2] (grand merci à Eric Lippert)

    public static List<T> ExcludeConsecutiveDuplicates<T>(List<T> InputList)
    {
        object lastItem = null;
        List<T> result = new List<T>();

        for (int i = 0; i < InputList.Count; i++)
        {
            if (i==0 || Object.Equals(InputList[i],lastItem) != true)
            {
                lastItem = InputList[i];
                result.Add((T)lastItem);
            }
        }

        return result;
    }

4 votes

Pourquoi est-il nécessaire d'être capable de trier les articles ? Il suffit de pouvoir les comparer pour qu'ils soient égaux.

0 votes

@Eric : désolé, je ne comprends pas bien pourquoi vous demandez le tri. J'ai spécifié IComparable inteface seulement pour pouvoir utiliser CompareTo Méthode. Je vous ai bien compris ?

5 votes

Bon, pourquoi avez-vous besoin de CompareTo ? CompareTo sert à trier ; c'est pour déterminer quel élément est plus grand qu'un autre. Pourquoi cet algorithme devrait-il être limité aux seuls types qui définissent un ordre de tri ? Cet algorithme ne nécessite que la vérification de égalité et vous pouvez le faire avec la méthode pratique "Equals" qui se trouve sur chaque objet.

5voto

Stephen Chung Points 9467

Vous pouvez le faire en LINQ :

list.Aggregate(new List<string>(), 
   (current, next) => {
      if (current.Length <= 0 || current[current.Length-1] != next) current.Add(next);
      return current;
   });

Essentiellement, cela crée une liste initialement vide, parcourt toute la liste source et n'ajoute un élément à la liste cible que s'il n'est pas identique au dernier élément de la liste cible.

Vous pouvez tout aussi bien (probablement plus facilement) le faire sans LINQ :

var target = new List<string>();
foreach (var item in list) {
   if (target.Length <= 0 || target[target.Length-1] != item) target.Add(item);
}

1voto

wegginho Points 913

Résolution :

IList<string> stringList = new List<string>() { "red", "red", 
                                                "blue", "green", 
                                                "green", "red", 
                                                "red", "yellow", 
                                                "white", "white", 
                                                "red", "white", "white" };      
  for (int i = 0; i < stringList.Count; i++)
  {
    // select the first element
    string first = stringList[i];

    // select the next element if it exists
    if ((i + 1) == stringList.Count) break;
    string second = stringList[(i + 1)];

    // remove the second one if they're equal
    if (first.Equals(second))
    {
      stringList.RemoveAt((i + 1));
      i--;
    }
  }

Corrigez-moi dans les commentaires si quelque chose ne va pas, s'il vous plaît !

/e : J'ai modifié le code pour qu'il fonctionne sur "blanc", "blanc", "blanc", "blanc".

0 votes

La suppression d'éléments dans votre boucle for va perturber les indices.

0 votes

@Steven Jeuris pouvez-vous me dire pourquoi vous avez rétrogradé un code qui fonctionne ??? Au lieu de dire des bêtises à partir de rien, tu devrais l'essayer et le voir fonctionner, mec.

0 votes

En outre, essayez la séquence suivante "blanc", "blanc", "blanc", "blanc" . Maintenant j'ai rétrogradé. ;p

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X