For logging purposes, I need to output a double[,] to the log file. So I need to represent the array as a string.
The following code gets the job done using basic C# 1 features, but I was wondering if there is a more elegant solution using Linq:
private static string OneRowPerLine(double[,] numbers)
{
var ret = new StringBuilder();
for(var i=numbers.GetLowerBound(0);i<=numbers.GetUpperBound(0);i++)
{
for (var j = numbers.GetLowerBound(1); j <= numbers.GetUpperBound(1); j++)
{
if(j>numbers.GetLowerBound(1))
{
ret.Append(",");
}
ret.Append(numbers[i, j]);
}
ret.Append(Environment.NewLine);
}
return ret.ToString();
}
2 Answers 2
First off, I think there may be bugs in your code. Specifically in the dimension parameters of some of the GetLowerBound()
parameters. Based on a quick glance, it should be:
private static string OneRowPerLine(double[,] numbers)
{
var ret = new StringBuilder();
for(var i=numbers.GetLowerBound(0);i<=numbers.GetUpperBound(0);i++)
{
for (var j = numbers.GetLowerBound(1); j <= numbers.GetUpperBound(1); j++)
{
if(j>numbers.GetLowerBound(1))
{
ret.Append(",");
}
ret.Append(numbers[i, j]);
}
ret.Append(Environment.NewLine);
}
return ret.ToString();
}
I'll update this answer in a moment with some enhancements, but wanted to get this bit out of the way.
EDIT: here's a pair of methods which will do it a little more generically and without LINQ. It does employ generics (<T>
) and extension methods (this
), one of which is an iterator (yield return
):
private static string OneRowPerLine<T>(this T[,] numbers)
{
return string.Join(Environment.NewLine, numbers.FormatOneRow());
}
private static IEnumerable<string> FormatOneRow<T>(this T[,] numbers)
{
for (var i = numbers.GetLowerBound(0); i <= numbers.GetUpperBound(0); i++)
{
var row = new T[numbers.GetLength(1)];
for (var j = numbers.GetLowerBound(1); j <= numbers.GetUpperBound(1); j++)
{
row[j] = numbers[i, j];
}
yield return string.Join(",", row);
}
}
call like this:
double[,] stuff = {{1,2}, {3,4}, {5,6}};
Console.WriteLine(stuff.OneRowPerLine());
"Better"? I don't know. There is a different way to deal with this in LINQ. You can use Cast<T>
to flatten an IEnumerable
and the use Aggregate
to generate a single string from all the elements in the enumerable. You can then "group" items by line depending on how far you are in the array. If you have a double[,]
, you might do something like this:
var text = numbers.Cast<double>()
.Aggregate(string.Empty, (s, i) =>
{
if ((temp%(numbers.GetUpperBound(1) + 1)) == 0)
{
s = s + Environment.NewLine;
}
temp++;
return s + i.ToString(CultureInfo.InvariantCulture) + ", ";
});
If you like StringBuilder
better:
var text = numbers.Cast<double>()
.Aggregate(new StringBuilder, (s, i) =>
{
if ((temp%(numbers.GetUpperBound(1) + 1)) == 0)
{
s.Append(Environment.NewLine);
}
temp++;
return s.Append( i.ToString(CultureInfo.InvariantCulture)).Append(", ");
}).ToString();
-
2\$\begingroup\$ I think the math here makes is non-obvious what the code actually does and whether it's correct. \$\endgroup\$svick– svick2012年08月30日 06:50:02 +00:00Commented Aug 30, 2012 at 6:50
-
\$\begingroup\$ Can you use a StringBuilder instead of a string in you solution? \$\endgroup\$Arne Lund– Arne Lund2012年08月30日 13:10:44 +00:00Commented Aug 30, 2012 at 13:10
-
\$\begingroup\$ @svick Hence my comment about "different" \$\endgroup\$Peter Ritchie– Peter Ritchie2012年08月30日 14:52:44 +00:00Commented Aug 30, 2012 at 14:52