简体   繁体   English

同步两个枚举

[英]Synchronizing two enumerables

I have two collections. 我有两个收藏。

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

And I would like to do some action on the item in case it's missing in one collection or another. 我想对这个项目做一些动作,以防一个或另一个集合中缺少它。

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);
  }
}

This is what I got but I am not happy with it. 这就是我得到的,但我对此不满意。 I can't help but wonder if there's a better way to implement this, because I think Distinct() is pretty big performance hit and also I am not sure if it's better to iterate whole second Enumerable and check if item is not present in first Enumerable already (like above) or if it would be better to iterate second.Except(first) ? 我忍不住想知道是否有更好的方法来实现这一点,因为我认为Distinct()会对性能造成很大的影响,并且我不确定是否最好迭代整个第二个Enumerable并检查是否第一个不存在已经可以枚举(如上),或者最好迭代second.Except(first) What do you guys think? 你们有什么感想?

I call it like this: 我这样称呼它:

  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));

I call ToArray() so collections don't get changed while being iterated over and lambdas just add missing elements to respective lists. 我调用ToArray()以便在迭代时不更改集合,而lambda只是将缺失的元素添加到相应的列表中。

Also, this is just a test implementation. 另外,这只是一个测试实现。 In production environment, Enumerables won't be of same type. 在生产环境中,Enumerables将不是同一类型。 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> . 将来,Enumerable首先是ICollection<DummyRemoteItem>而Enumerable第二个将是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. 为了使使用不同的集合成为可能,我想建议另一个类型参数和一个Func<T1, T2, bool>来比较项目。 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)

You can use the Except and Intersect methods to find the differences or identical items between two enumerable sources. 您可以使用ExceptIntersect方法查找两个可枚举来源之间的差异或相同项。 MSDN has a lot of resources on both Except and Intersect . MSDN在ExceptIntersect上都有很多资源。

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. 至于关于Distinct()的性能命中代价高昂的评论,我怀疑命中率是次要的,尝试进行优化可能会过早。

I guess the most elegant way would be to use sets. 我猜最优雅的方法是使用集合。 In .NET HashSet might be what you are looking for, 在.NET中,HashSet可能就是您想要的,

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

Linq full outer join is your friend here. Linq全外部加入是您的朋友在这里。

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. 并在结果枚举中查找null。

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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM