【问题标题】:How can I retrieve a set of unique arrays from a list of arrays using LINQ?如何使用 LINQ 从数组列表中检索一组唯一数组?
【发布时间】:2013-02-06 17:16:47
【问题描述】:

我有这样的结构

List<int[]> propIDs = new List<int[]>();

我可以使用 LINQ 从 propID 中获取所有唯一值吗?例如,我有以下列表 (1,2) (4,5) (1,5) (1,2) (1,5) 我必须得到 (1,2) (4,5) (1,5)

【问题讨论】:

  • 你的int[]s 的两个元素都长吗?
  • 订单重要吗? (1,2)(2,1) 一样吗?
  • 你可以使用 Point() 代替 int[]
  • 如果所有元素的大小都相同,我会考虑Tuple&lt;int,int&gt; 而不是int[]
  • @Mykhalik (1,2) 和 (2,1) 具有相同的长度:2。因此使用 Tuple&lt;int,int&gt; 将适用于这种情况。

标签: c# arrays linq list


【解决方案1】:

您可以使用带有相等比较器的Enumerable.Distinct 的重载。

class IntPairArrayComparer : IEqualityComparer<int[]>
{
    public bool Equals(int[] left, int[] right)
    {
        if (left.Length != 2) throw new ArgumentOutOfRangeException("left");
        if (right.Length != 2) throw new ArgumentOutOfRangeException("right");

        return left[0] == right[0] && left[1] == right[1];
    }

    public int GetHashCode(int[] arr)
    {
        unchecked
        {
            return (arr[0].GetHashCode() * 397) ^ arr[1].GetHashCode();
        }
    }
}

IEnumerable<int[]> distinctPairs = propIDs.Distinct(new IntPairArrayComparer());

如果您想要大于对的集合:

class IntArrayComparer : IEqualityComparer<int[]>
{
    public bool Equals(int[] left, int[] right)
    {
        if (left.Length != right.Length) return false;

        return left.SequenceEquals(right);
    }

    public int GetHashCode(int[] arr)
    {
        unchecked
        {
            int hc = 1;

            foreach (int val in arr) hc = hc * 397 ^ val.GetHashCode();
        }
    }
}

如果您的所有 int 数组都是两个元素长,您也可以使用 Tuples 代替,这样您就可以在没有自定义相等比较器的情况下使用 Distinct

IEnumerable<Tuple<int, int>> propIDs = [] { Tuple.Create(1,2), … };
IEnumerable<Tuple<int, int>> distinctPairs = propIDs.Distinct();

【讨论】:

  • 如果数组有不同的长度怎么办?
  • 我很确定这甚至不会编译。还有一个技巧可以让GetHashCode() 方法工作。您需要从它静态返回一个-1,以便它甚至可以运行Equals 方法。
  • @MichaelPerrenoud:添加了GetHashCode 实现。
  • @daryal:问题说明数组是对的。显然,以这种方式使用数组时应该小心。
  • @daryal:为任意长度数组添加了一个示例比较器。
【解决方案2】:

HashSet,集合是不包含重复元素的集合:

var propIDs = HashSet<Tuple<int,int>>

【讨论】:

    【解决方案3】:

    以下是您需要的完整且有效的应用程序。

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ListsAndArrays
    {
        class Program
        {
            static void Main(string[] args)
            {
                List<int[]> propIDs = new List<int[]>();
                propIDs.Add(new[] { 1, 2 });
                propIDs.Add(new[] { 4, 5 });
                propIDs.Add(new[] { 1, 5 });
                propIDs.Add(new[] { 1, 2 });
                propIDs.Add(new[] { 1, 5 });
    
                var distinct = propIDs.Distinct(new DistinctIntegerArrayComparer());
    
                foreach (var item in distinct)
                {
                    Console.WriteLine("{0}|{1}", item[0], item[1]);
                }
    
                if (Debugger.IsAttached)
                {
                    Console.ReadLine();
                }
            }
    
            private class DistinctIntegerArrayComparer : IEqualityComparer<int[]>
            {
                public bool Equals(int[] x, int[] y)
                {
                    if (x.Length != y.Length) { return false; }
                    else if (x.Length != 2 || y.Length != 2) { return false; }
    
                    return x[0] == y[0] && x[1] == y[1];
                }
    
                public int GetHashCode(int[] obj)
                {
                    return -1;
                }
            }
    
        }
    }
    

    【讨论】:

    • 虽然这可行,但返回一个常量(此处为-1)哈希码是最差可能(仍然正确)的实现。
    • @JeppeStigNielsen,我能理解您的意思,感谢您的反馈!构建一个在数组相同时实际上相同的哈希码可能是一种更好的方法 - 特别是在处理非常大的数组集时。但是,正如您从其他帖子中看到的那样,构建哈希码并不是直截了当的,而且意见明显不同。使用一小组数组,这里没有任何真正的性能影响。
    【解决方案4】:

    此代码适用于任何长度的数组。

        class MyEqualityComparer : IEqualityComparer<int[]>
        {
            public bool Equals(int[] item1, int[] item2)
            {
                if (item1 == null && item2 == null)
                    return true;
                if ((item1 != null && item2 == null) ||
                        (item1 == null && item2 != null))
                    return false;
                return item1.SequenceEqual(item2);
            }
    
            public int GetHashCode(int[] item)
            {
                if(item == null)
                {
                    return int.MinValue;
                }
                int hc = item.Length;
                for (int i = 0; i < item.Length; ++i)
                {
                    hc = unchecked(hc * 314159 + item[i]);
                }
                return hc;
            }
        }
    

    以及不同的代码:

    var result = propIDs.Distinct(new MyEqualityComparer());
    

    【讨论】:

      【解决方案5】:

      假设您不能使用已经提供相等性的Tuple&lt;T1, T2&gt;,您可以改为创建自己的IEqualityComparer&lt;T&gt;,通过简单地要求所有元素顺序相等来定义数组的相等性:

      class ArrayEqualityComparer<T> : IEqualityComparer<T[]> {
      
        public Boolean Equals(T[] x, T[] y) {
          if (x.Length != y.Length)
            return false;
          return x.Zip(y, (xx, yy) => Equals(xx, yy)).All(equal => equal);
        }
      
        public Int32 GetHashCode(T[] obj) {
          return obj.Aggregate(0, (hash, value) => 31*hash + value.GetHashCode());
        }
      
      }
      

      然后你可以很容易地得到不同的值:

      var distinctPropIDs  = propIDs.Distinct(new ArrayEqualityComparer<Int32>());
      

      【讨论】:

        【解决方案6】:
        public return List<Tuple<double, double>> uniquePairs(List<double[]> lst)
        {
        HashSet<Tuple<double, double>> hash = new HashSet<Tuple<double, double>>();
        for (int i = 0; i < lst.count; i++)
        {
        hash.Add(new Tuple<double, double>(lst[i][0], lst[i][1]))
        }
        List<Tuple<double, double>> lstt = hash.Distinct().ToList();
        }
        
        For example:
        List<double[]> lst = new List<double[]> {new double[] { 1, 2 }, new double[] { 2, 3 }, new double[] { 3, 4 }, new double[] { 1, 4 }, new double[] { 3, 4 }, new double[] { 2, 1 }}; //  this list has 4 unique numbers, 5 unique pairs, the desired output would be the 5 unique pairs (count = 5)
        List<Tuple<double, double>> lstt = uniquePairs(lst);
        Console.WriteLine(lstt.Count().ToString());
        

        输出为 5

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-11-12
          • 1970-01-01
          • 1970-01-01
          • 2022-11-10
          • 1970-01-01
          相关资源
          最近更新 更多