I need to intersect 2 arrays (List<T>
) by keeping just the distinct elements and mix the elements of one array with the other like this:
A1 B1 C1 D1
W2 X2 Y2 Z2
=> A1 W2 B1 X2 C1 Y2 D1 Z2
A1 B1 A1 D1
W2 X2 Y2 Z2
=> A1 W2 B1 X2 Y2 D1 Z2 // repeating A1
A1 B1 A1 D1
Z2 X2 Y2 Z2
=> A1 Z2 B1 X2 Y2 D1 开发者_如何转开发// repeating A1, Z2
A1 B1 C1 D1 E1
Z2 X2 Y2
=> A1 Z2 B1 X2 C1 Y2 D1 E1
What is the optimal LINQ query to do it?
(Note that List<T>
is not an array, by the way.)
Well, I think this will do what you want, if you're using .NET 4 - and assuming the arrays are the same length:
var query = first.Zip(second, (x, y) => new[] { x, y })
.SelectMany(pair => pair)
.Distinct();
EDIT: As pointed out in the comments, the ordering of Distinct
is not guaranteed. However, I expect it to actually stay the same in practice... given other changes I've suggested which affect observable results only in a positive (and tiny) way, I can't imagine this is going to change.
lasseespeholt's solution deals with the interleaving part. If you want absolutely guaranteed ordering, you can always implement Disrinct yourself, much like I did in Edulinq:
public static IEnumerable<TSource> OrderPreservingDistinct<TSource>(
this IEnumerable<TSource> source)
{
return source.OrderPreservingDistinct(EqualityComparer<TSource>.Default);
}
public static IEnumerable<TSource> OrderPreservingDistinct<TSource>(
this IEnumerable<TSource> source,
IEqualityComparer<TSource> comparer)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
return OrderPreservingDistinct(source, comparer ??
EqualityComparer<TSource>.Default);
}
private static IEnumerable<TSource> OrderPreservingDistinct<TSource>(
IEnumerable<TSource> source,
IEqualityComparer<TSource> comparer)
{
HashSet<TSource> seenElements = new HashSet<TSource>(comparer);
foreach (TSource item in source)
{
if (seenElements.Add(item))
{
yield return item;
}
}
}
Then:
var result = firstList.Interleave(secondList).OrderPreservingDistinct();
An interleave extension method (replaces Zip
/SelectMany
in Jon Skeets version). This may be a little faster and it includes the remaining of the longest list if the lists aren't of equal length:
public static IEnumerable<T> Interleave<T>(this IEnumerable<T> first, IEnumerable<T> second)
{
var e0 = first.GetEnumerator();
var e1 = second.GetEnumerator();
bool e0HasValue = false;
bool e1HasValue = false;
// Could be made a little faster by not checking both when one is empty
while ((e0HasValue = e0.MoveNext()) | (e1HasValue = e1.MoveNext()))
{
if (e0HasValue)
yield return e0.Current;
if (e1HasValue)
yield return e1.Current;
}
}
And if you want distinct items, use:
var result = firstList.Interleave(secondList).Distinct();
This answer, of cause, doesn't solve the uncertainty about Distinct
as described in comments to Jon Skeet´s answer. But it could be combined with Jon Skeet´s order preserving extension method.
Maybe not use only LINQ but i think use is simply and enough faster.
//List<string> l1 = new List<string>() { "A1", "B1", "C1", "D1" };
List<string> l1 = new List<string>() { "A1", "B1", "A1", "D1" };
//List<string> l2 = new List<string>() { "W2", "X2", "Y2", "Z2" };
List<string> l2 = new List<string>() { "Z2", "X2", "Y2", "Z2" };
List<string> ls = new List<string>();
ls.AddRange(l1);
ls.AddRange(l2);
ls = ls.Distinct().ToList();
精彩评论