Mykhalik
Mykhalik

Reputation: 244

How can I retrieve a set of unique arrays from a list of arrays using LINQ?

I have such construction

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

Can I get all unique value from propIDs with LINQ.For example I have list of (1,2) (4,5) (1,5) (1,2) (1,5) and I must get (1,2) (4,5) (1,5)

Upvotes: 5

Views: 901

Answers (6)

ehfaafzv
ehfaafzv

Reputation: 191

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());

the output is 5

Upvotes: 0

Martin Liversage
Martin Liversage

Reputation: 106826

Assuming that you can't use Tuple<T1, T2> which already provides equality you can instead create your own IEqualityComparer<T> that defines equality for arrays by simply requiring that all elements are equal sequentially:

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());
  }

}

Then you can easily get the distinct values:

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

Upvotes: 1

Dmitrii Dovgopolyi
Dmitrii Dovgopolyi

Reputation: 6301

This code works for any length arrays.

    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;
        }
    }

and the code for distinct:

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

Upvotes: 1

Paul Ruane
Paul Ruane

Reputation: 38590

You can use the overload of Enumerable.Distinct that takes an equality comparer.

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());

If you want collections larger than pairs:

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();
        }
    }
}

If all of your int arrays are two elements long, you could also use Tuples instead which will allow you to use the Distinct without a custom equality comparer:

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

Upvotes: 3

Mike Perrenoud
Mike Perrenoud

Reputation: 67898

Below is a complete and working application of your need.

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;
            }
        }

    }
}

Upvotes: 1

Reza ArabQaeni
Reza ArabQaeni

Reputation: 4907

HashSet, a set is a collection that contains no duplicate elements:

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

Upvotes: -1

Related Questions