开发者

Mix 2 arrays using LINQ

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