2016-06-16 3 views
12

имеющих список Int массивов как:Удалить дубликаты в списке ИНТ массивов

List<int[]> intArrList = new List<int[]>(); 
intArrList.Add(new int[3] { 0, 0, 0 }); 
intArrList.Add(new int[5] { 20, 30, 10, 4, 6 }); //this 
intArrList.Add(new int[3] { 1, 2, 5 }); 
intArrList.Add(new int[5] { 20, 30, 10, 4, 6 }); //this 
intArrList.Add(new int[3] { 12, 22, 54 }); 
intArrList.Add(new int[5] { 1, 2, 6, 7, 8 }); 
intArrList.Add(new int[4] { 0, 0, 0, 0 }); 

Как удалить дубликаты (с помощью дубликата я имею в виду элемент списка имеет такую ​​же длину и те же номера).

На примере я хотел бы удалить элемент { 20, 30, 10, 4, 6 }, потому что он находится в два раза

Я думал о сортировке списка по размеру элемента, затем цикл каждый элемент от остальных, но я не уверен, как это сделать.

Другой вопрос будет, если использовать другую структуру, такую ​​как Хэш, было бы лучше ... Если да, то как ее использовать?

+1

имеет ли значение, если они находятся в одном порядке, и если числа совпадают, но один список повторяет одно из чисел? –

+0

ну, на самом деле Это имеет значение, если они имеют одинаковую длину, например '{1,2,3,4} равно {2,3,4,1}' – cMinor

+2

Будьте осторожны! Надежный и эффективный ответ на этот вопрос является нетривиальным для реализации, в то время как маловероятное и/или медленное решение этой проблемы тривиально для реализации – Mick

ответ

13

Применение GroupBy:

var result = intArrList.GroupBy(c => String.Join(",", c)) 
         .Select(c => c.First().ToList()).ToList(); 

Результат:

{0, 0, 0}

{20, 30, 10, 4, 6}

{ 1, 2, 5}

{12, 22, 54}

{1, 2, 6, 7, 8}

{0, 0, 0, 0}

EDIT: Если вы хотите, чтобы рассмотреть {1,2,3,4} равным {2,3,4,1} вам нужно использовать OrderBy так:

var result = intArrList.GroupBy(p => string.Join(", ", p.OrderBy(c => c))) 
         .Select(c => c.First().ToList()).ToList(); 

edit2: для того, чтобы помочь понять, как LINQ GroupBy решение работает рассмотрим следующий метод:

public List<int[]> FindDistinctWithoutLinq(List<int[]> lst) 
{ 
    var dic = new Dictionary<string, int[]>(); 
    foreach (var item in lst) 
    { 
     string key = string.Join(",", item.OrderBy(c=>c)); 

     if (!dic.ContainsKey(key)) 
     { 
      dic.Add(key, item); 
     } 
    } 

    return dic.Values.ToList(); 
} 
+1

Также вы можете реализовать класс 'EqualityComparer' и использовать его в методе LINK. Но я думаю, что было бы проще с помощью 'GroupBy' для этой цели –

+0

будет' GropuBy' считать '{1,2,3,4}' равным '{2,3,4,1}'? – cMinor

+1

Вы можете использовать заказ на c, прежде чем группировать его, таким образом он будет равен – Jannik

7

Вы можете определить свою собственную реализацию IEqualityComparer и использовать его вместе с IEnumerable.Distinct:

class MyComparer : IEqualityComparer<int[]> 
{ 
    public int GetHashCode(int[] instance) { return 0; } // TODO: better HashCode for arrays 
    public bool Equals(int[] instance, int[] other) 
    { 
     if (other == null || instance == null || instance.Length != other.Length) return false; 

     return instance.SequenceEqual(other); 
    } 
} 

Теперь пишу это, чтобы получить только различные значения для списка:

var result = intArrList.Distinct(new MyComparer()); 

Однако, если вы хотите разные перестановки, вы также должны реализовать свой компаратор таким образом:

public bool Equals(int[] instance, int[] other) 
{ 
    if (ReferenceEquals(instance, other)) return true; // this will return true when both arrays are NULL 
    if (other == null || instance == null) return false; 
    return instance.All(x => other.Contains(x)) && other.All(x => instance.Contains(x)); 
} 

EDIT: Для лучшего GetashCode -внедрение вы можете посмотреть на this post как и предложенный в @ Mick's ответ.

+1

Будет ли это решение считать '{1,2,3,4'} равным' {2,3,4,1} ', как хочет OP? –

+0

Уверенный, он должен, как вы можете видеть во второй реализации «Equals». Или мне что-то не хватает? – HimBromBeere

5

Код подъема скважины от here и here.Более общая реализация GetHashCode бы сделать это более общим характер, однако я считаю, что ниже реализация является самым надежным

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<int[]> intArrList = new List<int[]>(); 
     intArrList.Add(new int[3] { 0, 0, 0 }); 
     intArrList.Add(new int[5] { 20, 30, 10, 4, 6 }); //this 
     intArrList.Add(new int[3] { 1, 2, 5 }); 
     intArrList.Add(new int[5] { 20, 30, 10, 4, 6 }); //this 
     intArrList.Add(new int[3] { 12, 22, 54 }); 
     intArrList.Add(new int[5] { 1, 2, 6, 7, 8 }); 
     intArrList.Add(new int[4] { 0, 0, 0, 0 }); 

     var test = intArrList.Distinct(new IntArrayEqualityComparer()); 
     Console.WriteLine(test.Count()); 
     Console.WriteLine(intArrList.Count()); 
    } 

    public class IntArrayEqualityComparer : IEqualityComparer<int[]> 
    { 
     public bool Equals(int[] x, int[] y) 
     { 
      return ArraysEqual(x, y); 
     } 

     public int GetHashCode(int[] obj) 
     { 
      int hc = obj.Length; 
      for (int i = 0; i < obj.Length; ++i) 
      { 
       hc = unchecked(hc * 17 + obj[i]); 
      } 
      return hc; 
     } 

     static bool ArraysEqual<T>(T[] a1, T[] a2) 
     { 
      if (ReferenceEquals(a1, a2)) 
       return true; 

      if (a1 == null || a2 == null) 
       return false; 

      if (a1.Length != a2.Length) 
       return false; 

      EqualityComparer<T> comparer = EqualityComparer<T>.Default; 
      for (int i = 0; i < a1.Length; i++) 
      { 
       if (!comparer.Equals(a1[i], a2[i])) return false; 
      } 
      return true; 
     } 
    } 
} 

Edit: родовой реализации IEqualityComparer в течение массивов любого типа: -

public class ArrayEqualityComparer<T> : IEqualityComparer<T[]> 
{ 
    public bool Equals(T[] x, T[] y) 
    { 
     if (ReferenceEquals(x, y)) 
      return true; 

     if (x == null || y == null) 
      return false; 

     if (x.Length != y.Length) 
      return false; 

     EqualityComparer<T> comparer = EqualityComparer<T>.Default; 
     for (int i = 0; i < x.Length; i++) 
     { 
      if (!comparer.Equals(x[i], y[i])) return false; 
     } 
     return true; 
    } 

    public int GetHashCode(T[] obj) 
    { 
     int hc = obj.Length; 
     for (int i = 0; i < obj.Length; ++i) 
     { 
      hc = unchecked(hc * 17 + obj[i].GetHashCode()); 
     } 
     return hc; 
    } 
} 

Edit2: Если упорядочение чисел в массивах не имеет значения, я бы

var test = intArrList.Select(a => a.OrderBy(e => e).ToArray()).Distinct(comparer).ToList(); 
+0

Nice 'HashCode'-реализация. – HimBromBeere

+0

Я не беру на себя ответственность за это, как указано, из одной из ссылок. Хорошая реализация hashcode - довольно сложная вещь. Средняя реализация Hashcode будет работать 99.99999% времени и укусить вас довольно прямо в заднице 0.00001% времени. – Mick

+0

Вот почему я сконцентрировал свои способности в ответе на полезные вещи :). Хороший ответ, хотя, поддержан. – HimBromBeere

2
List<int[]> CopyString1 = new List<int[]>(); 
CopyString1.AddRange(intArrList); 
List<int[]> CopyString2 = new List<int[]>(); 
CopyString2.AddRange(intArrList); 
for (int i = 0; i < CopyString2.Count(); i++) 
{ 
    for (int j = i; j < CopyString1.Count(); j++) 
    { 
     if (i != j && CopyString2[i].Count() == CopyString1[j].Count()) 
     { 
      var cnt = 0; 
      for (int k = 0; k < CopyString2[i].Count(); k++) 
      { 
       if (CopyString2[i][k] == CopyString1[j][k]) 
        cnt++; 
       else 
        break; 
      } 
      if (cnt == CopyString2[i].Count()) 
       intArrList.RemoveAt(i); 
     } 
    } 
} 
0

