jvoigt
jvoigt

Reputation: 432

using linq to merge items together in a collection

I have a list of objects (call them type salesItems) Lets say these items have 50 properties, with Name, price, quantity being 3 of them). I would like to know how to merge the list together combining any salesItems by name using the following logic:

If there are multiple salesOrders that have the same Name:

I would like to do with linq. I realize i could use a big for each c# loop instead.

If there are additional items in the list I would like to follow similar logic for those as well.

EX:    A salesOrder list with (A,B,C,D,E)

A: Name=ball  Price= 2.24  Quantity=1   (other values = bla bla)
B: Name= ball  Price = 15.33  Quantity=3   (other values)
c: Name= bat  Price = 22.14  Quantity=3    (other values)
D: Name= bat Price= 19.22 Quantity=2    (other values)
E: Name = ball Price=4.32 Quantity=2   (other values)

Result list I want 2 Sales orders in list (A,C) A: Name=ball Price= 2.24 Quantity=6 (other values = bla bla from a's properties) c: Name= bat Price = 22.14 Quantity=5 (other values from c's properties)

Upvotes: 2

Views: 4804

Answers (2)

Ben
Ben

Reputation: 5715

You want linq's .GroupBy method!!!

I've defined your class as:

public class SalesOrder
{   
    public string Name { get; set; }
    public double Price { get; set; }
    public int Quantity { get; set; }

    public SalesOrder(string Name, double Price, int Quantity)
    {
        this.Name = Name;
        this.Price = Price;
        this.Quantity = Quantity;
    }
}

then I have created a list of your orders like this:

List<SalesOrder> Orders = new List<SalesOrder>()
{
    new SalesOrder("Ball", 2.24, 1),
    new SalesOrder("Ball", 15.33, 3),
    new SalesOrder("Bat", 22.14, 3),
    new SalesOrder("Bat", 19.22, 2),
    new SalesOrder("Ball", 4.32, 2)
};

and grouped them by the name before selecting the values you want for each group into a new instance of the SalesOrder class like this:

List<SalesOrder> Combined_Orders = Orders
    .GroupBy (o => o.Name)
    .Select (o => new SalesOrder(o.Key, o.Select (x => x.Price).First(), o.Sum(x => x.Quantity)))
    .ToList();

UPDATE: In response to OP's comment

As the real SalesOrder will have hundreds of properties, you can avoid typing them all out in the linq query by adding a constructor to the SalesOrder class that accepts the result of the group by as an argument, then do all the work in the constructor. While it doesn't stop you from having to type out all the properties, it does mean that its neatly abstracted away. Also this way it forces/enables you to decide on what to do with each of the properties (first/sum/average).

To do this you will need a second constructor that looks like this:

    public SalesOrder(IGrouping<string, SalesOrder> Group)
    {
        this.Name = Group.Key;
        this.Price = Group.First().Price;
        this.Quantity = Group.Sum(g => g.Quantity);
        // do all other properties here too
    }

Then update the group by to look like this (note that only the result of the grouping "g" is passed into the constructor now):

List<SalesOrder> Combined_Orders = Orders
    .GroupBy (o => o.Name)
    .Select (g => new SalesOrder(g))
    .ToList();

Upvotes: 7

vikasse
vikasse

Reputation: 455

Hi You can use the following code,

class SalesItem
{
    public string Name { get; set; }
    public int Price { get; set; }
    public int Quantity { get; set; }
}

class SalesOrder
{
    public void LoadItems()
    {
        List<SalesItem> SalesItems = new List<SalesItem>();

        SalesItem salesitem = new SalesItem()
        {
            Name = "Ball",
            Price = 12,
            Quantity = 1
        };

        SalesItems.Add(salesitem);

        salesitem = new SalesItem()
        {
            Name = "Ball",
            Price = 36,
            Quantity = 3
        };

        SalesItems.Add(salesitem);

        salesitem = new SalesItem()
        {
            Name = "Bat",
            Price = 50,
            Quantity = 1
        };

        SalesItems.Add(salesitem);

        salesitem = new SalesItem()
        {
            Name = "Ball",
            Price = 84,
            Quantity = 7            
        };

        SalesItems.Add(salesitem);

        salesitem = new SalesItem()
        {
            Name = "Bat",
            Price = 150,
            Quantity = 3
        };

        SalesItems.Add(salesitem);

        GroupOrders(SalesItems);
    }

    public List<SalesItem> GroupOrders(List<SalesItem> SalesItems)
    {
        var list = from item in SalesItems
                   group item by item.Name into orders
                   select new SalesItem
                   {
                       Name = orders.Key,
                       Price = orders.Sum(X=>X.Price),
                       Quantity = orders.Sum(X=>X.Quantity)
                   };

        List<SalesItem> resultList = new List<SalesItem>();
        foreach (SalesItem saleitem in list)
        {
            resultList.Add(saleitem);
        }
        return resultList;
    }
}

Upvotes: 0

Related Questions