使用Zip()合并不同长度的数组而不会丢失值

时间:2015-05-05 03:38:08

标签: c# .net arrays linq

在以下代码中,我正在合并两个类型intstring的数组。第一个的长度大于第二个长度,因此,最后一个索引(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);
}

我想知道一种让它合并的方法。例如,在null中存在的最后一个字符串之后创建words或空字符串,并使用它与最后的numbers索引合并。无法理解。

修改 结果我得到了

1one
2two
3three
4four

我想要的结果

1one
2two
3three
4four
5

谢谢!

编辑:不重复,我的另一个问题是关于在null对象上调用方法。

3 个答案:

答案 0 :(得分:3)

您可以轻松编写自己的类似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);
                }
            }
        }
    }
}

但是如果您的源始终是一对数组,那么简单地使用for循环可能更容易:

public static IEnumerable<TResult> ZipWithDefault<TFirst, TSecond, TResult>(this TFirst[] first, TSecond[] second, Func<TFirst, TSecond, TResult> selector)
{
    var maxLength = Math.Max(first.Length, second.Length);

    for(var i = 0; i < maxLength; i++)
    {
        var firstItem = i < first.Length ? first[i] : default(TFirst);
        var secondItem = i < second.Length ? second[i] : default(TSecond);
        yield return selector(firstItem, secondItem);

    }
}

答案 1 :(得分:2)

您可以在压缩它们之前扩展两个集合中较小的一个,例如像这样的东西:

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);
}

理想情况下,您希望将其包装在实用程序方法中并且是通用的,因此它适用于任何压缩的集合。

看起来某人已在此Programmers StackExchange answer上编写了一个通用实现。

答案 2 :(得分:-1)

这是一个非对称解决方案,它使用第一个序列作为锚点,在可用时将第二个序列中的相应元素配对,否则使用默认值。不需要长度计算,也不需要对可枚举项进行早期强制评估。

向 IEnumerable 添加扩展:

    <!-- https://mvnrepository.com/artifact/org.bytedeco/ffmpeg-platform -->
    <dependency>
        <groupId>org.bytedeco</groupId>
        <artifactId>ffmpeg-platform</artifactId>
        <version>4.3.2-1.5.5</version>
    </dependency>


 /**
 * Convert the file into MP4 using H264 Codac in order to make it work in IOS Mobile Device
 * @param file
 * @param outputFile
 */
private void convertToM4A(File file, File outputFile) {
    try {
        String ffmpeg = Loader.load(org.bytedeco.ffmpeg.ffmpeg.class);
        ProcessBuilder pb = new ProcessBuilder(ffmpeg, "-i", file.getPath(), "-vcodec", "h264", outputFile.getPath());
        pb.inheritIO().start().waitFor();
    }catch (Exception e ){
        e.printStackTrace();
    }
}

在压缩之前使用扩展名“填充”第二个序列:

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);
     }
}