【问题标题】:Find continuous duplicates in a List查找列表中的连续重复项
【发布时间】:2016-08-24 10:52:07
【问题描述】:

有很多方法可以找到列表中的重复项,有没有办法在列表中找到连续的重复项。

例如

List<string> stringList = new List<string>();
stringList.Add("Name1");
stringList.Add("Name2");
stringList.Add("Name1");

除了

应该找不到
stringList.Add("Name1");
stringList.Add("Name1");
stringList.Add("Name2"); 

应该返回 1 个条目

这会返回重复项。

 var q = listString.GroupBy(x => x)
         .Select(g => new { Value = g.Key, Count = g.Count() })
         .OrderByDescending(x => x.Count);

【问题讨论】:

  • 你想要什么样的结果?真/假结果,重​​复部分的列表?
  • 显示您在尝试解决此问题时编写的代码,以便我们查看您出错的地方。不要忘记告诉我们为什么它不起作用。并调试您的代码。
  • @LasseV.Karlsen,如果我们能得到哪个项目是重复的,起始索引和出现次数。

标签: c# list duplicates


【解决方案1】:

这是一种返回重复项及其索引的方法:

var duplicates =
    stringList
    .Select((x,i) => new {Item = x, Index = i})
    .Skip(1) //We start with the second item
    .Where(y => y.Item == stringList[y.Index-1])
    .ToList();

【讨论】:

  • 感谢支持,但是我将 Dmitry 的答案标记为已接受,因为我首先尝试了他的方法并且它奏效了。
  • @Pankaj 我认为您应该标记最佳答案,而不是第一个。在工作时,Dmitry 代码与 .Skip(1).Where((y, i) => y == stringList[i]) 基本相同
【解决方案2】:

既然您问“我们是否可以获取哪个项目是重复的以及起始索引和出现次数”,那么这里有一个针对该特定要求的解决方案。

这会输出以下内容:

2 was repeated 2 times starting at index 1
3 was repeated 3 times starting at index 4
4 was repeated 4 times starting at index 8

代码如下:

using System;
using System.Collections.Generic;

namespace Demo
{
    class DupeInfo
    {
        public string Text;
        public int Index;
        public int Count;
    }

    static class Program
    {
        static void Main()
        {
            var test = new[]
            {
                "1",
                "2", "2",
                "A",
                "3", "3", "3",
                "B",
                "4", "4", "4", "4",
                "C",
            };

            foreach (var dupeinfo in FindRepeats(test))
                Console.WriteLine($"{dupeinfo.Text} was repeated {dupeinfo.Count} times starting at index {dupeinfo.Index}");
        }

        public static IEnumerable<DupeInfo> FindRepeats(IEnumerable<string> input)
        {
            int i = 0;
            int j = 0;
            int c = -1;

            string prev = null;

            foreach (var curr in input)
            {
                if (curr != prev)
                {
                    if (c >= 0)
                        yield return new DupeInfo {Text = prev, Count = c + 2, Index = j};

                    c = -1;
                    j = i;
                }
                else
                {
                    ++c;
                }

                prev = curr;
                ++i;
            }

            if (c >= 0)
                yield return new DupeInfo {Text = prev, Count = c + 2, Index = j};
        }
    }
}

【讨论】:

    【解决方案3】:

    为什么不只存储最后一项?像这样的

    public static partial class EnumerableExtensions { 
      // Simplest; IEquatable<T> for advanced version
      public static IEnumerable<T> Continuous<T>(this IEnumerable<T> source) {
        if (null == source)
          throw new ArgumentNullException("source");  
    
        T lastItem = default(T);
        Boolean first = true;
    
        foreach (var item in source) {
          if (first) {
            lastItem = item;
            first = false;
          }
          else if (Object.Equals(item, lastItem)) 
            yield return item;
          else
            lastItem = item; 
        }
      }  
    }
    

    然后

    List<string> stringList = new List<string>() {
      "Name1",
      "Name1",
      "Name2",
    };
    
    var contDups = stringList
      .Continuous() 
      .ToList();
    

    【讨论】:

    • 此逻辑有效,但此行 (item == lastItem) 上的编译器错误除外,因为运算符“==”不能应用于“T”和“T”类型的操作数。我将其更改为 else if (item.Equals(lastItem)) ,它就像一个魅力。
    • @Pankaj:我明白了,应该是Object.Equals(item, lastItem) - 更安全的版本(如果itemlastItem 中的任何一个是null)。查看我的编辑。
    【解决方案4】:

    您可以从定义一个项目成为“连续重复”的含义开始:

    如果位置i 的项目与位置i-1 的项目相同,则它是连续重复项

    将一个值与在前位置的另一个值进行比较的一种方法是将Zip 与一个“移位”一个元素的列表一起使用:

    var consecutiveDuplicates = list.Skip(1)
        .Zip(list, (me, prior) => new {ThisItem = me, Prior = prior})
        .Where(p => p.ThisItem == p.Prior)
        .Select(p => p.ThisItem) // Both sides are equal, pick either one
        .ToList();
    

    list.Skip(1).Zip(list,...) 表达式将列表与自身组合在一起,移位为 1,因此您可以在定义了前一个元素的 N-1 个位置获取此元素及其前一个元素。剩下的就是将英文定义直接翻译成 LINQ 语句

    【讨论】:

    • 感谢支持,但是我将 Dmitry 的答案标记为已接受,因为我首先尝试了他的方法并且它奏效了。
    猜你喜欢
    • 2013-04-25
    • 1970-01-01
    • 2020-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-12
    • 1970-01-01
    相关资源
    最近更新 更多