How to compare and merge lists using linq or moreLinq

457 Views Asked by At

I have two lists of type KeyValuePair.

Values are as following

List A
2019-01-01 1
2019-01-02 0
2019-01-03 1

List B
2019-01-01 0
2019-01-03 1

I want to merge these two list so it looks like this

List C 

2019-01-01 0 //item from list B 
2019-01-02 0 //item from list A, missing date in List B
2019-01-03 1 //item from list A - items are the same in list A and B

Is there a way with Linq or MoreLinq that can actually do this, ie.

  • merge items from two lists
  • use item from list A if same item does not exist in list B
  • replace item from list A with item from list B if they are not equal
3

There are 3 best solutions below

0
Jon Hanna On BEST ANSWER

If you know you can never have a default DateTime as the key:

var r = from a in ListA
join b in ListB on a.Key equals b.Key into joined
from b in joined.DefaultIfEmpty()
select b.Key != default(DateTime) ? b : a;

If you might have a default DateTime then convert the keys to a DateTime? and back again to detect the missing case reliably:

var r = from a in ListA.Select(kv => new KeyValuePair<DateTime?, int>(kv.Key, kv.Value))
join b in ListB.Select(kv => new KeyValuePair<DateTime?, int>(kv.Key, kv.Value))
on a.Key equals b.Key into joined
from b in joined.DefaultIfEmpty()
select new KeyValuePair<DateTime, int>(b.Key ?? a.Key.GetValueOrDefault(), b.Value);

Note that in both we skip the rule "replace item from list A with item from list B if they are not equal" with just "use the item from B if it exists" because we have to check if B exists anyway, and if B's value is the same as A's then it doesn't matter that we still used it.

I'd probably just build a dictionary from A and then replace the values with those from B unless I really cared about order, though.

5
Derviş Kayımbaşıoğlu On

I formulated something like this. Please check:

var q =
    from a in ListA
    join b in ListB on a.Dt equals b.Dt into j
    from b in j.DefaultIfEmpty()
    select new { Date=a.dt, Val = a.val == b?.val ? a.val : (b?.val ?? a.val)  };
0
Maciej Los On

Please, read my comment to the question first.

Another way is to use Union with GroupBy

var result = ListB
    .Union(ListA)
    .GroupBy(kvp=>kvp.Key)
    .Select(grp=> new KeyValuePair<DateTime, int>(grp.Key, grp.Select(v=>v.Value).First()))
    .OrderBy(kvp=>kvp.Key);

Note #1: i think it's less efficient than join.

Note #2: there's probably more ways to achieve that, i.e via using: Zip, SkipWhile, TakeWhile etc.

Note #3: i've made above example to show how useful, flexible and funny is Linq ;)