【问题标题】:How do I flatten an array of arrays?如何展平一组数组?
【发布时间】:2018-12-13 12:51:36
【问题描述】:

我有一个由以下元素组成的数组:

var schools = new [] {
    new object[]{ new[]{ "1","2" }, "3","4" },
    new object[]{ new[]{ "5","6" }, "7","8" },
    new object[]{ new[]{ "9","10","11" }, "12","13" }
};

我试图展平的真正对象是将数据从 CSV 导入数组数组,然后将其加入字段值:

    var q =
        from c in list
        join p in vocatives on c.Line[name1].ToUpper() equals p.first_name.ToUpper() into ps
        from p in ps.DefaultIfEmpty()
        select new object[] { c.Line,  p == null ? "(No vocative)" : p.vocative, p == null ? "(No sex)" : p.sex }; 

我想展平该字符串数组以获得:

string[] {
    new string[]{ "1","2","3","4" },
    new string[]{ "5","6","7","8" },
    new string[]{ "9","10","11","12","13" }
}

我已经有一个可以循环执行此操作的解决方案,它在性能方面并不那么明智,但它似乎可以正常工作。

我尝试使用SelectMany,但无法提出解决方案。

非常感谢您的反馈;) 我已经尝试过 npo 的回答:

var result = schools.Select(z => z.SelectMany(y=> y.GetType().IsArray 
           ? (object[])y : new object[] { y })
);

但 CSVwriter 类方法只接受显式类型:

IEnumerable<string[]>

那么如何在 linq 中做到这一点,我尝试过:

List<string[]> listOflists = (List<string[]>)result;

但不行,很遗憾,InvalidCastException 出现了。

【问题讨论】:

  • 听起来像是一道作业题。
  • 因为您将字符串数组与字符串混合,所以我认为不会有一个简单的 linq 解决方案。我认为您需要进行更传统的循环检查每个项目是数组还是字符串,并根据该检查进行处理。
  • "我尝试过使用 SelectMany" 然后请显示您尝试过的内容。我怀疑如果有人在这里找到与您已经拥有的完全相同的解决方案并将其发布在此处,它会对您有所帮助。
  • 一件事:我在您的示例中注意到始终存在一个字符串数组,并且它始终是对象数组中的第一项。这是一个规则还是只是您的测试数据中的巧合?
  • 如果这种结构是由您的应用程序或数据“自然”生成的(即不是拼图或家庭作业),请考虑对其进行改造。虽然您可以编写代码来处理它(如答案所示),但部分困难在于,就 O-O 而言,这种结构的类型相当糟糕。例如,如果将单个字符串表示为单元素数组,则整个内容自然会适合string[][][]。或者,封装这些字符串所代表的数据的适当类也会有所帮助(例如,string Valuestring[] Children)。

标签: c# arrays linq


【解决方案1】:

第一步,您必须将数据标准化为一种类型。然后你可以随意迭代它们。所以首先创建一个方法来将特定点的值展平到任意深度:

public static class Extensions
{
    public static IEnumerable<object> FlattenArrays(this IEnumerable source)
    {
        foreach (var item in source)
        {
            if (item is IEnumerable inner
                && !(item is string))
            {
                foreach (var innerItem in inner.FlattenArrays())
                {
                    yield return innerItem;
                }
            }

            yield return item;
        }
    }
}

现在您可以在顶层进行迭代以获得所有值的单个数组:

// Produces one array => ["1", "2", "3", "4", ...]
var allFlat = schools.FlattenArrays().OfType<string>().ToArray();

或者您可以创建更深一层的单个数组:

foreach (var item in schools)
{
    // Produces an array for each top level e.g. ["5", "6", "7", "8"]
    var flat = item.FlattenArrays().OfType<string>().ToArray();
}

