Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linq Group by and sum problems

Tags:

c#

linq

I have the following entities:

An account class with a list of subscriptions:

public class Account
{
    /// <summary>
    /// Account ID
    /// </summary>
    public string ID { get; set; }

    /// <summary>
    /// A List with all subscriptions
    /// </summary>
    public IEnumerable<Subscription> Subscriptions { get; set; }
}

the subscription class with a list of variations and add ons:

public class Subscription
{
    /// <summary>
    /// Subscription ID
    /// </summary>
    public string ID { get; set; }

    /// <summary>
    /// Quantity of the subscription.
    /// </summary>
    public int Quantity { get; set; }

    /// <summary>
    /// A list with all subscription add ons
    /// </summary>
    public IEnumerable<AddOn> AddOns { get; set; }

    /// <summary>
    /// A List with all subscription variations
    /// </summary>
    public IEnumerable<Variation> Variations { get; set; }
}

the add on class with a list of variations:

public class AddOn
{
    /// <summary>
    /// Gets or Sets add on id
    /// </summary>
    public string ID { get; set; }

    /// <summary>
    /// Quantity of the add on.
    /// </summary>
    public int Quantity { get; set; }

    /// <summary>
    /// A List with all add on variations
    /// </summary>
    public IEnumerable<Variation> Variations { get; set; }

}

And the variation class:

public class Variation
{
    /// <summary>
    /// Variation ID
    /// </summary>
    public string ID { get; set; }

    /// <summary>
    /// offerUri
    /// </summary>
    public string Code { get; set; }

    /// <summary>
    /// Variation Value
    /// </summary>
    public string Value { get; set; }

    /// <summary>
    /// Variation Name
    /// </summary>
    public string Name { get; set; }

}

What I'm trying to do is to group all add ons with specific Code and sum the quantity. For example I tried:

var groupAddOnsByCode = acc.Subscriptions.Select(s => s.AddOns.GroupBy(a => a.Variations.Select(v => v.Code).FirstOrDefault())).ToList();

This one correct groups add ons but I want a list with with add ons per subscription group by code and total quantity per code.

For example if a Subscription has X number of add ons and the Code of each add on is 1, 2, ..., X, I want to group by the add ons by Code and total Quantity with this Code. I expect the result will be something like if I have the following structure:

A pseudo-code with current structure(Code refers to Variation Class that each add on has):

Subscription {
 //a list with X number of add ons
 AddOn1 = { Variation = { Code = 1 }, Quantity = 2 },
 AddOn2 = { Variation = { Code = 2 }, Quantity = 3 },
 ...
 AddOnX = { Variation = { Code = X }, Quantity = 4 }
}

What I expect:

Subscription {
    AddOn1 { Variation = { Code = 1 }, Quantity = totalAmountOfQuantityForAddOnsWithCode = 1 },
    ...
    AddOnX { Variation = { Code = X }, Quantity = totalAmountOfQuantityForAddOnsWithCode = X },
}

You can use this dotnetfiddle to test dummy data.

Sorry for the long post and I will appreciate any help. Also keep in mind my c# and Linq knowledge is limited.

like image 446
Alex Char Avatar asked Jan 04 '16 14:01

Alex Char


3 Answers

If I understand correctly, the following might produce the desired result

var result = acc.Subscriptions.Select(s => new
{
    Subscription = s,
    AddOns = s.AddOns.SelectMany(a => a.Variations, (a, v) => new { a, v })
        .GroupBy(e => e.v.Code, (key, elements) => new 
        { 
            Code = key,
            Quantity = elements.Sum(e => e.a.Quantity)
        }).ToList()
}).ToList();
like image 148
Ivan Stoev Avatar answered Oct 20 '22 12:10

Ivan Stoev


Though I'm not completely sure this is what you want, try this

var codes = addOns.SelectMany(a => a.Variations).Select(v => v.Code).Distinct();

var addOnsByCode = new List<AddOn>(); //your desired result
foreach (var code in codes)
{
    var addOnsWithThisCode = addOns.Where(a => a.Variations.Any(v => v.Code == code));
    addOnsByCode.Add(new AddOn{
        Variations = new List<Variation> { new Variation { Code = code } },
        Quantity = addOnsWithThisCode.Sum(a => a.Quantity),
        ID = string.Join("_", addOnsWithThisCode.Select(a => a.ID)) //not required>
    });
}

Where addOns is a List<AddOn>, for example

var addOns = new List<AddOn> {
    new AddOn { 
        ID = "1", 
        Quantity = 5, 
        Variations = new List<Variation> 
        {
            new Variation { Code = "A" },
            new Variation { Code = "B" }
        } 
    },
    new AddOn { 
        ID = "2", 
        Quantity = 7, 
        Variations = new List<Variation> 
        {
            new Variation { Code = "B" },
            new Variation { Code = "C" }
        } 
    },
    new AddOn { 
        ID = "3", 
        Quantity = 9, 
        Variations = new List<Variation> 
        {
            new Variation { Code = "D" },
            new Variation { Code = "A" }
        } 
    },
};
like image 34
Arghya C Avatar answered Oct 20 '22 11:10

Arghya C


If I understood your problem correctly I think that the following method will help. I added a method within your Subscription class to group its add-ons by its Variation Codes and then sum the quantities of various codes. The key to my code is using SelectMany to break a single Add-On with multiple variants into separate elements that pair Code and Quantity. Then you can group the broken-up elements.

public Dictionary<string, int> GetAddonGroups()
{
    //spreading out each Add-on into multiple elements, each with a Code and quantity
    var addOnVariationsWithQuantity = this.AddOns
        .SelectMany(ad =>  ad.Variations
                    .Select(v => new {Code = v.Code, Quantity = ad.Quantity}));

    //group by Code and Sum the quantity
    var addOnGroups = addOnVariationsWithQuantity
                        .GroupBy(v => v.Code)
                        .ToDictionary(grp => grp.Key, 
                                      grp => grp.Sum( el => el.Quantity));
    return addOnGroups;
}

Forked Fiddle

like image 2
ryanyuyu Avatar answered Oct 20 '22 10:10

ryanyuyu