Best algorithm for synchronizing two IList in C# 2.0

前端 未结 6 1041
忘了有多久
忘了有多久 2020-12-10 05:12

Imagine the following type:

public struct Account
{
    public int Id;
    public double Amount;
}

What is the best algorithm to synchroniz

相关标签:
6条回答
  • 2020-12-10 05:23

    I had the same problem, and my best solution was the following (adapted to your case), having both lists loaded:

    1. For each Account in L1, verify if it exists in L2:
      • If found, update all values from L1's Account based on L2's. Then, delete the Account from L2.
      • If not found, mark L1's Account as deleted, or delete it from the list, it depends on how your database is structured.
    2. For each Account left in L2, add it into L1.

    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);
    
    0 讨论(0)
  • 2020-12-10 05:24

    L2 = L1.clone()?

    ... but I would guess you forgot to mention something.

    0 讨论(0)
  • 2020-12-10 05:25

    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

    0 讨论(0)
  • 2020-12-10 05:28

    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?

    0 讨论(0)
  • 2020-12-10 05:29

    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.

    0 讨论(0)
  • 2020-12-10 05:30

    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.

    0 讨论(0)
提交回复
热议问题