43

Is there a "nice" way to eliminate consecutive duplicates of list elements?

Example:

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

should become

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

2020欧洲杯手机版注册--By "nice" I mean most readable and understandable to a new user and fast execution :)

63

A simple and very readable solution:

List<string> results = new List<string>();foreach (var element in array){    if(results.Count == 0 || results.Last() != element)        results.Add(element);}
  • 2
    I ask this question on interviews and no one has had this elegant of a solution. Have an upvote. – AngryHacker Apr 21 '11 at 22:59
  • This is extremely inefficient. Last enumerates all the elements, and you are calling it on every iteration. – Theodor Zoulias Apr 5 '19 at 11:32
  • @TheodorZoulias It is efficient with a List<T>. Specifically, Last() is fast . And . – Smi Aug 11 '19 at 13:16
19

2020欧洲杯手机版注册You can roll your own, linq-style.

// 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;      }   }}

And use as

array.NonConsecutive().ToArray()

The advantage is that it's lazily evaluated, so you can use it on any enumeration without having to consume it in its entirety, and chain it with other linq methods (eg: array.Where(i => i != "red").NonConsecutive().Skip(1).ToArray()). If you don't have that requirement and you just want to work with arrays, something like Simon Bartlett's solution might be slightly more performant.

For more information on why it has to be two methods, see

  • General idea seems right, but what if the first item is null? – Steven Jeuris Apr 20 '11 at 12:04
  • 2
    Noted, added a flag to address that. – Alex J Apr 20 '11 at 12:07
  • 1
    Nice solution. Since this is very similar to the Distinct method, I might consider naming it accordingly, i.e. ConsecutiveDistict. – Scott Wegner Apr 24 '11 at 17:56
8

2020欧洲杯手机版注册You can create simple generic method for this purpose, like below:

[EDIT 2]2020欧洲杯手机版注册 (great thanks to 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
    Why is it necessary to be able to sort the items? All you need is to be able to compare them for equality. – Eric Lippert Apr 20 '11 at 14:47
  • @Eric: sorry, cant clearly get why you are asking about sorting. I specified IComparable inteface only to able use CompareTo method. did I understand you correctly? – Anton Semenov Apr 20 '11 at 14:52
  • 5
    Right; why do you need CompareTo? CompareTo is for sorting; it is for determining which element is bigger than another. Why should this algorithm be limited to only types that define a sort order? This algorithm only requires checking for equality and you can do that with the convenient "Equals" method that is on every object. – Eric Lippert Apr 20 '11 at 14:57
  • 2
    OK, but we're not done yet. What if its a list of strings and the first string in the list is null? What happens? – Eric Lippert Apr 20 '11 at 15:21
  • 3
    Better! However it is not necessary for you to write IsObjectEquals, since we implement that for you. You can just call the static method System.Object.Equals and it will do the null check for you. – Eric Lippert Apr 20 '11 at 19:19
5

You can do it in LINQ:

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

Essentially, this creates an initially-empty list, runs through the entire source list, and only add an item to the target list if it is not the same as the last item of the target list.

2020欧洲杯手机版注册You can just as easily (probably easier) do it without LINQ:

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

Try this:

using System;    using System.Linq;    using System.Collections.Generic;namespace RemoveDuplicates{    class MainClass    {        public static void Main (string[] args)        {            string[] a = new string[]             { "red", "red", "red", "blue",                       "green", "green", "red", "red",                       "yellow", "white", "white", "red", "white", "white" };            for(int i = 0; i < a.Length; ++i)                if (i == a.Length-1 || a[i] != a[i+1])                    Console.WriteLine(a[i]);        }    }}

Output:

redbluegreenredyellowwhiteredwhite
1

Resolution:

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

correct me in the comments if something is wrong please!

/e: Edited code so it works on "white","white","white","white"

  • Removing items inside your for loop will mess up the indices. – Steven Jeuris Apr 20 '11 at 12:05
  • @Steven Jeuris can you tell me why you downvoted working code??? Instead of talking nonsense out of nothing you should try it and see it work dude. – bastianwegge Apr 20 '11 at 13:08
  • Furthermore, try the following sequence "white", "white", "white", "white". Now I downvoted. ;p – Steven Jeuris Apr 20 '11 at 13:32
  • look again and correct your vote please, or give me something to work on :D – bastianwegge Apr 20 '11 at 13:37
  • @wegginho: K removed the downvote. ;p But that's the kind of indexer magic I was referring to, it isn't quite readable and I find the upvoted answers better. – Steven Jeuris Apr 20 '11 at 13:41
1

I think this is the simplest it can get with Linq:

colors.Where((color, i) => i == 0 || color != colors[i - 1]);

You can try it in C# Interactive:

> var colors = new[] { "red", "red", "blue", "green", "green", "red", "red", "yellow", "white", "white", "red", "white", "white" };> colors.Where((color, i) => i == 0 || color != colors[i - 1])WhereIterator { "red", "blue", "green", "red", "yellow", "white", "red", "white" }

2020欧洲杯手机版注册The trick here is to use the Where() overload that accepts a predicate with index, then compare to the previous item in the original array.

0

Functional approach:

var input = new[] {"red", "red", "blue",                    "green", "green", "red", "red", "yellow",                   "white", "white", "red", "white", "white"};var output = input.Aggregate(new List<string>(),                             (runningOutput, value) =>                             (runningOutput.LastOrDefault() == value                                      ? runningOutput                                      : runningOutput.Append(value)));

Presupposes the existence of an extension method similar to:

static class Ex{    public static List<T> Append<T>(this List<T> source, T value)    {        return new List<T>(source) { value };    }}

Supply your own validation as you feel is necessary.

0

2020欧洲杯手机版注册Like this you don't need a new object.

public static void RemoveConsecutiveDuplicates<T>(this List<T> collection){    for (int i = 0; i < collection.Count - 1; i++)    {        if (collection[i].Equals(collection[i + 1]))        {            collection.RemoveAt(i);            i--;        }    }}var collection = new [] { 2, 7, 7, 7, 2, 6, 4 }.ToList();collection.RemoveConsecutiveDuplicates();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy2020欧洲杯手机版注册

Not the answer you're looking for? Browse other questions tagged or ask your own question.