【问题标题】:Removing duplicates from a list of tuples从元组列表中删除重复项
【发布时间】:2015-06-12 17:56:59
【问题描述】:

我有一个 Tuple<string,string> 对象列表,我想删除重复项,例如,元组 (a,b)(b,a) 被认为是相同的(这些是图的边缘)。这样做的不错方法是什么?

【问题讨论】:

  • 不使用 EqualityComparer 工作,如这篇 SO 文章中所示 stackoverflow.com/questions/17275727/…
  • 您是否需要保留元组的顺序,例如你能把它们全部排序,以便第一个
  • 不,排序无关紧要
  • 解决办法差不多就是做一个反转元组的列表,用Except。但问题是,如果您的原始列表中有一个元组 ("a","b")("b","a")
  • 如何在 Python 中做同样的事情?谢谢

标签: c# linq


【解决方案1】:

您需要创建一个可以比较元组的比较器,以使项目的顺序无关紧要:

public class UnorderedTupleComparer<T> : IEqualityComparer<Tuple<T, T>>
{
    private IEqualityComparer<T> comparer;
    public UnorderedTupleComparer(IEqualityComparer<T> comparer = null)
    {
        this.comparer = comparer ?? EqualityComparer<T>.Default;
    }

    public bool Equals(Tuple<T, T> x, Tuple<T, T> y)
    {
        return comparer.Equals(x.Item1, y.Item1) && comparer.Equals(x.Item2, y.Item2) ||
                comparer.Equals(x.Item1, y.Item2) && comparer.Equals(x.Item1, y.Item2);
    }

    public int GetHashCode(Tuple<T, T> obj)
    {
        return comparer.GetHashCode(obj.Item1) ^ comparer.GetHashCode(obj.Item2);
    }
}

请注意,哈希码的异或运算是一种无论操作数的顺序如何都会产生相同结果的操作,这使得它在这里是可取的(但不是在大多数哈希码生成算法中,因为它通常是一个 不受欢迎的属性)。至于Equals,只需检查两个可能的配对即可。

一旦你有了,你就可以做到:

var query = data.Distinct(new UnorderedTupleComparer<string>());

【讨论】:

  • +1 我已经创建了测试程序,您的算法要快得多。在我的情况下,清除 100000 个随机生成的元组列表的结果是 3825,1412 毫秒,而在你的情况下,对于相同的复制列表,只有 59,5329 毫秒。删除了我的答案。
【解决方案2】:

您可能需要创建一个实现IEqualityComparer&lt;Tuple&lt;string, string&gt;&gt; 的类:

public class TupleComparer : IEqualityComparer<Tuple<string, string>>
{
    public bool Equals(Tuple<string, string> x, Tuple<string, string> y)
    {

        if (ReferenceEquals(x, y))
        {
            return true;
        }

        if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
        {
            return false;
        }

        if (x.Item1.Equals(y.Item2) && x.Item2.Equals(y.Item1))
        {
            return true;
        }

        return x.Item1.Equals(y.Item1) && x.Item2.Equals(y.Item2);
    }   

    public int GetHashCode(Tuple<string, string> tuple)
    {
        // implementation
    }
}

然后您可以像这样使用Distinct() LINQ 方法:

List<Tuple<string, string>> list = new List<Tuple<string, string>> { Tuple.Create("a", "b"), Tuple.Create("a", "c"), Tuple.Create("b", "a") };
var result = list.Distinct(new TupleComparer());

