0

Here I have 2 lists of same object type.

object = {id: xxx, ...} // attribute "id" is used to find the identical obj

List oldSet = [old1, old2, old3];
List newSet = [new2, new3, new4];

// old2 = {id= 2, result = 5, ...}
// new2 = {id= 2, result = 1, ...}
// expected result = {oldSet: old2; newSet: new2}

I want to merge both lists, also keeping the origin of which list it came from.

The expected result as below:

List mergedSet = [{old1, null}, {old2, new2}, {old3, new3}, {null, new4}];

I'm thinking to use LINQ C# for it, but stuck somewhere.

Kindly advise.

Thanks! :)

pekcheng
  • 354
  • 1
  • 5
  • 14
  • 1
    ever heard of Dictionary ? or Multidimensional Arrays ? – iSR5 Jan 14 '19 at 03:30
  • 1
    What is the `Type` of `mergedSet`? Is it a `List>`? – Rufus L Jan 14 '19 at 03:57
  • I think the result Type can be optional. As long as I can tell this item is from oldSet or newSet, and combine the the ones with same id. THanks peeps for the hints – pekcheng Jan 14 '19 at 04:04
  • It would be awesome if you could provide a [mcve]. Your code, as is, does not compile. – mjwills Jan 14 '19 at 04:11
  • The following details are missing in your question to provide a useful answer: (1) Will the `oldSet` and `newSet` will have the items in the same order, ie if `id` of items in same index doesn't match is it safe to assume the item doesn't exist in the other list? (2)Whose sequence should be maintained? ie if both list has everything distinct and no single match for any item, what should be the output? – Mat J Jan 14 '19 at 06:09
  • @MatJ (1) nope, the sequence can be random. and yes, if one does not exists in the another list, it is safe to say it doesn't exists, and to return a null. (2) the objects sequence in list isn't important. The "sequence" mentioned in question is referring to the final result list, the first item will be from the oldSet, the second from the newSet. Hope this clarifies – pekcheng Jan 14 '19 at 06:26
  • Give an example of output required for two distinct lists. Like list1=[1,2,3,4,5], list2=[10,20,30]. What should be the output? – Mat J Jan 14 '19 at 06:41

5 Answers5

1

Here's some code that does what you want using Linq. It basically walks through all the old list, and adds pairs to the merged list by looking for matches from the new list (and adding null as the second item if no match was found). Then it walks through the remaining items in the new list and adds them with null for the first item. It selects a dynamic type with two properties: OldSet and NewSet, so you know where each item came from.

The merge code is simply:

var mergedSet = oldSet.Select(o =>
    new {OldSet = o, NewSet = newSet.FirstOrDefault(n => n.id == o.id)})
    .Concat(newSet.Where(n => oldSet.All(o => o.id != n.id)).Select(n =>
        new {OldSet = (Item) null, NewSet = n}));

This is based on the following item class:

class Item
{
    public int id { get; set; }
    public string result { get; set; }
    public override string ToString()
    {
        return $"{result}{id}";
    }
}

We create our lists:

List<Item> oldSet = new List<Item>
{
    new Item {id = 1, result = "old"},
    new Item {id = 2, result = "old"},
    new Item {id = 3, result = "old"},
};

List<Item> newSet = new List<Item>
{
    new Item {id = 2, result = "new"},
    new Item {id = 3, result = "new"},
    new Item {id = 4, result = "new"},
};

Run the merge code (very first snippet), and then display results:

foreach (var item in mergedSet)
{
    Console.WriteLine($"{item.NewSet},{item.OldSet}");
}

Output

![enter image description here

Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • This is way too much code for this task.... Linq exists specifically to do this kind of thing. There is no point in using tuples here, a couple of joins or a union will do just fine. – Drunken Code Monkey Jan 14 '19 at 04:51
  • You were right, I corrected my code, removed irrelevant comments... :) – Drunken Code Monkey Jan 14 '19 at 05:10
  • @DrunkenCodeMonkey Cool. I corrected mine also to reduce the merge code from 2 lines to one line (and made use of dynamic types as well) in case that's why someone downvoted. – Rufus L Jan 14 '19 at 05:42
0

Try something like this :

            List<string> oldSet = new List<string>() {"old1", "old2", "old3"};
            List<string> newSet = new List<string>() {"new2", "new3", "new4"};

            var results = oldSet.Select((x,i) => new { oldSet = x, newSet = newSet[i]}).ToList();
jdweng
  • 33,250
  • 2
  • 15
  • 20
0

You can left join the two lists. I edited the answer as you actually need to left join twice, union, and apply a select distinct to get the cases where oldSet = null and no duplicates...

    var mergedSet = (from o in oldSet
                     join n in newSet on o.id equals n.id into ns
                     from n in ns.DefaultIfEmpty()
                     select new { OldSet = o, NewSet = n })
                    .Union(from n in newSet
                           join o in oldSet on n.id equals o.id into os
                           from o in os.DefaultIfEmpty()
                           select new { OldSet = o, NewSet = n })
                    .Distinct();
Drunken Code Monkey
  • 1,796
  • 1
  • 14
  • 18
0

Might be an overkill, but if you really want to use LINQ

List<Item> oldSet = new List<Item>
{
    new Item {id = 1, result = "old"},
    new Item {id = 2, result = "old"},
    new Item {id = 3, result = "old"},
};

List<Item> newSet = new List<Item>
{
    new Item {id = 2, result = "new"},
    new Item {id = 3, result = "new"},
    new Item {id = 4, result = "new"},
};

var resultL = oldSet.GroupJoin(
          newSet, 
          o => o.id,
          n => n.id,
          (o,n) => new { Old = o, New = n })
    .SelectMany(
          n => n.New.DefaultIfEmpty(),
          (o,n) => new Tuple<Item,Item>(o.Old,n));


 var resultR= newSet.GroupJoin(
          oldSet, 
          n => n.id,
          o=> o.id,
          (n,o) => new { Old = o, New = n })
    .SelectMany(
          o=> o.Old.DefaultIfEmpty(),
          (n,o) => new Tuple<Item,Item>(o,n.New));

var result = resultL.Union(resultR).Distinct();
Anu Viswan
  • 17,797
  • 2
  • 22
  • 51
0

In this case, you have to use two GroupJoin and the Union the results. Look at the following code:

var res1 = oldSet.GroupJoin(newSet, o => o, k => k, (x, y) => { var yy = y.FirstOrDefault(); return new { X = x, Y = yy }; });
var res2 = newSet.GroupJoin(oldSet, o => o, k => k, (x, y) => { var yy = y.FirstOrDefault(); return new { X = yy, Y = x }; });
var result = res1.Union(res2).ToList();// Your result is here

enter image description here

Mohammad Nikravesh
  • 947
  • 1
  • 8
  • 27