我需要迭代两个有序的IEnumerable
- s,a
和b
,按给定的IComparer
排序,“并排”和{{ 1}}相等的元素(根据相同的Zip
相等)。
我需要IComparer
所有元素在其他集合中没有匹配Zip
(或null
值,无论如何)。
按default
ping我的意思是“返回Zip
个调用结果的集合,其中f()
是一个给定的闭包,带有2个参数,一个来自f()
和一个来自a
“。
b
和a
可以包含不同数量的元素,并且不必匹配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
中的相等元素应与其他集合中的匹配元素匹配。
输出集合最好保持源顺序。
是否存在这样的库实现?
答案 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));
}
}
}