我有两个系列。
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只是将缺少的元素添加到相应的列表中。
此外,这只是一个测试实现。在生产环境中,Enumerables不会是同一类型。这旨在用于同步远程和本地存储。将来,Enumerable将首先是ICollection<DummyRemoteItem>
,而Enumerable将是List<IO.FileSystemInfo>
。但我希望它更通用。为了能够使用不同的集合,我想我会提出另一个类型参数和Func<T1, T2, bool>
来比较项目。那将是一种最好的方法,对吗?
通常,实现
内部的最佳方式是什么Synchronize<T>(IEnumerable<T> first,IEnumerable<T> second,Action<T> firstSynchronizer,Action<T> secondSynchronizer)
和
Synchronize<TFirst, TSecond>(IEnumerable<TFirst> first,IEnumerable<TSecond> second,Action<TFirst> firstSynchronizer,Action<TSecond> secondSynchronizer, Func<TFirst, TSecond, bool> predicate)
答案 0 :(得分:0)
您可以使用Except
和Intersect
方法查找两个可枚举来源之间的差异或相同的项目。 MSDN在Except和Intersect上都有很多资源。
至于关于Distinct()
性能价格昂贵的评论,我会怀疑点击量很小,尝试优化会过早地这样做。
答案 1 :(得分:0)
我想最优雅的方式是使用套装。在.NET中,HashSet可能就是你要找的东西,
答案 2 :(得分:0)
Linq full outer join是你的朋友。
这是一个实现(来自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();
}
这样:
a.FullOuterJoin(b,a=>a,b=>b,(a,b)=>new {a,b})
并在生成的可枚举中查找空值。
答案 3 :(得分:0)
如果集合中的项目属于两种不同类型,则可以使用以下内容:
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);
}
}
}
}
用法如下:
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);