2

我有两个系列。

  var a = new List<string>() { "a", "b", "c", "d", "e", "f", "j" };
  var b = new List<string>() { "a", "c", "d", "h", "i" };

而且我想对该项目执行一些操作,以防它在一个或另一个集合中丢失。

public static Synchronize<T>(IEnumerable<T> first, IEnumerable<T> second, Action<T> firstSynchronizer, Action<T> secondSynchronizer)
{
  var firstUnique = first.Distinct();
  var secondUnique = second.Distinct();
  foreach (var item in firstUnique)
  {
    if (!secondUnique.Contains(item)) firstSynchronizer(item);
  }
  foreach (var item in second.Distinct())
  {
    if (!firstUnique.Contains(item)) secondSynchronizer(item);
  }
}

这就是我得到的,但我对此并不满意。我不禁想知道是否有更好的方法来实现这一点,因为我认为对Distinct()性能的影响很大,而且我不确定是否最好迭代整个第二个 Enumerable 并检查第一个 Enumerable 中是否不存在项目(像上面一样)还是迭代会更好second.Except(first)?你们有什么感想?

我这样称呼它:

  var a = new List<string>() { "a", "b", "c", "d", "e", "f", "j" };
  var b = new List<string>() { "a", "c", "d", "h", "i" };
  Synchronize(a.ToArray(), b.ToArray(), t => b.Add(t), t => a.Add(t));

我这样称呼ToArray()集合在被迭代时不会被改变,而 lambdas 只是将缺失的元素添加到相应的列表中。

Also, this is just a test implementation. In production environment, Enumerables won't be of same type. This is intended to be used to sync remote and local storage. In future, Enumerable first will be for example ICollection<DummyRemoteItem> and Enumerable second will be List<IO.FileSystemInfo>. But I want it to be more generic. To make it possible to work with different collections, I think I would propose another type parameter and a Func<T1, T2, bool> for comparing items. That would be a best approach, right?

Generally, what's the best way to implement insides of

Synchronize<T>(IEnumerable<T> first,IEnumerable<T> second,Action<T> firstSynchronizer,Action<T> secondSynchronizer) 

and

Synchronize<TFirst, TSecond>(IEnumerable<TFirst> first,IEnumerable<TSecond> second,Action<TFirst> firstSynchronizer,Action<TSecond> secondSynchronizer, Func<TFirst, TSecond, bool> predicate)
4

4 回答 4

0

You can use the Except and Intersect methods to find the differences or identical items between two enumerable sources. MSDN has a lot of resources on both Except and Intersect.

As for the comment about Distinct() having an expensive performance hit I would suspect that the hit is minor and trying to optimize would be to do so prematurely.

于 2012-07-20T10:57:30.460 回答
0

I guess the most elegant way would be to use sets. In .NET HashSet might be what you are looking for,

http://msdn.microsoft.com/en-us/library/bb495294.aspx

于 2012-07-20T10:58:46.897 回答
0

Linq full outer join is your friend here.

Here's an implementation (from here)

public static IEnumerable<Tuple<T1, T2>> FullOuterJoin<T1, T2>
   (this IEnumerable<T1> one, IEnumerable<T2> two, Func<T1,T2,bool> match)
{
 var left = from a in one
   from b in two.Where((b) => match(a, b)).DefaultIfEmpty()
   select new Tuple<T1, T2>(a, b);

 var right = from b in two
   from a in one.Where((a) => match(a, b)).DefaultIfEmpty()
   select new Tuple<T1, T2>(a, b);

 return left.Concat(right).Distinct();
}

so:

a.FullOuterJoin(b,a=>a,b=>b,(a,b)=>new {a,b})

and look for nulls in the resulting enumerable.

于 2012-07-20T10:59:45.403 回答
0

The following could be used if the items in collections are of two different types:

 class CollectionSynchronizer<TSource, TDestination>
    {
        public Func<TSource, TDestination, bool> CompareFunc { get; set; }
        public Action<TDestination> RemoveAction { get; set; }
        public Action<TSource> AddAction { get; set; }
        public Action<TSource, TDestination> UpdateAction { get; set; }

        public void Synchronizer(ICollection<TSource> sourceItems, ICollection<TDestination> destinationItems)
        {
            // Remove items not in source from destination
            RemoveItems(sourceItems, destinationItems);

            // Add items in source to destination 
            AddOrUpdateItems(sourceItems, destinationItems);
        }

        private void RemoveItems(ICollection<TSource> sourceCollection, ICollection<TDestination> destinationCollection)
        {
            foreach (var destinationItem in destinationCollection.ToArray())
            {
                var sourceItem = sourceCollection.FirstOrDefault(item => CompareFunc(item, destinationItem));

                if (sourceItem == null)
                {
                    RemoveAction(destinationItem);
                }
            }
        }

        private void AddOrUpdateItems(ICollection<TSource> sourceCollection, ICollection<TDestination> destinationCollection)
        {
            var destinationList = destinationCollection.ToList();
            foreach (var sourceItem in sourceCollection)
            {
                var destinationItem = destinationList.FirstOrDefault(item => CompareFunc(sourceItem, item));

                if (destinationItem == null)
                {
                    AddAction(sourceItem);
                }
                else
                {
                    UpdateAction(sourceItem, destinationItem);
                }
            }
        }
    }

And the usage would be like this:

var collectionSynchronizer = new CollectionSynchronizer<string, ContentImageEntity>
            {
                CompareFunc = (communityImage, contentImage) => communityImage == contentImage.Name,
                AddAction = sourceItem =>
                {
                    var contentEntityImage = _contentImageProvider.Create(sourceItem);
                    contentEntityImages.Add(contentEntityImage);
                },
                UpdateAction = (communityImage, contentImage) =>
                {
                    _contentImageProvider.Update(contentImage);
                },
                RemoveAction = contentImage =>
                {
                    contentEntityImages.Remove(contentImage);
                }
            };

            collectionSynchronizer.Synchronizer(externalContentImages, contentEntityImages);
于 2018-11-09T15:11:40.437 回答