using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
public int ItemNumber { get; set; }
public DateTime ItemDate { get; set; }
public List<ItemsLine> ItemsLines { get; set; } = new List<ItemsLine>();
public void MergeItem( Items sourceItem)
var mergedItems = ItemsLines.FullOuterJoin(sourceItem.ItemsLines, i => i.ItemLineId, i => i.ItemLineId, (i1, i2) =>
i1.Cost = (i1.Quantity * i1.Cost + i2.Quantity * i2.Cost) / (i1.Quantity + i2.Quantity);
i1.Quantity = i1.Quantity + i2.Quantity;
ItemsLines.AddRange(mergedItems);
public int ItemLineId { get; set; }
public int Quantity { get; set; }
public double Cost { get; set; }
public ItemsLine(int itemLineId, int quantity, double cost)
this.ItemLineId = itemLineId;
this.Quantity = quantity;
public static void Test()
Console.WriteLine("\nTesting code from question:");
Console.WriteLine("\nTesting with merge required:");
private static void MergeItem()
item1.ItemsLines.Add(new ItemsLine()
item2.ItemsLines.Add(new ItemsLine()
item2.ItemsLines.Add(new ItemsLine()
Console.WriteLine("Grand total after merge = {0}", item1.ItemsLines.Sum(i => i.Cost * i.Quantity));
public static void TestTestWithMerge()
item1.ItemsLines.Add(new ItemsLine()
item1.ItemsLines.Add(new ItemsLine()
item2.ItemsLines.Add(new ItemsLine()
item2.ItemsLines.Add(new ItemsLine()
item2.ItemsLines.Add(new ItemsLine()
var originalQualtity1 = item1.ItemsLines.Sum(i => i.Quantity);
var originalQualtity2 = item2.ItemsLines.Sum(i => i.Quantity);
Console.WriteLine("Merged items: ");
Console.WriteLine(JsonConvert.SerializeObject(item1, Formatting.Indented));
Console.WriteLine("Grand total after merge = {0}", item1.ItemsLines.Sum(i => i.Cost * i.Quantity));
Assert.IsTrue(item1.ItemsLines.Count == 4);
Assert.AreEqual(1, item1.ItemsLines.Where(i => i.ItemLineId == 101).Count());
Assert.AreEqual(2, item1.ItemsLines.Where(i => i.ItemLineId == 101).Single().Quantity);
Assert.AreEqual(1.0, item1.ItemsLines.Where(i => i.ItemLineId == 101).Single().Cost);
Assert.AreEqual(originalQualtity1 + originalQualtity2, item1.ItemsLines.Sum(i => i.Quantity));
public static class Ext {
public static IEnumerable<TResult> LeftOuterJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) {
return from left in leftItems
join right in rightItems on leftKeySelector(left) equals rightKeySelector(right) into temp
from right in temp.DefaultIfEmpty()
select resultSelector(left, right);
public static IEnumerable<TResult> RightOuterJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) {
return from right in rightItems
join left in leftItems on rightKeySelector(right) equals leftKeySelector(left) into temp
from left in temp.DefaultIfEmpty()
select resultSelector(left, right);
public static IEnumerable<TResult> FullOuterJoinDistinct<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) {
return leftItems.LeftOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector).Union(leftItems.RightOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector));
public static IEnumerable<TResult> RightAntiSemiJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) {
var hashLK = new HashSet<TKey>(from l in leftItems select leftKeySelector(l));
return rightItems.Where(r => !hashLK.Contains(rightKeySelector(r))).Select(r => resultSelector(default(TLeft),r));
public static IEnumerable<TResult> FullOuterJoin<TLeft, TRight, TKey, TResult>(
this IEnumerable<TLeft> leftItems,
IEnumerable<TRight> rightItems,
Func<TLeft, TKey> leftKeySelector,
Func<TRight, TKey> rightKeySelector,
Func<TLeft, TRight, TResult> resultSelector) where TLeft : class {
return leftItems.LeftOuterJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector).Concat(leftItems.RightAntiSemiJoin(rightItems, leftKeySelector, rightKeySelector, resultSelector));
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];
It sounds as though you want to do a [full outer join](https: