0

我需要迭代两个有序的IEnumerable-sab,由给定的IComparer、“并排”和Zip相等的元素(根据相同的IComparer)排序。

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

通过ping 我的意思是“返回调用结果Zip的集合,其中一个给定的闭包采用 2 个参数,一个来自,一个来自”。f()f()ab

a并且b可以有不同数量的元素,并且不必匹配 1:1。

例如:

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和中的相等元素b应该与另一个集合中的匹配元素一样多。

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

是否存在这样的库实现?

4

2 回答 2

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();
            }
        }
    }
}
于 2013-04-09T13:15:47.133 回答
1

编辑

可以避免分组,但结果显然与丹尼尔的答案相似。

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));
        }
    }
}
于 2013-06-05T11:57:05.923 回答