Imagine the following type:
public struct Account
{
public int Id;
public double Amount;
}
What is the best algorithm to synchroniz
I had the same problem, and my best solution was the following (adapted to your case), having both lists loaded:
I recommend implementing the IEquatable<>
interface in your Account class (or just override the Equals()
method) so it always compares IDs on methods that require comparison between objects:
public struct Account : IEquatable<Account>
{
public int Id;
public double Amount;
public bool Equals(Account other)
{
if (other == null) return false;
return (this.Id.Equals(other.Id));
}
}
The sync algorithm would be something like this (make sure both lists are initialized so no errors occurr):
L1.ForEach (L1Account =>
{
var L2Account = L2.Find(a => a.Id == L1Account.id);
// If found, update values
if (L2Account != null)
{
L1Account.Amount = L2Account.Amount;
L2.Remove(L2Account);
}
// If not found, remove it
else
{
L1.Remove(L1Account);
}
}
// Add any remaining L2 Account to L1
L1.AddRange(L2);
L2 = L1.clone()?
... but I would guess you forgot to mention something.
I know this is an old post but you should check out AutoMapper. It will do exactly what you want in a very flexible and configurable way.
AutoMapper
For a start I'd get rid of the mutable struct. Mutable value types are a fundamentally bad thing. (As are public fields, IMO.)
It's probably worth building a Dictionary so you can easily compare the contents of the two lists. Once you've got that easy way of checking for presence/absence, the rest should be straightforward.
To be honest though, it sounds like you basically want L2 to be a complete copy of L1... clear L2 and just call AddRange? Or is the point that you also want to take other actions while you're changing L2?
If your two lists are sorted, then you can simply walk through them in tandem. This is an O(m+n) operation. The following code could help:
class Program
{
static void Main()
{
List<string> left = new List<string> { "Alice", "Charles", "Derek" };
List<string> right = new List<string> { "Bob", "Charles", "Ernie" };
EnumerableExtensions.CompareSortedCollections(left, right, StringComparer.CurrentCultureIgnoreCase,
s => Console.WriteLine("Left: " + s), s => Console.WriteLine("Right: " + s), (x,y) => Console.WriteLine("Both: " + x + y));
}
}
static class EnumerableExtensions
{
public static void CompareSortedCollections<T>(IEnumerable<T> source, IEnumerable<T> destination, IComparer<T> comparer, Action<T> onLeftOnly, Action<T> onRightOnly, Action<T, T> onBoth)
{
EnumerableIterator<T> sourceIterator = new EnumerableIterator<T>(source);
EnumerableIterator<T> destinationIterator = new EnumerableIterator<T>(destination);
while (sourceIterator.HasCurrent && destinationIterator.HasCurrent)
{
// While LHS < RHS, the items in LHS aren't in RHS
while (sourceIterator.HasCurrent && (comparer.Compare(sourceIterator.Current, destinationIterator.Current) < 0))
{
onLeftOnly(sourceIterator.Current);
sourceIterator.MoveNext();
}
// While RHS < LHS, the items in RHS aren't in LHS
while (sourceIterator.HasCurrent && destinationIterator.HasCurrent && (comparer.Compare(sourceIterator.Current, destinationIterator.Current) > 0))
{
onRightOnly(destinationIterator.Current);
destinationIterator.MoveNext();
}
// While LHS==RHS, the items are in both
while (sourceIterator.HasCurrent && destinationIterator.HasCurrent && (comparer.Compare(sourceIterator.Current, destinationIterator.Current) == 0))
{
onBoth(sourceIterator.Current, destinationIterator.Current);
sourceIterator.MoveNext();
destinationIterator.MoveNext();
}
}
// Mop up.
while (sourceIterator.HasCurrent)
{
onLeftOnly(sourceIterator.Current);
sourceIterator.MoveNext();
}
while (destinationIterator.HasCurrent)
{
onRightOnly(destinationIterator.Current);
destinationIterator.MoveNext();
}
}
}
internal class EnumerableIterator<T>
{
private readonly IEnumerator<T> _enumerator;
public EnumerableIterator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
MoveNext();
}
public bool HasCurrent { get; private set; }
public T Current
{
get { return _enumerator.Current; }
}
public void MoveNext()
{
HasCurrent = _enumerator.MoveNext();
}
}
You'll have to be careful about modifying the collections while iterating over them, though.
If they're not sorted, then comparing every element in one with every element in the other is O(mn), which gets painful really quickly.
If you can bear to copy the key values from each collection into a Dictionary or similar (i.e. a collection with acceptable performance when asked "is X present?"), then you could come up with something reasonable.
In addition to Jon Skeet's comment make your Account struct a class and override the Equals() and GetHashCode() method to get nice equality checking.