如何加入2个不同长度的列表。它应该与序列一起加入。 例如
{1,2,3,4} with {5,6,7}
我需要得到如下的结果。
{{1,5}, {2,6}, {3,7}, {4,null}}
我试过了。
var qry = a.Select((i, index) => new {i, j = b[index]});
但是它的抛出错误因为列表有不同的长度。 请帮我解决问题。
答案 0 :(得分:2)
丑陋但有效的版本如下:
a.Cast<int?>().Concat(Enumerable.Repeat<int?>(null, Math.Max(b.Count() - a.Count(), 0)))
.Zip(b.Cast<int?>()
.Concat(Enumerable.Repeat<int?>(null, Math.Max(a.Count() - b.Count(), 0))),
(x, y) => new { x, y });
它的缺点是对集合进行双重评估(第一个是通过调用.Count()
)。
所以最好只写一个扩展名
static IEnumerable<TResult> ZipNull<T1, T2, TResult>(this IEnumerable<T1> a, IEnumerable<T2> b, Func<T1?, T2?, TResult> func)
where T1 : struct
where T2 : struct
{
using (var it1 = a.GetEnumerator())
using (var it2 = b.GetEnumerator())
{
while (true)
{
if (it1.MoveNext())
{
if (it2.MoveNext())
{
yield return func(it1.Current, it2.Current);
}
else
{
yield return func(it1.Current, null);
}
}
else
{
if (it2.MoveNext())
{
yield return func(null, it2.Current);
}
else
{
break;
}
}
}
}
}
并将其用作
a.ZipNull(b, (x, y) => new { x, y });
答案 1 :(得分:1)
这应该有效:
var a = new int?[] { 1, 2, 3, 4 };
var b = new int?[] { 5, 6, 7 };
var result = Enumerable.Range(0, Math.Max(a.Count(), b.Count()))
.Select(n => new[] {a.ElementAtOrDefault(n), b.ElementAtOrDefault(n)});
请注意数组声明中的?
。为了在结果列表中具有空值,这是必要的。省略?
会导致结果为0而不是null
。
如果您不能或不想将数组声明为int?
,那么您必须在Select中执行强制转换:
var result = Enumerable.Range(0, Math.Max(a.Count(), b.Count()))
.Select(n => new[] { a.Select(i => (int?)i).ElementAtOrDefault(n), b.Select(i => (int?)i).ElementAtOrDefault(n) });
这第二段代码可以正常使用常规int
数组或列表。
答案 2 :(得分:0)
var x = new[] { 1, 2, 3, 4 }.ToList();
var y = new[] { 5, 6, 7 }.ToList();
var arrayLists = new[] {x, y}.OrderBy(t => t.Count).ToList();
var result = arrayLists
.Last()
.Select((item, i) => new[] { x[i], i < arrayLists.First().Count ? y[i] : (int?)null })
.ToList();
这适用于任何IEnumerable
答案 3 :(得分:0)
只需遍历列表并构建新的,让我们从每个列表元素中说出Dictionary<int?, int?>
:
var theFirstList = new List<int?> { 1, 2, 3, 4 };
var theSecondList = new List<int?> { 5, 6, 7 };
var el = new Dictionary<int?, int?>();
var length = Math.Max(theFirstList.Count, theSecondList.Count);
for (int i = 0; i < length; i++)
{
el.Add(theFirstList.ElementAtOrDefault(i), theSecondList.ElementAtOrDefault(i));
}
答案 4 :(得分:0)
你所拥有的实际上是一个Zip
,但它会拉伸到两个序列的较长而不是较短的末尾。您可以编写这样的Zip
方法,其内容看起来与实际的Zip实现类似:
public static IEnumerable<TResult> ZipAll<TSource, TSecond, TResult>(this IEnumerable<TSource> source,
IEnumerable<TSecond> other,
Func<TSource, TSecond, TResult> projection)
{
using (var firstIterator = source.GetEnumerator())
using (var secondIterator = other.GetEnumerator())
{
while (true)
{
bool hasFirst = firstIterator.MoveNext();
bool hasSecond = secondIterator.MoveNext();
TSource first = hasFirst ? firstIterator.Current : default(TSource);
TSecond second = hasSecond ? secondIterator.Current : default(TSecond);
if (hasFirst || hasSecond)
yield return projection(first, second);
else
yield break;
}
}
}
你可以这样写:
a.ZipAll(b, (i, j) => new { i, j });
你可以通过要求输入为列表来缩短代码,但代码不会像列表那样快,只需要更少的输入,并且它不像 那么多努力支持任何序列,所以我说它值得添加几行代码。