【问题标题】:Can I rewrite this more elegantly using LINQ?我可以使用 LINQ 更优雅地重写它吗?
【发布时间】:2010-10-09 18:30:04
【问题描述】:

我有一个double[][],我想将其转换为 CSV 字符串格式(即一行中的每一行,行元素用逗号分隔)。我是这样写的:

public static string ToCSV(double[][] array)
{
    return String.Join(Environment.NewLine,
                       Array.ConvertAll(array,
                                        row => String.Join(",",
                                                           Array.ConvertAll(row, x => x.ToString())));
}

有没有更优雅的方式来使用 LINQ 编写这个?

(我知道,可以使用临时变量来使它看起来更好,但这种代码格式更好地传达了我正在寻找的内容。)

【问题讨论】:

  • 回复“我实际上正在尝试学习 LINQ”; LINQ 只是一个工具;也许学习 LINQ 最重要的一课就是什么时候不使用它?
  • 也许吧。我不会在实际应用程序中将 LINQ 用于这种方法,但是当人们需要考虑现实生活中的需求(而不是一些人为的示例)时,它更容易学习。
  • 重新发布您的测量结果:然后我怀疑您的测量结果有问题...愿意发布吗?
  • @Marc,当然。我将为它创建一个单独的问题。
  • 您接受的答案可以更能揭示意图,意味着更多的“什么”,更少的“如何”(LINQ 的主要好处之一)。详情见我的回答。

标签: c# linq linq-to-objects


【解决方案1】:

你也可以使用聚合

public static string ToCSV(double[][] array)
{
  return array.Aggregate(string.Empty, (multiLineStr, arrayDouble) =>
           multiLineStr + System.Environment.NewLine + 
           arrayDouble.Aggregate(string.Empty, (str, dbl) => str + "," + dbl.ToString()));
}

【讨论】:

    【解决方案2】:

    这与double 的任何嵌套序列兼容。它还将ToString 实现推迟到调用者,允许格式化同时避免混乱的IFormatProvider 重载:

    public static string Join(this IEnumerable<string> source, string separator)
    {
        return String.Join(separator, source.ToArray());
    }
    
    public static string ToCsv<TRow>(this IEnumerable<TRow> rows, Func<double, string> valueToString)
        where TRow : IEnumerable<double>
    {
        return rows
            .Select(row => row.Select(valueToString).Join(", "))
            .Join(Environment.NewLine);
    }
    

    【讨论】:

      【解决方案3】:

      你可以,但我个人不会一次完成所有行 - 我会使用迭代器块:

      public static IEnumerable<string> ToCSV(IEnumerable<double[]> source)
      {
          return source.Select(row => string.Join(",",
             Array.ConvertAll(row, x=>x.ToString())));        
      }
      

      这将返回每一行(然后调用者可以有效地WriteLine 等,而无需缓冲所有内容)。它现在也可以从double[] 行的任何来源(包括但不限于锯齿状数组)调用。

      另外 - 使用局部变量,您可以使用 StringBuilder 使每行稍微便宜一些。


      要一次返回整个字符串,我会优化它以使用单个StringBuilder 来处理所有字符串工作;有点冗长,但效率更高(中间字符串少得多):

      public static string ToCSV(IEnumerable<double[]> source) {
          StringBuilder sb = new StringBuilder();
          foreach(var row in source) {
              if (row.Length > 0) {
                  sb.Append(row[0]);
                  for (int i = 1; i < row.Length; i++) {
                      sb.Append(',').Append(row[i]);
                  }
              }
          }
          return sb.ToString();
      }
      

      【讨论】:

      • 谢谢。但只是为了学习,你怎么把它变成一个字符串? String.Join(Environment.NewLine, ToCSV(source).ToArray()) 例如?
      • 谢谢。我明白这一点,但效率不是我关心的问题。我实际上正在尝试学习 LINQ。 :)
      • 我也不太确定 StringBuilder 会对输出产生真正的影响。它可能会减少内存需求(由于为每一行分配了不需要的字符串),但 String.Join 的实现是非常有效的 IMO。
      • 但是你每行有一个 StringBuilder,而不是一个整体。它还避免了每行额外的数组。
      • 根据我的测量,String.Join 在这种情况下有时比StringBuilder 快 10%,有时慢约 1%(取决于数据)。 String.Join 方法在我看来是一个更好的选择,因为使用它更容易且不易出错。
      【解决方案4】:

      您可以使用 LINQ 来做到这一点,但我不确定您是否比您更喜欢这个。恐怕你不知道。 :)

      var q = String.Join(Environment.NewLine, (from a in d
                                            select String.Join(", ", (from b in a
                                                                      select b.ToString()).ToArray())).ToArray());
      

      干杯, 马蒂亚斯

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-01
        • 2021-03-23
        • 1970-01-01
        • 2011-04-14
        • 1970-01-01
        • 2011-01-30
        相关资源
        最近更新 更多