Reputation: 2185
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();
Upvotes: 0
Views: 85
Reputation: 117019
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();
Upvotes: 2
Reputation: 4903
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.
Upvotes: 3