Вы можете использовать HashSet. HashSet - это коллекция, используемая для гарантии уникальности, и вы можете сравнить ее по коллекции, Intersect, Union. и т. д.

Плюсы: нет дубликатов, легко манипулировать группами данных, более эффективно Против: вы не можете получить конкретный элемент в коллекции, например: list [0] не работает для HashSets. Вы можете только перечислить элементы. например Еогеасп

Вот пример:

using System; 
using System.Collections.Generic; 

namespace ConsoleApp2 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      HashSet<HashSet<int>> intArrList = new HashSet<HashSet<int>>(new HashSetIntComparer()); 
      intArrList.Add(new HashSet<int>(3) { 0, 0, 0 }); 
      intArrList.Add(new HashSet<int>(5) { 20, 30, 10, 4, 6 }); //this 
      intArrList.Add(new HashSet<int>(3) { 1, 2, 5 }); 
      intArrList.Add(new HashSet<int>(5) { 20, 30, 10, 4, 6 }); //this 
      intArrList.Add(new HashSet<int>(3) { 12, 22, 54 }); 
      intArrList.Add(new HashSet<int>(5) { 1, 2, 6, 7, 8 }); 
      intArrList.Add(new HashSet<int>(4) { 0, 0, 0, 0 }); 

      // Checking the output 
      foreach (var item in intArrList) 
      { 
       foreach (var subHasSet in item) 
       { 
        Console.Write("{0} ", subHasSet); 
       } 

       Console.WriteLine(); 
      }    

      Console.Read(); 
     } 

     private class HashSetIntComparer : IEqualityComparer<HashSet<int>> 
     { 
      public bool Equals(HashSet<int> x, HashSet<int> y) 
      { 
       // SetEquals does't set anything. It's a method for compare the contents of the HashSet. 
       // Such a poor name from .Net 
       return x.SetEquals(y); 
      } 

      public int GetHashCode(HashSet<int> obj) 
      { 
       //TODO: implemente a better HashCode 
       return base.GetHashCode(); 
      } 
     } 
    } 
} 


Output: 
0 
20 30 10 4 6 
1 2 5 
12 22 54 
1 2 6 7 8 

Примечание: Так как 0 повторяется несколько раз, HashSet считает 0 только один раз. Если вам нужно разницу между 0 0 0 0 и 0 0 0, то вы можете заменить HashSet<HashSet<int>> for HashSet<List<int>> и реализовать вместо этого вместо сравнения.

Вы можете использовать эту ссылку, чтобы узнать, как сравнить список: https://social.msdn.microsoft.com/Forums/en-US/2ff3016c-bd61-4fec-8f8c-7b6c070123fa/c-compare-two-lists-of-objects?forum=csharplanguage

Если вы хотите узнать больше о коллекции и Datatypes этот курс является идеальным местом, чтобы узнать его: https://app.pluralsight.com/player?course=csharp-collections&author=simon-robinson&name=csharp-collections-fundamentals-m9-sets&clip=1&mode=live

0

Используя MoreLINQ, это может быть очень просто с DistinctBy.

var result = intArrList.DistinctBy(x => string.Join(",", x)); 

Как и в случае с ответом GroupBy, если вы хотите, чтобы различие было независимо от порядка заказа в соединении.

var result = intArrList.DistinctBy(x => string.Join(",", x.OrderBy(y => y))); 

EDIT: Это как это реализовано

public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, 
      Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer) 
     { 
      if (source == null) throw new ArgumentNullException(nameof(source)); 
      if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); 

      return _(); IEnumerable<TSource> _() 
      { 
       var knownKeys = new HashSet<TKey>(comparer); 
       foreach (var element in source) 
       { 
        if (knownKeys.Add(keySelector(element))) 
         yield return element; 
       } 
      } 
     } 

Так, если вы не нужны MoreLINQ для чего-то вы можете просто использовать метод, как это:

private static IEnumerable<int[]> GetUniqueArrays(IEnumerable<int[]> source) 
    { 
     var knownKeys = new HashSet<string>(); 
     foreach (var element in source) 
     { 
      if (knownKeys.Add(string.Join(",", element))) 
       yield return element; 
     } 
    } 
Смежные вопросы