2015-06-12 3 views
2

У меня есть список объектов Tuple<string,string>, и я хочу удалить дубликаты, где, например, кортежи (a,b) и (b,a) считаются одинаковыми (это ребра графа). Что такое nice Способ для этого?Удаление дубликатов из списка кортежей

+0

Не используя работа EqualityComparer, как показано в этой статье SO http://stackoverflow.com/questions/17275727/find-and-deletes-duplicates-in-list-of-tuples-in-c-sharp –

+0

Нужно ли сохранять упорядочение ваших кортежей, например вы могли бы отсортировать их все так, чтобы первый <= второй? Это упростило бы удаление дубликатов, например. используя Distinct, как в ответе здесь. – Rup

+0

Нет заказа не имеет значения – Cemre

ответ

6

Вам нужно создать компаратор, который может сравнить кортежи таким образом, что порядок элементов doens't дело:

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

+1 Я создал тестовую программу, и ваш алгоритм намного быстрее.Результатом очистки списка из 100000 случайно сгенерированных кортежей было 3825,1412ms в моем случае и только 59 532 9ms в вашем случае для того же скопированного списка. Удалил мой ответ. – VladL

+0

Отличный ответ спасибо! – Cemre

0

Вы, возможно, потребуется создать класс, который реализует IEqualityComparer<Tuple<string, string>>:

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

Почему downvote, это делает именно то, о чем попросил – CSharpie

0

попробовать с использованием словаря и макетирование ключ, обозначает каждый кортеж. у вас есть символ, который НЕ будет отображаться в ваших строках, который вы можете использовать в качестве разделителя? Я выбрал «:» в этом примере:

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

Живой Код: 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) 
    } 
] 
+0

Обратите внимание, что это может привести к возврату кортежа, который не находится в исходной последовательности; если вход является '{" b "," a "}', тогда он вернет '{" a "," b "}', а не '{" b "," a "}' на выходе. – Servy

+1

@Servy Да, но вот как я понял вопрос. Если нужно сохранить исходные элементы, я вернусь к dotnetfiddle.net и сделаю вторую версию. –

+0

Он хочет, чтобы пары, у которых значения в обратном порядке считаются равными, но это не обязательно означает, что это нормально для возврата элемента, который не находится в исходном наборе. Это может быть хорошо, но это может быть не так, это не определено. – Servy

0

Для сохранения оригинала, используйте 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 
Смежные вопросы