0

I need help with understanding the fourth argument for GroupJoin. From what i understand so far GroupJoin takes 4 arguments: (1, 2) - the first one is the secondary list and argument two is a Func that returns the key from the first object type in other words from the first list in this case (people). (3, 4) A Func that returns the key from the second object type from the second list in this case (items), and one that stores the grouped object with the group itself (I can't understand the code for this part). Considering this and having the code below:

var products = new Product[]
{
    new Product { Id = 1, Type = "Phone", Model = "OnePlus", Price = 1000 },
    new Product { Id = 2, Type = "Phone", Model = "Apple", Price = 2000 },
    new Product { Id = 3, Type = "Phone", Model = "Samsung", Price = 1500 },
    new Product { Id = 4, Type = "TV", Model = "Samsung 32", Price = 200 },
};

var people = new Person[]
{
    new Person { Id = 1, Name = "Ivan Ivanov", Money = 150000 },
    new Person { Id = 2, Name = "Dragan Draganov", Money = 250000 },
    new Person { Id = 3, Name = "Ivelin Ivelinov", Money = 350000 
}
};

var items = new Item[]
{
    new Item { PersonId = 1, ProductId = 1, Amount = 1 },
    new Item { PersonId = 1, ProductId = 4, Amount = 1 },
    new Item { PersonId = 1, ProductId = 5, Amount = 1 },
    new Item { PersonId = 1, ProductId = 7, Amount = 1 },
    new Item { PersonId = 2, ProductId = 2, Amount = 1 },
};

Query:

var productOwnerList = people
    .GroupJoin(
        items,
        o => o.Id,
        i => i.PersonId,
        (o, i) => new   <--- (**)            
        {
            Person = o,
            Products = i
                .Join(products,
                    o1 => o1.ProductId,
                    i2 => i2.Id,
                 (o1, i2) => i2)          <--- (*)
                .ToArray()
        })
    .ToArray();  

Just to mention I post only a few lines for the data. I need help to understand what the 4th argument for the join method is performing here -> (*) (stores the grouped object with the group itself) ? When i watch the result i see it it puts all Person id's associate with the product keys and joined the two lists based on Items list (one to many). But i cannot get what exactly this line means (o1, o2) => i2). Its obvious what is doing (put all the items associated with the person id in a array (items[]) for every person. but what is "under the hood" here ? Also one question about (**) this line its creating new object, is this a anonymous class or if its not what is it.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    [Linq to Entities join vs groupjoin](https://stackoverflow.com/a/15599143/2417602) might help.... – vikscool Jun 24 '19 at 06:16
  • @vikscool It helped a bit. –  Jun 24 '19 at 06:27
  • 2
    Please note that peppering your text with triple-backticks makes it very hard to read. I'm not sure what formatting you were hoping to achieve, but it definitely wasn't helping the clarity. – Jon Skeet Jun 24 '19 at 06:35
  • @Jon Skeet Sorry i didn't find appropriate formatting symbols for "underline" something. Can you give me a tip for what symbols to use ? –  Jun 24 '19 at 06:42
  • See https://stackoverflow.com/editing-help for formatting assistance, but I don't believe there's anything for underlining. Use bold or italics instead - but I'd suggest using them sparingly. – Jon Skeet Jun 24 '19 at 07:30

1 Answers1

3

The fourth argument - which maps to the fifth parameter in the documentation (because the first parameter is the target of the extension method call) is just the result selector. It's a function accepting two parameters: the first is an element of the "outer" sequence (the people array in your case) and the second is a sequence of elements from the "inner" sequence (the items array in your case) which have the same key as the outer element. The function should return a "result" element, and the overall result of the method call is a sequence of those results.

The function is called once for each of the "outer" elements, so you'd have:

  • First call: person ID 1, and products with IDs 1, 4, 5, 7
  • Second call: person ID 2, and the product with ID 2
  • Third call: person ID 3, and an empty sequence of products

Your query is complex because you're using an anonymous type for your result, and constructing an instance of the anonymous type using another query. Here's a simpler query that might help to clarify:

var productOwnerList = people
    .GroupJoin(
        items,
        o => o.Id,
        i => i.PersonId,
        (person, items) => $"{person.Id}: {string.Join(",", items.Select(item => item.ProductId))}"
    .ToArray();  
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Great answer i followed the documentation that you provide me and your answer. It is pretty clear now but what it turns out is that the second part where i create a anonymous type for the result everything there is just to construct the object and its not related to the query itself ? –  Jun 24 '19 at 07:03
  • @BorisBorovski: I'm afraid I don't understand the question in your comment. – Jon Skeet Jun 24 '19 at 07:30
  • I mean i have a second query in the result for the GroupJoin method that maps items from the Item data to the products in the product data. –  Jun 24 '19 at 07:40
  • @BorisBorovski: Yes - where you're calling `Join`, that's effectively a nested query which is executed for each group. – Jon Skeet Jun 24 '19 at 07:46