Is there a \"nice\" way to eliminate consecutive duplicates of list elements?
Example:
[\"red\"; \"red\"; \"blue\"; \"green
You can create simple generic method for this purpose, like below:
[EDIT 2] (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;
}
Here is my suggestion. It is similar to @AlexJ's answer, with the addition of an IEqualityComparer
parameter that allows to customize the equality check. I also removed the otherwise correct separation of argument-check and implementation, because this solution is not intended to be of Library-grade quality. As for the name I adopted the ExcludeConsecutiveDuplicates
from @AntonSemenov's answer.
public static IEnumerable<TSource> ExcludeConsecutiveDuplicates<TSource>(
this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer = null)
{
if (source == null) throw new ArgumentNullException(nameof(source));
comparer = comparer ?? EqualityComparer<TSource>.Default;
bool isFirst = true;
TSource last = default;
foreach (var item in source)
{
if (isFirst || !comparer.Equals(item, last)) yield return item;
isFirst = false;
last = item;
}
}
Usage example:
var source = new string[]
{
"Red", "red", "blue", "green", "green", "red", "red", "yellow",
"WHITE", "white", "red", "white", "white"
};
var result = source.ExcludeConsecutiveDuplicates(StringComparer.OrdinalIgnoreCase);
Console.WriteLine($"Result: {String.Join(", ", result)}");
Output:
Result: Red, blue, green, red, yellow, WHITE, red, white
The advantage of this solution compared to the accepted answer is that it doesn't require a source of type IList<T>
to be efficient.
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"
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.
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:
red
blue
green
red
yellow
white
red
white
Taking @Simon Bartlett's clean approach and improving upon it, you could also perform this generically.
public static IEnumerable<T> UniqueInOrder<T>(IEnumerable<T> iterable)
{
var returnList = new List<T>();
foreach (var item in iterable)
{
if (returnList.Count == 0 || !returnList.Last().Equals(item))
returnList.Add(item);
}
return returnList;
}