简体   繁体   中英

Comparing two list of objects and modifying a property

I'm attempting to compare two lists of objects and create a brand new list from them.

I have a class named Item.

public class Item
{
    public string ItemNumber;
    public string OptionNumber;
    public int Count;
}

I have two lists of Items - processedItems and currentItems.

List<Item> processedItems = new List<Item>();  
List<Item> currentItems = new List<Item>();

I want a list of processedItems.

However, if the ItemNumber and OptionNumber exists in currentItems, I want the Count property to be the sum of the two value (the value in processedItems and currentItems).

Example expected and actual values.

processedItems.Add(new Item() { ItemNumber = "123456", OptionNumber = "123", Count = 1 });
processedItems.Add(new Item() { ItemNumber = "123456", OptionNumber = "126", Count = 2 });
processedItems.Add(new Item() { ItemNumber = "112233", OptionNumber = "111", Count = 1 });
processedItems.Add(new Item() { ItemNumber = "112244", OptionNumber = "222", Count = 4 });

currentItems.Add(new Item() { ItemNumber = "123456", OptionNumber = "123", Count = 1 });
currentItems.Add(new Item() { ItemNumber = "123456", OptionNumber = "126", Count = 2 });
currentItems.Add(new Item() { ItemNumber = "998877", OptionNumber = "111", Count = 1 });
currentItems.Add(new Item() { ItemNumber = "112244", OptionNumber = "222", Count = 0 });

From the above example, I'm trying to get the following output:

List<Item> output = new List<Item>();

output.Add(new Item() { ItemNumber = "123456", OptionNumber = "123", Count = 2 });
output.Add(new Item() { ItemNumber = "123456", OptionNumber = "126", Count = 4 });
output.Add(new Item() { ItemNumber = "112233", OptionNumber = "111", Count = 1 });
output.Add(new Item() { ItemNumber = "112244", OptionNumber = "222", Count = 4 });

Do I need to use a Concat? I've currently got something like this

var output = processedItems.Select(x => new Item
{
    ItemNumber = x.ItemNumber,
    OptionValue = x.OptionValue,
    Count = x.Count + (currentItems.FirstOrDefault(y => y.ItemNumber == x.ItemNumber && y.OptionValue == x.OptionValue).Count)
}).Concat(currentItems
    .Where(x => processedItems.Any(y => y.ItemNumber == x.ItemNumber && y.OptionValue == x.OptionValue)))
    .Select(x => new Item
    {
        ItemNumber = x.ItemNumber,
        OptionValue = x.OptionValue,
        Count = x.Count
    })
    .ToList();

You could use GroupJoin , and select elements from processedItems , by using currentItems just for the sum of count between joined elements, like the following code:

var output =
    processedItems
        .GroupJoin(
            currentItems,
            prd => new { prd.ItemNumber, prd.OptionNumber },
            sel => new { sel.ItemNumber, sel.OptionNumber },
            (prd, sel) => new Item
            {
                ItemNumber = prd.ItemNumber,
                OptionNumber = prd.OptionNumber,
                Count = sel.Sum(s => s.Count) + prd.Count
            })
        .ToList();

Demo :

foreach (Item item in output)
{
    Console.WriteLine($"ItemNumber:{item.ItemNumber}, OptionNumber:{item.OptionNumber}, Count:{item.Count}");
}

Result

ItemNumber:123456, OptionNumber:123, Count:2
ItemNumber:123456, OptionNumber:126, Count:4
ItemNumber:112233, OptionNumber:111, Count:1
ItemNumber:112244, OptionNumber:222, Count:4

I hope this helps you fix the issue.

I often prefer to use .ToLookup in situations like this:

var lookup = currentItems.ToLookup(x => new { x.ItemNumber, x.OptionNumber }, x => x.Count);

var output =
    processedItems
        .Select(x => new Item
        {
            ItemNumber = x.ItemNumber,
            OptionNumber = x.OptionNumber,
            Count = x.Count + lookup[new {x.ItemNumber, x.OptionNumber }].Sum()
        })
        .ToList();

Alternatively, a group join is the right way to go.

var output =
(
    from x in processedItems
    join y in currentItems
        on new { x.ItemNumber, x.OptionNumber }
        equals new { y.ItemNumber, y.OptionNumber }
        into gys
    select new Item()
    {
        ItemNumber = x.ItemNumber,
        OptionNumber = x.OptionNumber,
        Count = x.Count + gys.Sum(z => z.Count)
    }
).ToList();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM