Reputation: 353
I want to know if it's possible in LINQ to achieve something along the lines of:
newList: { [1], [2] }
oldList: { [2], [3], [4], [5] }
resultantList = { [1], [2, 2], [3], [4], [5] }
Ok that was an oversimplification. Lets say:
Class A
{
public string Name;
public IList<B> Items;
public bool Equals(A obj)
{
return obj.Name == Name;
}
}
newList<A> = {{ Name = "A", Items[1]}, { Name = "B", Items[1] }}
oldList<A> = {{ Name = "C", Items[2]}, { Name = "A", Items[2] }, { Name = "D", Items[1] }, { Name = "E", Items[1] },}
mergedList<A> = {{ Name = "A", Items[3]}, { Name = "B", Items[1]}, { Name = "C", Items[2]}, { Name = "D" , Items[1]}, { Name = "E" , Items[1]}}
Notice that for the instance with Name="A" the list is actually the merged list of both lists. Order doesn't matter and in reality the equality is more complex.
I wish to achieve this on types (i.e. below works, but would be inefficient):
var fragments = newGeometryFragments.Except(oldGeometryFragments).ToList();
fragments.AddRange(oldGeometryFragments.Except(newGeometryFragments).ToArray());
var mergedFragments = (from newGeometry in newGeometryFragments
from oldGeometry in oldGeometryFragments
where newGeometry.Equals(oldGeometry)
select MergeGeometryFragments(newGeometry, oldGeometry)).ToArray();
fragments.AddRange(mergedFragments);
Upvotes: 1
Views: 1949
Reputation: 9009
You can use the following code, assuming you have the oldList
and newList
already filled:
newList.Union(oldList) // unite the collections
.GroupBy(x => x /*, ComparerInstance*/) // will group by unique elemens
.Select(x => x.ToList()) // or .ToArray(), convert each group to array or list
.ToList(); // return a list of lists/arrays
The code above will produce a list of collections. Each collection will have one or more equal elements, depending on how many times that element is present in newList.Union(oldList)
.
If you are using specific elements and want to control the way they are compared (determined equal), pass a custom IEqualityComparer<YourType>
instance to the GroupBy
method.
Upvotes: 1
Reputation: 460208
You could use an outer join:
var oldList = new List<int?> { 2, 3, 4, 5 };
var newList = new List<int?> { 1, 2 };
var result = from all in oldList.Union(newList).OrderBy(num => num)
join o in oldList on all equals o into gjOld
from oldOuter in gjOld.DefaultIfEmpty()
join n in newList on all equals n into gjNew
from newOuter in gjNew.DefaultIfEmpty()
select new { newVal = newOuter, oldVal = oldOuter };
This works also with a custom class when you implement Equals
and GetHashCode
or implement a custom IEqualityComparer<T>
. Then the Nullable<int>
trick is also unnecessary
newVal: 1 oldVal:
newVal: 2 oldVal: 2
newVal: oldVal: 3
newVal: oldVal: 4
newVal: oldVal: 5
Upvotes: 0