我一直在使用LINQ和Lambda Expressions一段时间,但我对该功能的各个方面仍然不太满意。
所以,当我最近在一个项目上工作时,我需要根据一些属性获得一个独特的对象列表,然后我遇到了这个代码。它有效,我对此很好,但我想了解分组机制。如果我可以提供帮助,我不喜欢简单地插入代码并远离问题。
无论如何,代码是:
var listDistinct
=list.GroupBy(
i => i.value1,
(key, group) => group.First()
).ToList();
在上面的代码示例中,您首先调用GroupBy
并向其传递一个lambda表达式,告诉它按属性value1
进行分组。代码的第二部分引起了混淆。
我理解key
在value1
声明中引用了(key, group)
,但我仍然没有围绕正在发生的事情。
答案 0 :(得分:7)
表达式是什么
list.GroupBy( i => i.value1, (key, group) => group.First())
办?
这会创建一个查询,在执行时会分析序列list
以生成一系列组,然后将组的序列投影到一个新序列中。在这种情况下,投影是从每组中取出第一项。
第一个lambda选择构建组的“密钥”。在这种情况下,列表中具有相同value1
属性的所有项都放在一个组中。他们共享的价值成为集团的“关键”。
第二个lambda来自键控组序列;就好像你在组序列上做了select
一样。此查询的净效果是从列表中选择一组元素,使得结果序列的每个元素具有不同的value1
属性值。
文档在这里:
http://msdn.microsoft.com/en-us/library/bb549393.aspx
如果文件不清楚,我很乐意将批评转交给文档管理员。
此代码使用
group
作为lambda的形式参数。group
不是保留关键字吗?
不,group
是上下文关键字。 LINQ被添加到C#3.0中,因此可能已经存在使用group
作为标识符的现有程序。如果group
成为保留关键字,则在重新编译时会破坏这些程序。相反,group
仅是查询表达式上下文中的关键字。在查询表达式之外,它是普通标识符。
如果要引起注意它是普通标识符的事实,或者如果要在查询表达式中使用标识符group
,则可以告诉编译器“将其视为标识符,而不是关键字“通过@
添加前缀。如果我写上面的代码,我会说
list.GroupBy(
i => i.value1,
(key, @group) => @group.First())
说清楚。
C#中还有其他上下文关键字吗?
是。我在这里记录了它们:
http://ericlippert.com/2009/05/11/reserved-and-contextual-keywords/
答案 1 :(得分:4)
我想将此简化为int
列表以及如何使用GroupBy
在此列表中区分:
var list = new[] {1, 2, 3, 1, 2, 2, 3};
如果您使用GroupBy
致电x => x
,您将获得3个类型为的群组:
IEnumerable<IEnumerable<int>>
{{1,1},{2,2,2},{3,3}}
每组的关键是:1,2,3。然后,当调用group.First()
时,这意味着你得到每组的第一项:
{1,1}: -> 1.
{2,2,2}: -> 2
{3,3} -> 3
所以最终的结果是:{1, 2, 3}
您的情况与此相似。
答案 2 :(得分:2)
它使用Enumerable.GroupBy
方法的this overload:
public static IEnumerable<TResult> GroupBy<TSource, TKey, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TKey, IEnumerable<TSource>, TResult> resultSelector
)
,正如MSDN所述:
根据指定的键选择器函数对序列的元素进行分组,并从每个组及其键创建结果值。
因此,与返回一组组(IEnumerable<IGrouping<TK, TS>>
)的other overloads不同,此重载允许您将每个组投影到您选择的TResult
的单个实例。 / p>
请注意,您可以使用基本GroupBy
重载和Select
获得相同的结果:
var listDistinct = list
.GroupBy(i => i.value1)
.Select(g => g.First())
.ToList();
答案 3 :(得分:1)
(key, group) => group.First()
它只是在每个组中使用First()
元素。
在该lambda表达式中key
是用于创建该组的键(在您的示例中为value1
),group
为IEnumerable<T>
,其中包含具有该值的所有元素key
。
答案 4 :(得分:0)
下面的自我描述示例应该可以帮助您理解分组:
class Item
{
public int Value { get; set; }
public string Text { get; set; }
}
static class Program
{
static void Main()
{
// Create some items
var item1 = new Item {Value = 0, Text = "a"};
var item2 = new Item {Value = 0, Text = "b"};
var item3 = new Item {Value = 1, Text = "c"};
var item4 = new Item {Value = 1, Text = "d"};
var item5 = new Item {Value = 2, Text = "e"};
// Add items to the list
var itemList = new List<Item>(new[] {item1, item2, item3, item4, item5});
// Split items into groups by their Value
// Result contains three groups.
// Each group has a distinct groupedItems.Key --> {0, 1, 2}
// Each key contains a collection of remaining elements: {0 --> a, b} {1 --> c, d} {2 --> e}
var groupedItemsByValue = from item in itemList
group item by item.Value
into groupedItems
select groupedItems;
// Take first element from each group: {0 --> a} {1 --> c} {2 --> e}
var firstTextsOfEachGroup = from groupedItems in groupedItemsByValue
select groupedItems.First();
// The final result
var distinctTexts = firstTextsOfEachGroup.ToList(); // Contains items where Text is: a, c, e
}
}
答案 5 :(得分:0)
等于
var listDistinct=(
from i in list
group i by i.value1 into g
select g.First()
).ToList();
原始代码中的i => i.value1
部分是键选择器。在此代码中,组元素的语法只需i.value
键 。
原始代码中的(key, group) => group.First()
部分是结果选择器的委托。在此代码中,它是以更精确的语法编写的 from ... select 。原始代码中g
group
为{{1}}。