I use the following code to get the changes between two collections. Objects are "joined" using a primary key. Any tips on performance issues or other optimizations appreciated.
/// <summary>
/// Gets the changes [Deleted, changed, inserted] comparing this collection to another.
/// </summary>
/// <param name="local">The source collection.</param>
/// <param name="remote">The remote collection to comare agains.</param>
/// <param name="keySelector">The primary key selector function</param>
/// <returns></returns>
public static ChangeResult<TSource> CompareTo<TSource, TKey>(this IEnumerable<TSource> local, IEnumerable<TSource> remote, Func<TSource, TKey> keySelector)
{
if (local == null)
throw new ArgumentNullException("local");
if (remote == null)
throw new ArgumentNullException("remote");
if (keySelector == null)
throw new ArgumentNullException("keySelector");
var remoteKeyValues = remote.ToDictionary(keySelector);
var deleted = new List<TSource>();
var changed = new List<TSource>();
var localKeys = new HashSet<TKey>();
foreach (var localItem in local)
{
var localKey = keySelector(localItem);
localKeys.Add(localKey);
/* Check if primary key exists in both local and remote
* and if so check if changed, if not it has been deleted
*/
TSource changeCandidate;
if (remoteKeyValues.TryGetValue(localKey, out changeCandidate))
{
if (!changeCandidate.Equals(localItem))
changed.Add(changeCandidate);
}
else
{
deleted.Add(localItem);
}
}
var inserted = remoteKeyValues
.Where(x => !localKeys.Contains(x.Key))
.Select(x => x.Value)
.ToList();
return new ChangeResult<TSource>(deleted, changed, inserted);
}
/// <summary>
/// Immutable class containing changes
/// </summary>
public class ChangeResult<T>
{
public ChangeResult(IList<T> deleted, IList<T> changed, IList<T> inserted)
{
Deleted = new ReadOnlyCollection<T>(deleted);
Changed = new ReadOnlyCollection<T>(changed);
Inserted = new ReadOnlyCollection<T>(inserted);
}
public IList<T> Deleted { get; private set; }
public IList<T> Changed { get; private set; }
public IList<T> Inserted { get; private set; }
}
Usage
var changes = Col1.CompareTo(Col2, x => x.UniqueId);
local
parameter is null twice and not checking theremote
parameter at all. Looks like a simple typo/copy and paste. \$\endgroup\$ – Stuart Leyland-Cole Oct 22 '13 at 8:44local
andremote
to be? How many changes, insertions and deletions would you expect? \$\endgroup\$ – breischl Oct 23 '13 at 18:02local
andremote
will be around 250.000 items, the nr. of changes may vary considerably, but should not be more than around 2%. \$\endgroup\$ – Magnus Oct 23 '13 at 19:52