2015-11-05 241 views
1

标题可能会产生误导,这样的例子:删除“重复”

我有一个类:

class Pair 
{ 
    Book Book1; 
    Book Book2; 
} 

我有这些列表:

var list = new List<Pair>(); 

list.Add(new Pair() { 
    Book1 = new Book() { Id = 123 }, 
    Book2 = new Book() { Id = 456 } 
}); 

list.Add(new Pair() { 
    Book1 = new Book() { Id = 456 }, 
    Book2 = new Book() { Id = 123 } 
}); 

现在,尽管书被“翻转”,我的系统应该将这些视为重复。

我需要一个方法来从列表中删除这些'duplicates'中的一个一个(任何一个 - 所以我们假设第一个使它简单)。

我已经试过

 var tempList = new List<Pair>(); 
     tempList.AddRange(pairs); 

     foreach (var dup in pairs) 
     { 
      var toRemove = pairs.FirstOrDefault(o => o.Book1.Id == dup.Book2.Id 
                 && o.Book2.Id == dup.Book1.Id); 

      if (toRemove != null) 
       tempList.Remove(toRemove); 
     } 

     return tempList; 

这不返回任何项目(假设上面的例子),因为这两个Pair对象将满足拉姆达的情况下,我只有一个,虽然删除一个。

注意:如果我只是删除从集合马上(而不是从临时列表)元素这不会发生 - 但后来我就不能来遍历它没有例外。

+0

'dup.Book.Id'?这不应该是Book1或Book2吗? – Vlad274

+0

@ Vlad274 - 很好,我编辑了这个问题。 –

回答

4

您可以设置一个IEqualityComparer<Pair>具体类并传递到.Distinct()方法:

class PairComparer : IEqualityComparer<Pair> 
{ 
    public bool Equals(Pair x, Pair y) 
    { 
     return (x.Book1.Id == y.Book1.Id && x.Book2.Id == y.Book2.Id) 
      || (x.Book1.Id == y.Book2.Id && x.Book2.Id == y.Book1.Id); 
    } 

    public int GetHashCode(Pair obj) 
    { 
     return obj.Book1.Id.GetHashCode()^obj.Book2.Id.GetHashCode(); 
    } 
} 

,然后用它像这样:

var distinctPairs = list.Distinct(new PairComparer()); 
+0

这是唯一正确的答案。 – ataravati

0

我已经设法找到解决方案,但这是我不满意的一个。对于我想要做的工作来说,这似乎过于冗长。我现在做一个额外的检查,以查看是否重复已经被添加到列表中:

if(toRemove != null && tempList.Any(o => o.Book1.Id == toRemove.Book2.Id 
             && o.Book2.Id == toRemove.Book1.Id)) 
              tempList.Remove(toRemove); 

我非常开放给其他建议。

+0

当做一个List.Remove你应该注意复杂性。 'Any'是O(N),'Remove'是O(N),foreach是O(N),所以你用O(N^3)的时间复杂度就是这样。 –

+0

@GeorgePolevoy - 是的,你完全正确,这就是为什么我不满意这种方法。这是为了让它工作(现在),但我不接受它。 –

2

的问题是,你都去掉重复。

试试这个:

var uniquePairs = list.ToLookup(p => Tuple.Create(Math.Min(p.Book1.Id, p.Book2.Id), Math.Max(p.Book1.Id, p.Book2.Id))).Select(g => g.First()).ToList(); 
1

我会用以下

foreach (var dup in pairs) 
    { 
     var toRemove = pairs.FirstOrDefault(o => o.Book1.Id == dup.Book2.Id 
                && o.Book2.Id == dup.Book1.Id 
                && o.Book1.Id > o.Book2.Id); 

     if (toRemove != null) 
      tempList.Remove(toRemove); 
    } 

确切地说,这将删除重复那就是 “乱序”。但是,如果重复对具有相同顺序的书籍,则此(和您的原件)将失败。


更好的解决方案(因为我们遍历曾经配对反正)是使用一个HashSet

var hashSet = new HashSet<Tuple<int,int>>(); 
    foreach (var item in pairs) 
    { 
     var tuple = new Tuple<int,int>(); 
     if (item.Book1.Id < item.Book2.Id) 
     { 
      tuple.Item1 = item.Book1.Id; 
      tuple.Item2 = item.Book2.Id; 
     } 
     else 
     { 
      tuple.Item1 = item.Book2.Id; 
      tuple.Item2 = item.Book1.Id; 
     } 

     if (hashSet.Contains(tuple)) 
     { 
      tempList.Remove(dup); 
     } 
     else 
     { 
      hashSet.Add(tuple); 
     } 
    }