如何不使用循环声明方式仅获取纯记录

时间:2019-04-24 18:32:03

标签: c# linq

我有些人在买自行车。 桌子看起来像

PeopleID Bicycle

1         Green

1         Yellow

2         Red

2         Red

3         Green

3         Red

3         Yellow

我想吸引只购买同一辆自行车的用户。 输出必须用于此示例,因为PeopleID 2没有其他颜色的自行车

  

2红色

班级是

 class Foo
    {
        public String PeopleID { get; set; }
        public String Bicycle { get; set; }
    }

函数是

static void WhoBuyedSameBike(IEnumerable<Foo> foos) 
    {
        var tmpFoos = foos;
        var result = tmpFoos.GroupBy(x => x.PeopleID);
        var queryResult = new List<QueryResult>();
        foreach (var fooItem in result)
        {
            if (fooItem.Count() == tmpFoos.Count(w => w.PeopleID == fooItem.Key && w.Bicycle == "Red"))
            {
                queryResult.Add(new QueryResult { PeopleID = fooItem.Key });
            }

        }
        foreach (var item in queryResult)
        {
            Console.WriteLine($"PeopleId : {item.PeopleID}");
        }
    }

是否还有其他方法可以通过不使用Linq的foreach循环获取更多声明性代码来获取输出?

1 个答案:

答案 0 :(得分:1)

使用Linq,按每个人分组,获得每种自行车颜色的不同计数,然后从计数为1的每个组中选择第一个(唯一的)项目。

IEnumerable<Foo> result = 
    foos
    .GroupBy(foo => foo.PeopleID)
    .Where(group => group.Select(foo => foo.Bicycle).Distinct().Count() == 1)
    .Select(group => group.First());

像这样输出它:

foreach(var foo in result) {
    Console.WriteLine($"PeopleId:{foo.PeopleID}, Bicycle:{foo.Bicycle}");
}


编辑

在评论中,Harald Coppoolse建议在将计数计入where语句之前插入.Take(2)。这将更加有效,因为.Count()必须枚举整个集合才能获得大小(除非它实现ICollection)。他还提出了第二种方法,该方法更加有效。

但是,在进行此操作时,我认为我走得更远,将嵌套的Select和Distinct方法压缩为一个聚合,其结果无需再次重复以确定是否存在重复项。

foos
    .GroupBy(foo => foo.PeopleID)
    .Where(group => group.Aggregate((a,b) => a?.Bicycle == b.Bicycle ? a : null) != null)
    .Select(group => group.First());

当然,所有这些只是为了更好地理解。当然,在了解Aggregate方法,terneray运算符和null条件运算符之前,请先将头放在原始版本上。

顺便说一句。我之前没有提到过,如果性能比可读性或代码优雅更重要,那么不要逃避for循环。上面的代码可以不受此限制地提高效率。