I used LINQ and Lambda Expressions for a while, but I'm still not quite happy with every aspect of this feature.
So, while I was recently working on a project, I needed to get a separate list of objects based on some property, and I came across this code. This works, and I'm fine with that, but I would like to understand the grouping mechanism. I don’t like just plugging in the code and running away from the problem if I can help him.
In any case, the code:
var listDistinct =list.GroupBy( i => i.value1, (key, group) => group.First() ).ToList();
In the above code example, you first call GroupByand pass it a lambda expression telling it to group by property value1. The second part of the code is confusing.
GroupBy
value1
I understand that it keyrefers to value1in the expression (key, group), but I still do not envelop everything that is happening.
key
(key, group)
What makes an expressionlist.GroupBy( i => i.value1, (key, group) => group.First()) do?
What makes an expression
list.GroupBy( i => i.value1, (key, group) => group.First())
do?
This creates a query that, when executed, analyzes the sequence listto create a sequence of groups, and then projects the sequence of groups into a new sequence. In this case, the projection should infer the first element from each group.
list
The first lambda selects the "key" on which the groups are built. In this case, all elements in the list that have the same property value1are placed in a group. The value they share becomes the "key" of the group.
- ; select . , value1.
select
:
http://msdn.microsoft.com/en-us/library/bb549393.aspx
, .
group . group ?
group
, group - . LINQ # 3.0, , , group . , group . group . .
, , group , " , " @. ,
@
list.GroupBy( i => i.value1, (key, @group) => @group.First())
.
#?
. :
http://ericlippert.com/2009/05/11/reserved-and-contextual-keywords/
int GroupBy:
int
var list = new[] {1, 2, 3, 1, 2, 2, 3};
GroupBy x => x, 3 :
x => x
IEnumerable<IEnumerable<int>> {{1,1},{2,2,2},{3,3}}
: 1, 2, 3. group.First() , :
group.First()
{1,1}: -> 1. {2,2,2}: -> 2 {3,3} -> 3
, : {1, 2, 3}
{1, 2, 3}
Enumerable.GroupBy:
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 .
IEnumerable<IGrouping<TK, TS>>
TResult
, , GroupBy a Select:
Select
var listDistinct = list .GroupBy(i => i.value1) .Select(g => g.First()) .ToList();
(key, group) => group.First()
First() .
First()
- key - , (value1 ), group - IEnumerable<T> , 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 .
i => i.value1
i.value
The part (key, group) => group.First()in the source code is a delegate to the result selector . In this code, it is written in a more semantic syntax from ... select. There gis groupin the source code.
g