Linq以非常规的方式删除ASP.NET WEB API中的重复项

时间:2014-02-22 05:36:48

标签: c# linq select asp.net-web-api

这是我的模特:

 public class Combination
{
    public int Id { get; set; }
    public int CombOne{ get; set; }
    public int CombTwo{ get; set; }

}

我想编写一个LINQ语句来提取那些包含属性CombOne和CombTwo的重复组合的Combination类的实例。 因此,如果有三个实例,如:

Combination C1= new Combination{Id=1, CombOne=1, CombTwo=2};
Combination C2= new Combination{Id=2, CombOne=2, CombTwo=1};
Combination C3= new Combination{Id=3, CombOne=1, CombTwo=2};

Linq语句应该返回C2和C3的列表,因为它们包含CombOne和CombTwo变量的重复组合,同时它应该保留原始实例(C1)(不应该返回C1,因为它是第一个实例这种组合。)

我使用foreach循环得到了正确的结果。

List<Combination> invalid2 = new List<Combination>();
foreach (Combination g in list)// Gamelist.Match is a list of Combination type
{
    if (invalid2.Contains(g))
        continue;
    List<Combination> invalid3 = (from r in list
                                    where
                                        ((r != g) &&
                                        (((r.CombOne == g.CombOne) && (r.CombTwo == g.CombTwo)) ||
                                        ((r.CombOne == g.CombTwo) && (r.CombTwo == g.CombOne))))
                                    select r).ToList();
    invalid2 = invalid2.Concat(invalid3).ToList();
}

我想仅使用Linq语句来获得结果以提高效率。我尝试了很多,但没有得到所需的输出。在此先感谢您的真诚努力

2 个答案:

答案 0 :(得分:1)

如果我理解正确,您希望输出生成包含之前已见过的集合{ CombOne, CombTwo }的任何实例。这意味着某种聚合。要做到这一点,您需要跟踪之前已经看到的实例并引用该设置以确保尚未看到所检查的每个后续元素。我将利用Hashet.Add不添加已在集合中的元素并在HashSet中使用自定义比较器进行相等的事实。

 var set = new HashSet<Combination>(new CombinationComparer());
 var invalid = list.Aggregate(new List<Combination>(list.Count),
                              (a,c) => 
                              {
                                 if (!set.Add(c))
                                 {
                                     a.Add(c);
                                 }
                                 return a;
                              });

其中

public class CombinationComparer : IEqualityComparer<Combination>
{
     public bool Equals(Combination c1, Combination c2)
     {
         if (ReferenceEquals(c1,c2))
         {
              return true;
         }

         if (c1 == null || c2 == null)
         {
              return false;
         }

         return (c1.CombOne == c2.CombOne && c1.CombTwo == c2.CombTwo)
                    || (c1.CombOne == c2.CombTwo && c1.CombTwo == c2.CombOne);
    }

    public int GetHashCode(Combination c)
    {
         if (c == null)
         {
             return 0;
         }

         unchecked
         {
              // it's important that this be commutative so we don't
              // do the usual multiply by a prime to differentiate
              // them.
              return CombOne.GetHashCode() + CombTwo.GetHashCode();
         }

    }
}

我会注意到,这不是更高效,并且比使用循环和构建结果的可读性稍差:

var set = new HashSet<Combination>(new CombinationComparer());
var invalid = new List<Combination>(list.Count);
foreach (var item in list)
{
    if (set.Add(item)) continue;

    invalid.Add(item);
}

在这两种情况下,作为奖励,您可以获得存储在set中的唯一且首次出现的重复的集合。使用HashSet可以在两种情况下都非常有效,因为您只对列表进行一次迭代,而HashSet.AddList.Add都是平均情况O(1) - 特别是当我们预先调整大小时最初列出它的最大尺寸。

我会注意到,如果你真正想要的是删除重复项,你可以简单地将比较器与Distinct一起使用。除了您没有保留无效列表外,这与上述类似。

var unique = list.Distinct(new CombinationComparer());

我在http://dotnetfiddle.net/YoApPu

创造了一个工作小提琴

答案 1 :(得分:0)

 invalid2= (from r in GameList.Match
                   from g in GameList.Match
                   where((r.Id<g.Id)&&(((r.TeamAId == g.TeamAId) && (r.TeamBId == g.TeamBId)) || ((r.TeamAId == g.TeamBId) && (r.TeamBId == g.TeamAId))))
                   select g).Distinct().ToList();

这将适用于id严格遵循升序模式的情况。在其他情况下,我们将不得不使用嵌套查询首先使用orderby以升序方式对列表进行排序,然后执行上述查询。