你能解释一下这个 lambda 分组函数吗?
本文关键字:lambda 函数 能解释 一下 | 更新日期: 2023-09-27 18:01:39
我已经使用 LINQ 和 Lambda 表达式一段时间了,但我仍然不完全适应该功能的各个方面。
因此,当我最近在处理一个项目时,我需要基于某些属性获取一个不同的对象列表,我遇到了这段代码。它有效,我对此很好,但我想了解分组机制。我不喜欢简单地插入代码并在可以帮助它的情况下逃避问题。
无论如何,代码是:
var listDistinct
=list.GroupBy(
i => i.value1,
(key, group) => group.First()
).ToList();
在上面的代码示例中,您首先调用GroupBy
并向其传递一个 lambda 表达式,告诉它按属性 value1
进行分组。代码的第二部分导致混淆。
我知道key
在(key, group)
声明中引用了value1
,但我仍然没有对正在发生的一切进行思考。
表达式是什么
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/
将其简化为int
列表以及如何通过使用GroupBy
在此列表中进行区分:
var list = new[] {1, 2, 3, 1, 2, 2, 3};
如果您使用 x => x
调用 GroupBy
,您将获得 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}
您的情况与此类似。
它使用 Enumerable.GroupBy
方法的重载:
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>>
(,此重载允许您将每个组投影到所选TResult
的单个实例。
请注意,您可以使用基本的GroupBy
重载和Select
获得相同的结果:
var listDistinct = list
.GroupBy(i => i.value1)
.Select(g => g.First())
.ToList();
(key, group) => group.First()
它只是在每个组中获取First()
元素。
在该 lambda 表达式中,key
是一个用于创建该组的键(在您的示例中value1
(,并且group
与具有该key
的所有元素IEnumerable<T>
。
下面的自描述示例应该可以帮助您理解分组:
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
}
}
它等于
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 ...选择。在这里g
group
原始代码。