【讨论】:

    【解决方案3】:

    尝试使用字典并组成一个表示每个元组的键。你有一个不会出现在你的字符串中的字符,你可以用作分隔符吗?我在这个例子中选择了“:”:

    static void Main(string[] args)
    {
        // original list of data
        var list = new List<Tuple<string, string>> { };
        list.Add(new Tuple<string, string>("a", "b"));
        list.Add(new Tuple<string, string>("b", "a"));
    
        // dictionary to hold unique tuples
        var dict = new Dictionary<string, Tuple<string, string>>();
        foreach (var item in list)
        {
            var key1 = string.Concat(item.Item1, ":", item.Item2);
            var key2 = string.Concat(item.Item2, ":", item.Item1);
    
            // if dict doesnt contain tuple, add it.
            if (!dict.ContainsKey(key1) && !dict.ContainsKey(key2))
                dict.Add(key1, item);
        }
    
        // print unique tuples
        foreach (var item in dict)
        {
            var tuple = item.Value;
            Console.WriteLine(string.Concat(tuple.Item1, ":", tuple.Item2));
        }
    
        Console.ReadKey();
    }
    

    【讨论】:

      【解决方案4】:

      直播代码:https://dotnetfiddle.net/LUErFj

      首先对 Tuple 对进行排序,然后进行 Distinct:

      using System;
      using System.Collections.Generic;
      
      using System.Linq;
      
      public class Program
      {
          static List<Tuple<string, string>> myList = new List<Tuple<string, string>>()
          {
              Tuple.Create<string, string>("A", "B"),
              Tuple.Create<string, string>("B", "A"), // duplicate
              Tuple.Create<string, string>("C", "B"),
              Tuple.Create<string, string>("C", "B"), // duplicate
              Tuple.Create<string, string>("A", "D")              
          };
      
          public static void Main()
          {
              myList
                  .Select(x => new[] { x.Item1, x.Item2 }.OrderBy(s => s).ToArray())
                  .Select(x => Tuple.Create<string,string>(x[0], x[1]))
                  .Distinct()
                  .Dump();
          }
      }
      

      输出:

      Dumping object(System.Linq.<DistinctIterator>d__81`1[Tuple`2[String,String]])
      [
         {
         Item1  : A
         Item2  : B
         ToString(): (A, B)
         },
         {
         Item1  : B
         Item2  : C
         ToString(): (B, C)
         },
         {
         Item1  : A
         Item2  : D
         ToString(): (A, D)
         }
      ]
      

      【讨论】:

      • 请注意,这可能会导致返回的元组不在原始序列中;如果输入是{"b","a"},那么它将在输出中返回{"a","b"} 而不是{"b","a"}
      • @Servy 是的,但这就是我理解问题的方式。如果它需要保留原始元素,我将返回 dotnetfiddle.net 并制作第二个版本
      • 他希望将具有相反顺序的值的对视为相等,但这并不一定意味着可以返回不在原始集合中的项目。可能没问题,但可能不是,它没有定义。
      • 我喜欢这种方法,因为它很简单。但是实现 IEqualityComparer 感觉像是更“干净”的解决方案。
      【解决方案5】:

      要保留原件,请使用group by 而不是Distinct,这样我们仍然可以访问组的第一个元素:

      直播代码:https://dotnetfiddle.net/LYZItb

      using System;
      using System.Collections.Generic;
      
      using System.Linq;
      
      public class Program
      {
          static List<Tuple<string, string>> myList = new List<Tuple<string, string>>()
          {
              Tuple.Create<string, string>("B", "A"),
              Tuple.Create<string, string>("A", "B"), // duplicate
              Tuple.Create<string, string>("C", "B"),
              Tuple.Create<string, string>("C", "B"), // duplicate
              Tuple.Create<string, string>("A", "D"),
              Tuple.Create<string, string>("E", "F"),
              Tuple.Create<string, string>("F", "E"), // duplicate        
          };
      
          public static void Main()
          {
      
              var result =
                  from y in 
                      from x in myList
                      select new { Original = x, SortedPair = new[] { x.Item1, x.Item2 }.OrderBy(s => s).ToArray() }  
                      group y by new { NormalizedTuple = Tuple.Create<string,string>(y.SortedPair[0], y.SortedPair[1]) } into grp
                  select new { Pair = grp.Key.NormalizedTuple, Original = grp.First().Original };
      
      
              foreach(var item in result)
              {
                  Console.WriteLine("Pair: {0} {1}", item.Original.Item1, item.Original.Item2);
              }
          }
      }
      

      输出:

      Pair: B A
      Pair: C B
      Pair: A D
      Pair: E F
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-12
        • 2018-10-17
        • 1970-01-01
        • 2018-12-25
        • 2022-01-20
        相关资源
        最近更新 更多