一个IEnumerable<object[]>和一个IEnumerable<object>[]之间的优雅转换

4
有没有方法可以将 IEnumerable<int[]> 转换为 IEnumerable<int>[]?具体来说,我有一个如下的 IEnumerable:
    IEnumerable<int[]> data = new List<int[]>()
    {
    new int[]{1,11},
    new int[]{2,22},
    new int[]{3,33},
    new int[]{4,44},
    // ...
    // ...
    };

我希望将其转化为以下形式:

    IEnumerable<int>[] data = new List<int>[] 
    { 
    new List<int>(){1,2,3,4 },
    new List<int>(){11,22,33,44}
    };

我目前想到的唯一解决方案如下:

public static IEnumerable<int>[] Convert(IEnumerable<int[]> data)
{
    var length = data.First().Length;
    var output = new List<int>[length];
    for (int i = 0; i < length; i++)   
        output[i] = new List<int>();
    foreach (var entry in data) 
    {
        for (int i = 0; i < length; i++)    
        {
            output[i].Add(entry[i]);
        }
    }
    return output;
}

这种方法并非理想,因为我需要遍历整个数据集。期望的解决方案是利用LINQ或内置的迭代器模式功能(yield return)。有没有更好的解决方法?

2个回答

2

如果您不介意多次枚举 data,您可以这样做:

public static IEnumerable<int>[] Convert(IEnumerable<int[]> data)
{
    var length = data.First().Length;
    var output = new IEnumerable<int>[length];
    for (int i = 0; i < length; i++)
    {
        output[i] = CustomEnumerable(i);
    }

    return output;

    IEnumerable<int> CustomEnumerable(int index)
    {
        foreach (var entry in data) 
        {
            yield return entry[index];
        }
    }
}

正如您所看到的,我没有填充列表,而是返回自定义的 IEnumerable<int>,这些是使用本地迭代器函数(使用 yield return)创建的。

如果您关心仅对 data 进行一次迭代,您可以像这样做:

IEnumerable<int>[] Convert(IEnumerable<int[]> data)
{
    var found = new List<int[]>();
    using var enumerator = data.GetEnumerator();
    var proxy = ProxyEnumerable();
    
    var length = proxy.First().Length;
    var output = new IEnumerable<int>[length];
    for (int i = 0; i < length; i++)
    {
        output[i] = CustomEnumerable(i);
    }

    return output;

    IEnumerable<int> CustomEnumerable(int index)
    {
        foreach (var entry in proxy) 
        {
            yield return entry[index];
        }
    }
    
    IEnumerable<int[]> ProxyEnumerable()
    {    
        foreach (var value in found)
        {
            yield return value;
        }

        while (enumerator.MoveNext())
        {
            var value = enumerator.Current;
            found.Add(value);
            yield return value;
        }
    }
}

在这里,我添加了另一个 IEnumerable<int[]>,它会填充一个缓存 List<int[]>,当我们迭代它时。因此,data 只被迭代一次,并且随后的迭代使用缓存。

在线试用


第二个提出的解决方案正是我渴望的。非常感谢。 - Pepa Zdepa

0

我在LINQ方面有一个简短的建议。通过下面的示例,您可以获得一个交错数组

var maxLength = data.Max(x => x.Length);
var converted = new IEnumerable<int>[maxLength]
    .Select((dst, index) => data
        .Select(src => src.Length > index ? src[index] : default)
        .Where(n => n != default));

通过这个方法,您可以将源数组切割成矩阵

var minLength = data.Min(x => x.Length);
var converted = new IEnumerable<int>[minLength]
    .Select((dst, index) => data
        .Select(src => src[index]));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接