【讨论】:

    【解决方案2】:

    根据 cmets,由于您的内部数组混合了 string[]string 的元素,因此直接在 Linq 中执行此操作可能并非易事。

    但是,在辅助函数(我称为 Flattener)的帮助下,您可以手动对两种内部类型的处理进行分支,以返回数组中的元素(如果是 string[]),或者如果不是,则将单个元素作为可枚举返回。 SelectMany 然后可用于展平内部级别,但您似乎希望不展平外部级别:

    var schools = new [] {
        new object[]{new[]{"1","2"}, "3","4"}, 
        new object[]{new[]{"5","6"}, "7","8"},
        new object[]{new[]{"9","10","11"}, "12","13"}
    };
    
    var result = schools
        .Select(s => s.SelectMany(o => Flattener(o)));
    

    返回IEnumerable&lt;IEnumerable&lt;string&gt;&gt;的类型

    其中凌乱的拆包位由:

    public IEnumerable<string> Flattener(object o)
    {
        if (o is IEnumerable<string> strings)
        {
            return strings;
        }
        if (o is string s)
        {
           return new[]{s};
        }
        return new[]{"?"};
    }
    

    请注意,上面使用了 C#7 的模式匹配功能。

    结果截图由 LinqPad 提供:

    【讨论】:

      【解决方案3】:

      如果你想通过 linq 来做,这里有一个示例

           var schools = new[] {
          new object[]{new[]{"1","2"}, "3","4"},
          new object[]{new[]{"5","6"}, "7","8"},
          new object[]{new[]{"9","10","11"}, "12","13"}
      };
      
      var result = schools.Select(z => z.SelectMany(y=> y.GetType().IsArray ? (object[])y : new object[] { y }));
      

      【讨论】:

      • 证明可以在 LINQ 中完成并不意味着它必须在 LINQ 中完成。不过很好理解。
      • 请注意,将数组转换为 object[] 仅在数组为引用类型时才有效。 int[] 无法转换为 object[]。更通用的解决方案是... y=&gt; y is IEnumerable ? ((IEnumerable)y).Cast&lt;object&gt;() : new object[] { y } 但是,要小心; string 实现了 IEnumerable&lt;char&gt;,因此您可能需要在其中测试字符串性!
      【解决方案4】:

      提出的解决方案致力于转换任何类型的int 数组、常规、锯齿状、嵌套这些最后取自 javascript 及其对象表示法,但它们也可以在 C# 中实现为复杂的交错对象数组),转换为简单的一维整数数组。

      要使您的请求适应它,您应该只需将对象锯齿数组的 string 类型元素更改为 int 类型。

      这里是 C# 函数:

          public static int[] getFlattenedIntArray(object jaggedArray)
          {
      
              var flattenedArray = new List<int>();
      
              var jaggedArrayType = jaggedArray.GetType();
              var expectedType = typeof(int);
      
              if (jaggedArrayType.IsArray)
              {
                  if (expectedType.IsAssignableFrom(jaggedArrayType.GetElementType()))
                  {
                      foreach (var el in jaggedArray as int[])
                      {
                          flattenedArray.Add(el);
                      }
                  }
                  else
                  {
                      foreach (var el in jaggedArray as object[])
                      {
                          foreach (var retIntEl in getFlattenedIntArray(el))
                          {
                              flattenedArray.Add(retIntEl);
                          }
                      }
      
                  }
              }
              else if (jaggedArrayType == expectedType)
              {
                  flattenedArray.Add((int)jaggedArray);
              }
              else
              {
                  return new int[0];
              }
      
              return flattenedArray.ToArray();
          }
      

      试试这个小提琴:https://dotnetfiddle.net/5HGX96

      【讨论】:

        猜你喜欢
        • 2015-07-21
        • 2011-02-03
        • 1970-01-01
        • 1970-01-01
        • 2021-10-24
        • 2019-11-17
        • 2014-08-29
        • 2022-10-24
        • 2011-11-03
        相关资源
        最近更新 更多