C# 使用Zip()合并不同长度的数组而不丢失值
在下面的代码中,我将合并两个类型为C# 使用Zip()合并不同长度的数组而不丢失值,c#,.net,arrays,linq,C#,.net,Arrays,Linq,在下面的代码中,我将合并两个类型为int和string的数组。第一个索引的长度大于第二个索引的长度,因此,最后一个索引(即5)不会合并: int[] numbers = new[] { 1, 2, 3, 4, 5 }; string[] words = new string[] { "one", "two", "three", "four" }; var numbersAndWords = numbers.Zip(words, (n, w) => new { Number = n, Wo
int
和string
的数组。第一个索引的长度大于第二个索引的长度,因此,最后一个索引(即5
)不会合并:
int[] numbers = new[] { 1, 2, 3, 4, 5 };
string[] words = new string[] { "one", "two", "three", "four" };
var numbersAndWords = numbers.Zip(words, (n, w) => new { Number = n, Word = w });
foreach (var nw in numbersAndWords)
{
Console.WriteLine(nw.Number + nw.Word);
}
我想知道合并的方法。例如,在words
中存在的最后一个null
字符串之后创建一个null或空字符串,并使用它与最后一个数字
索引合并。我想不出来
编辑:
我得到的结果
1one
2two
3three
4four
我想要的结果
1one
2two
3three
4four
5
谢谢
编辑:不是重复,我的另一个问题是关于对空对象调用方法。在压缩之前,您可以扩展两个集合中较小的集合,例如:
int[] numbers = new[] { 1, 2, 3, 4, 5 };
string[] words = new string[] { "one", "two", "three", "four" };
IEnumerable<string> wordsExtended = words;
if(words.Length < numbers.Length)
{
wordsExtended = words.Concat(Enumerable.Repeat("", numbers.Length - words.Length));
}
var numbersAndWords = numbers.Zip(wordsExtended, (n, w) => new { Number = n, Word = w });
foreach (var nw in numbersAndWords)
{
Console.WriteLine(nw.Number + nw.Word);
}
int[]number=new[]{1,2,3,4,5};
字符串[]字=新字符串[]{“一”、“二”、“三”、“四”};
IEnumerable words趋势=单词;
if(字.长度<数字.长度)
{
wordsExtended=words.Concat(可枚举的重复(“,number.Length-words.Length));
}
var numbersAndWords=numbers.Zip(wordsTrend,(n,w)=>new{Number=n,Word=w});
foreach(数值和文字中的变量nw)
{
控制台写入线(nw.Number+nw.Word);
}
理想情况下,您希望将其封装在实用程序方法中,并且是泛型的,以便它适用于任何压缩的集合
看起来有人已经为此编写了一个通用实现。您可以轻松编写自己的类似LINQ的扩展方法:
public static class MyEnumerable
{
public static IEnumerable<TResult> ZipWithDefault<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector)
{
bool firstMoveNext, secondMoveNext;
using (var enum1 = first.GetEnumerator())
using (var enum2 = second.GetEnumerator())
{
while ((firstMoveNext = enum1.MoveNext()) & (secondMoveNext = enum2.MoveNext()))
yield return selector(enum1.Current, enum2.Current);
if (firstMoveNext && !secondMoveNext)
{
yield return selector(enum1.Current, default(TSecond));
while (enum1.MoveNext())
{
yield return selector(enum1.Current, default(TSecond));
}
}
else if (!firstMoveNext && secondMoveNext)
{
yield return selector(default(TFirst), enum2.Current);
while (enum2.MoveNext())
{
yield return selector(default(TFirst), enum2.Current);
}
}
}
}
}
这是一种非对称解决方案,它使用第一个序列作为锚,在可用时将第二个序列中的对应元素配对,或者使用默认值。不需要计算长度,也不需要提前强制计算枚举数
向IEnumerable添加扩展名:
public static class EnumerableExtensions {
public static IEnumerable<T> PadRight<T>(this IEnumerable<T> s)
{
foreach (var t in s)
yield return t;
while (true)
yield return default(T);
}
}
您链接到的答案的问题是,它将导致源集合上出现多个枚举(因为Count
调用。当使用数组作为源时,这并不重要,但当使用不同的源时,这可能很重要。这只有在您事先知道哪些输入较短时才起作用。它要求您通过调用此PadRight()显式处理较短的输入)
方法。与公认的答案相比,这似乎是一个明显较差的解决方案。我无法想象有人会选择它而不是更好、更通用的解决方案。事实上,这个答案几乎与相同,有许多相同的缺点。感谢建设性的批评。我将您的评论作为参考注意。我确实指出,建议的解决方案不需要强制枚举惰性序列(以计算长度)。因此,对于某些应用程序,它可能更有效。“建议的解决方案不需要强制枚举惰性序列”--当然可以,但公认的解决方案也不需要,也不需要像您的解决方案那样提前知道哪个序列会更短。
public static class EnumerableExtensions {
public static IEnumerable<T> PadRight<T>(this IEnumerable<T> s)
{
foreach (var t in s)
yield return t;
while (true)
yield return default(T);
}
}
int[] numbers = new[] { 1, 2, 3, 4, 5 };
string[] words = new string[] { "one", "two", "three", "four" };
var numbersAndWords = numbers.Zip(words.PadRight(), (n, w) => new { Number = n, Word = w });
foreach (var nw in numbersAndWords)
{
Console.WriteLine(nw.Number + nw.Word ?? string.Empty);
}