给定IComparer,Zip两个命令IEnumerable-s,只配对相同的元素?

时间:2013-04-09 13:07:55

标签: .net linq

我需要迭代两个有序的IEnumerable - s,ab,按给定的IComparer排序,“并排”和{{ 1}}相等的元素(根据相同的Zip相等)。

我需要IComparer所有元素在其他集合中没有匹配Zip(或null值,无论如何)。

default ping我的意思是“返回Zip个调用结果的集合,其中f()是一个给定的闭包,带有2个参数,一个来自f()和一个来自a“。

ba可以包含不同数量的元素,并且不必匹配1:1。

例如:

b

我希望IComparer comparer = ...; int[] a = { 1, 2, 4, 7, 7 }; int[] b = { -1, 1, 3, 4, 7, 8 }; var zipped = EvenMoreLinq.ZipEqual(a, b, comparer, (a, b) => new int[]{ a, b }); 为:

zipped

{ {0, -1}, {1, 1}, {2, 0}, {0, 3}, {4, 4}, {7, 7}, {7, 0}, {0, 8} }; a中的相等元素应与其他集合中的匹配元素匹配。

输出集合最好保持源顺序。

是否存在这样的库实现?

2 个答案:

答案 0 :(得分:4)

假设Jon的评论答案是“是”,那么实现可能如下所示:

public static IEnumerable<TResult> ZipEqual<TFirst, TSecond, TResult>(
    this IEnumerable<TFirst> first, IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> resultSelector,
    IComparer comparer)
{
    var enumerator1 = first.GetEnumerator();
    var enumerator2 = second.GetEnumerator();

    var enumerator1HasElement = enumerator1.MoveNext();
    var enumerator2HasElement = enumerator2.MoveNext();

    while(enumerator1HasElement || enumerator2HasElement)
    {
        if(!enumerator2HasElement)
        {
            yield return resultSelector(enumerator1.Current, default(TSecond));
            enumerator1HasElement = enumerator1.MoveNext();
        }
        else if(!enumerator1HasElement)
        {
            yield return resultSelector(default(TFirst), enumerator2.Current);
            enumerator2HasElement = enumerator2.MoveNext();
        }
        else
        {
            var compareResult = comparer.Compare(enumerator1.Current,
                                                 enumerator2.Current);
            if(compareResult == 0)
            {
                yield return resultSelector(enumerator1.Current,
                                            enumerator2.Current);
                enumerator1HasElement = enumerator1.MoveNext();
                enumerator2HasElement = enumerator2.MoveNext();
            }
            else if(compareResult < 0)
            {
                yield return resultSelector(enumerator1.Current,
                                            default(TSecond));
                enumerator1HasElement = enumerator1.MoveNext();
            }
            else
            {
                yield return resultSelector(default(TFirst),
                                            enumerator2.Current);
                enumerator2HasElement = enumerator2.MoveNext();
            }
        }
    }
}

答案 1 :(得分:1)

修改

可以避免分组,但结果显然与Daniel's answer.

类似
public static IEnumerable<Tuple<T, T>> ZipEqual<T>(
    this IEnumerable<T> source,
    IEnumerable<T> other,
    IComparer<T> comparer = null)
{
    if (other == null)
    {
        throw new ArgumentNullException("other");
    }

    if (comparer == null)
    {
        comparer = Comparer<T>.Default;
    }

    var first = source.OrderBy(t => t, comparer).GetEnumerator();
    var second = other.OrderBy(t => t, comparer).GetEnumerator();

    var firstMore = first.MoveNext();
    var secondMore = second.MoveNext();

    while (firstMore && secondMore)
    {
        var comp = comparer.Compare(first.Current, second.Current);

        if (comp == 0)
        {
            yield return Tuple.Create(first.Current, second.Current);
            firstMore = first.MoveNext();
            secondMore = second.MoveNext();
            continue;
        }

        if (comp > 0)
        {
             yield return Tuple.Create(default(T), second.Current);
             secondMore = second.MoveNext();
             continue;
        }

        yield return Tuple.Create(first.Current, default(T));
        firstMore = first.MoveNext();
    }

    while (firstMore)
    {
        yield return Tuple.Create(first.Current, default(T));
        firstMore = first.MoveNext();
    }

    while (secondMore)
    {
        yield return Tuple.Create(default(T), second.Current);
        secondMore = second.MoveNext();
    }
}

怎么样,

public static IEnumerable<Tuple<T, T>> ZipEqual<T>(
    this IEnumerable<T> source,
    IEnumerable<T> other,
    IComparer<T> comparer = null)
{
    if (other == null)
    {
        throw new ArgumentNullException("other");
    }

    if (comparer == null)
    {
        comparer = Comparer<T>.Default;
    }

    var orderedGroups =
        source.Select(t => new { Value = t, First = true })
            .Concat(other.Select(t => new { Value = t, First = false }))
            .ToLookup(a => a.Value)
            .OrderBy(l => l.Key, comparer);

    foreach (var group in orderedGroups)
    {
        var firsts = group.Where(a => a.First).Select(a => a.Value).ToList();
        var seconds = group.Where(a => !a.First).Select(a => a.Value).ToList();

        var limit = Math.Max(firsts.Count, seconds.Count);
        for (var i = 0; i < limit; i++)
        {
            yield return Tuple.Create(
                firsts.ElementAtOrDefault(i),
                seconds.ElementAtorDefault(i));
        }
    }
}