Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ query using list of objects property of an object

Tags:

c#

linq

I've tried to decipher how to do this myself but my understanding of LINQ usage is pretty limited. What I have is two classes as such:

public class Grocery
{
    public string ItemName { get; set; }
    public List<ItemType> ItemTypes { get; set; }
}

public class ItemType
{
    public double Weight { get; set; }
    public long Quantity { get; set; }
}

The idea being you can have a Grocery object with ItemName "Carrots", and that comes in two different ItemTypes, e.g. where Weight = 1lb and where Weight = 2lb, with different quantities of each. Obviously there will be many more items than just carrots, and these are stored in a list also:

List<Grocery> groceries;

What I'd like to do is get a list of all quantities for all groceries, given a certain weight. E.g. I could do it like this for all items that have a 1lb type:

List<long> quantities = new List<long>();

foreach (Grocery grocery in groceries)
{
    foreach (ItemType itemType in grocery.ItemTypes)
    {
        if (itemType.Weight == 1)
        {
            quantities.Add(itemType.Quantity);
        }
    }
}

What I would like to know is how I can achieve the same result using a LINQ expression, though I'm not sure if this is possible without reverting to using .ForEach and simply doing the same as the code above but in a less verbose (and therefore less readable) way. Appreciate your help.

like image 975
Eamonn Sullivan Avatar asked Oct 21 '25 12:10

Eamonn Sullivan


2 Answers

As Reno says, SelectMany is the way to go. But rather than using ForEach to populate a list, go LINQ all the way:

List<long> quantities = groceries.SelectMany(g => g.ItemTypes)
    .Where(t => t.Weight == 1)
    .Select(t => t.Quantity)
    .ToList();
like image 188
StriplingWarrior Avatar answered Oct 23 '25 02:10

StriplingWarrior


You can achieve this using SelectMany. In order to replicate your foreach processing, you can use the following:

List<long> quantities = groceries.SelectMany(x => x.ItemTypes)
    .Where(x => x.Weight == 1)
    .Select(x => x.Quantity)
    .ToList();

In order to get a list of quantities for each Grocery, as you mention in your question, you can use something like this:

var groceryQuantities = groceries.Select(g => new
{
    g.ItemName,
    Quantities = g.ItemTypes
        .Where(it => it.Weight == 1)
        .Select(it => it.Quantity)
        .ToList()
}).ToList();

This will create a list of anonymous objects, that looks something like this (in JSON for demonstration):

[
    {
        "ItemName": "Grocery #1",
        "Quantities": [1, 2, 3]
    },
    ...
]

You can sum the quantities, if that's what you're actually after. That would look like this:

var groceryQuantities = groceries.Select(g => new
{
    g.ItemName,
    TotalQuantity = g.ItemTypes
        .Where(it => it.Weight == 1)
        .Sum(it => it.Quantity)
}).ToList();

There's a tonne of other things you can do, but this should cover one way to approach this as a whole.

like image 44
Kirk Larkin Avatar answered Oct 23 '25 03:10

Kirk Larkin