I have already thought about how I'm going to solve this by rolling my own solution, but I wondered if .NET already has the functionality for what I'm trying to acheive - if so, I'd rather use something built-in.
Suppose I have two instances of a Widget
object, let's call them PartA
and PartB
. The information from each has been garnered from two different web services, but both have matching IDs.
PartA
{
ID: 19,
name: "Percy",
taste: "",
colour: "Blue",
shape: "",
same_same: "but different"
}
PartB
{
ID: 19,
name: "",
taste: "Sweet",
colour: "",
shape: "Hexagon",
same_same: "but not the same"
}
I want to merge these to create the following:
Result
{
ID: 19,
name: "Percy",
taste: "Sweet",
colour: "Blue",
shape: "Hexagon",
same_same: "but different"
}
Notice how the value for same_same
differs between each, but we consider PartA the master, so the result retains the value but different
.
Now to complicate matters:
Suppose we have two lists:
List<Widget> PartA = getPartA();
List<Widget> PartB = getPartB();
Now here's some pseudocode describing what I want to do:
List<Widget> Result = PartA.MergeWith(PartB).MergeObjectsOn(Widget.ID).toList();
You could write your own extension method(s), something like this:
static class Extensions
{
public static IEnumerable<T> MergeWith<T>(this IEnumerable<T> source, IEnumerable<T> other) where T : ICanMerge
{
var otherItems = other.ToDictionary(x => x.Key);
foreach (var item in source)
{
yield return (T)item.MergeWith(otherItems[item.Key]);
}
}
public static string AsNullIfEmpty(this string s)
{
if (string.IsNullOrEmpty(s))
return null;
else
return s;
}
}
Where ICanMerge
is like:
public interface ICanMerge
{
object Key { get; }
ICanMerge MergeWith(ICanMerge other);
}
Implemented e.g. like:
public class Widget : ICanMerge
{
object ICanMerge.Key { get { return this.ID; } }
int ID {get;set;}
string taste {get;set;}
public ICanMerge MergeWith(ICanMerge other)
{
var merged = new Widget();
var otherWidget = (Widget)other;
merged.taste = this.taste.AsNullIfEmpty() ?? otherWidget.taste;
//...
return merged;
}
}
Then it's as simple as PartA.MergeWith(PartB).ToList()
.
If your lists are one for one (i.e. same number of items and each item in the PartA list has a match in the PartB list), then I would consider the Zip extension method. Note that Zip does not actually require each list to have same number of items. However, if you can't rely on "pairing up" items with matching IDs, then my simplistic approach won't work.
You could do something like this:
var alist = GetPartAWidgets().OrderBy(w => w.ID);
var blist = GetPartBWidgets().OrderBy(w => w.ID);
var merged = alist.Zip(blist, (a,b) => new Widget()
{
ID = a.ID,
Name = string.IsNullOrEmpty(a.Name) ? b.Name : a.Name,
//etc.
});
If you want your linq to look cleaner, you could encapsulate the individual Widget merging logic in a function or extension method and use that instead of the inline delegate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With