【问题标题】:Elegant transformation between an IEnumerable<object[]> and an IEnumerable<object>[]IEnumerable<object[]> 和 IEnumerable<object>[] 之间的优雅转换
【发布时间】:2021-06-30 00:04:33
【问题描述】:

有没有办法将 IEnumerable&lt;int[]&gt; 转换为 IEnumerable&lt;int&gt;[] ?具体来说,我有以下 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)。有没有更好的办法解决这个问题?

【问题讨论】:

    标签: c# arrays iterator enumerable


    【解决方案1】:

    如果你不介意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&lt;int&gt;s,而不是填充列表,它们是使用本地迭代器函数(使用 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&lt;int[]&gt;,当我们迭代它时,它正在填充缓存 List&lt;int[]&gt;。这样data 只迭代一次,后续迭代使用缓存。

    Try it online.

    【讨论】:

    • 第二个提议的解决方案正是我所渴望的。非常感谢。
    【解决方案2】:

    我对 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]));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多