消除列表元素的连续重复

Wol*_*lfy 43 c# list

是否有一种"好的"方法来消除列表元素的连续重复

例:

["red"; "red"; "blue"; "green"; "green"; "red"; "red"; "yellow"; "white"; "white"; "red"; "white"; "white"] 
Run Code Online (Sandbox Code Playgroud)

应该成为

["red"; "blue"; "green"; "red"; "yellow"; "white"; "red"; "white"]
Run Code Online (Sandbox Code Playgroud)

- "好"我的意思是对新用户和快速执行最具可读性和可理解性:)

Sim*_*ett 62

一个简单且易读的解决方案:

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

  • @TheodorZoulias 使用 `List&lt;T&gt;` 非常有效。具体来说,“Last()”速度很快[如果序列实现“IList&lt;T&gt;”](https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,75722fd194dc2e0e)。[`List&lt;T&gt;` 确实如此](https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,cf7f4095e4de7646)。 (3认同)
  • 我在采访中问这个问题,没有人有这个优雅的解决方案.有一个upvote. (2认同)

Ale*_*x J 19

你可以自己动手,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;
      }
   }
}
Run Code Online (Sandbox Code Playgroud)

并用作

array.NonConsecutive().ToArray()
Run Code Online (Sandbox Code Playgroud)

优点是它被懒惰地评估,因此您可以在任何枚举上使用它而不必完全使用它,并将其与其他linq方法(例如:)链接array.Where(i => i != "red").NonConsecutive().Skip(1).ToArray().如果你没有这个要求而你只想使用数组,那么Simon Bartlett的解决方案可能会稍微提高一些性能.

有关为什么必须有两种方法的更多信息,请参见此处

  • 这将工作提供`input.First()!= default(T)` (2认同)
  • 注意到,添加了一个标志来解决这个问题. (2认同)

Ant*_*nov 8

您可以为此目的创建简单的通用方法,如下所示:

[编辑2](非常感谢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;
    }
Run Code Online (Sandbox Code Playgroud)

  • 为什么必须能够*排序*项目?您所需要的只是能够比较它们的平等性. (4认同)
  • 对; 为什么你需要CompareTo?CompareTo用于*排序*; 它用于确定哪个元素*比另一个元素大*.为什么此算法仅限于定义排序顺序的类型?此算法仅需要检查*equality*,您可以使用每个对象上方便的"Equals"方法来执行此操作. (4认同)
  • 更好!但是,您没有必要编写IsObjectEquals,因为我们为您实现了这一点.您可以只调用静态方法System.Object.Equals,它将为您执行null检查. (3认同)
  • 好的,但我们还没有完成.如果它的字符串列表和列表中的第一个字符串为空怎么办?怎么了? (2认同)

asc*_*eck 6

我认为这是使用 Linq 可以获得的最简单的结果:

colors.Where((color, i) => i == 0 || color != colors[i - 1]);
Run Code Online (Sandbox Code Playgroud)

您可以在 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" }
Run Code Online (Sandbox Code Playgroud)

这里的技巧是使用Where()重载来接受带有索引的谓词,然后与原始数组中的前一项进行比较。


Ste*_*ung 5

您可以在LINQ中执行此操作:

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

实质上,这会创建一个初始为空的列表,在整个源列表中运行,并且只有在与目标列表的最后一项不同时才将项添加到目标列表.

没有LINQ,你可以轻松(可能更容易)做到这一点:

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