你能解释一下这个lambda分组功能吗?

时间:2013-02-28 15:54:59

标签: c# .net linq lambda grouping

我一直在使用LINQ和Lambda Expressions一段时间,但我对该功能的各个方面仍然不太满意。

所以,当我最近在一个项目上工作时,我需要根据一些属性获得一个独特的对象列表,然后我遇到了这个代码。它有效,我对此很好,但我想了解分组机制。如果我可以提供帮助,我不喜欢简单地插入代码并远离问题。

无论如何,代码是:

var listDistinct
    =list.GroupBy(
        i => i.value1,
        (key, group) => group.First()
    ).ToList();

在上面的代码示例中,您首先调用GroupBy并向其传递一个lambda表达式,告诉它​​按属性value1进行分组。代码的第二部分引起了混淆。

我理解keyvalue1声明中引用了(key, group),但我仍然没有围绕正在发生的事情。

6 个答案:

答案 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),groupIEnumerable<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}}。