我能否使用LINQ更优雅地重写这个问题?

3

我有一个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最重要的一课是什么时候不使用它? - Marc Gravell
也许吧。在真实应用中,我不会使用LINQ来实现这个方法,但是当一个人需要考虑一个真实的需求时(而不是一些人为的例子),使用LINQ更容易学习。 - Hosam Aly
1
@Marc,好的。我会为此创建一个单独的问题。 - Hosam Aly
@Bryan,是的,但你的答案使用了比LINQ更多的扩展方法。我可能需要考虑哪一个更“优雅”,因为这是我最初的问题... - Hosam Aly
好的,我被说服了。当涉及到“优雅”时,你的答案可能更“优雅”,但我希望我能再给@Mudu一票。 :) - Hosam Aly
显示剩余4条评论
4个回答

6

您可以这样做,但我个人不会一次性处理所有行 - 我会使用迭代器块:

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()) - Hosam Aly
谢谢。我明白,但效率不是我的关注点。我实际上正在尝试学习LINQ。 :) - Hosam Aly
我也不确定StringBuilder对输出会有真正的影响。它可能会减少内存需求(由于为每行分配不必要的字符串),但在我看来,String.Join的实现非常有效。 - Hosam Aly
但是这样你每行都有一个StringBuilder,而不是一个整体的。它还避免了每行多余的数组。 - Marc Gravell
根据我的测量,在这种情况下,String.Join 有时比 StringBuilder 快10%左右,有时慢约1%(取决于数据)。对我来说,String.Join 方法似乎是更好的选择,因为使用它更容易且不容易出错。 - Hosam Aly

2
你也可以使用聚合函数。
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()));
}

1
你可以用LINQ做到,但我不确定你是否比自己喜欢这个更好。我担心你不会。 :)
var q = String.Join(Environment.NewLine, (from a in d
                                      select String.Join(", ", (from b in a
                                                                select b.ToString()).ToArray())).ToArray());

祝好, Matthias


1

这个适用于任何嵌套的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);
}

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