C#には、数字のみを含むintの配列があります。この配列を文字列に変換したいです。
配列の例:
int[] arr = {0,1,2,3,0,1};
これを次の形式の文字列に変換するにはどうすればよいですか"012301"
?
at.net 3.5の使用:
String.Join("", new List<int>(array).ConvertAll(i => i.ToString()).ToArray());
at.net 4.0以降の使用:(@Jan Remundaの回答を参照)
string result = string.Join("", array);
String.Join 関数を使用するだけで、セパレータとしてstring.Empty
内部的にStringBuilder
を使用するため。
string result = string.Join(string.Empty, new []{0,1,2,3,0,1});
例:セミコロンをセパレータとして使用すると、結果は0;1;2;3;0;1
。
それは実際にヌルセパレータで動作し、2番目のパラメータは次のように任意のオブジェクトの列挙可能です。
string result = string.Join(null, new object[]{0,1,2,3,0,"A",DateTime.Now});
私の意見はおそらく人気のあるものではないことはわかっていますが、Linq-yのバンドワゴンに飛び乗るのは難しいと思います。気の利いた。凝縮されています。私はそれを理解し、適切な場所で使用することに反対しません。多分それは私だけかもしれませんが、人々は、必要なことを達成するためにユーティリティ関数を作成することを考えるのをやめ、代わりに、高密度の1ライナーを作成するために(時には)過度に長いLinqコードをコードに散らかすことを好むように感じます。
ここで人々が提供したLinqの答えが悪いと言っているわけではありませんが、さまざまな状況に対処する必要があるため、これらの単一行のコードが長くなり、より曖昧になり始める可能性があると思います。配列がnullの場合はどうなりますか?単なる連結ではなく、区切り文字列が必要な場合はどうでしょうか?配列内の整数の一部が2桁であり、各要素の文字列が残りと同じ長さになるように各値に先行ゼロを埋め込む場合はどうでしょうか?
例として提供された回答の1つを取り上げます。
result = arr.Aggregate(string.Empty, (s, i) => s + i.ToString());
配列がnullになることを心配する必要がある場合、次のようになります。
result = (arr == null) ? null : arr.Aggregate(string.Empty, (s, i) => s + i.ToString());
カンマ区切りの文字列が必要な場合は、次のようになります。
result = (arr == null) ? null : arr.Skip(1).Aggregate(arr[0].ToString(), (s, i) => s + "," + i.ToString());
これはまだそれほど悪くはありませんが、このコード行が何をしているのか一目で明らかではないと思います。
もちろん、このコード行を独自のユーティリティ関数にスローすることを妨げるものは何もありません。そのため、特に複数の場所で実行している場合は、アプリケーションロジックに長い混乱が混在することはありません。
public static string ToStringLinqy<T>(this T[] array, string delimiter)
{
// edit: let's replace this with a "better" version using a StringBuilder
//return (array == null) ? null : (array.Length == 0) ? string.Empty : array.Skip(1).Aggregate(array[0].ToString(), (s, i) => s + "," + i.ToString());
return (array == null) ? null : (array.Length == 0) ? string.Empty : array.Skip(1).Aggregate(new StringBuilder(array[0].ToString()), (s, i) => s.Append(delimiter).Append(i), s => s.ToString());
}
しかし、とにかくユーティリティ関数に入れようとするなら、それを1ライナーに凝縮する必要が本当にあるのでしょうか?その場合、明確にするためにいくつかの余分な行を入れて、StringBuilderを利用して、連結操作を繰り返さないようにします。
public static string ToStringNonLinqy<T>(this T[] array, string delimiter)
{
if (array != null)
{
// edit: replaced my previous implementation to use StringBuilder
if (array.Length > 0)
{
StringBuilder builder = new StringBuilder();
builder.Append(array[0]);
for (int i = 1; i < array.Length; i++)
{
builder.Append(delimiter);
builder.Append(array[i]);
}
return builder.ToString()
}
else
{
return string.Empty;
}
}
else
{
return null;
}
}
そして、パフォーマンスが本当に心配な場合は、string.Joinに参加するか、配列内の要素数に応じてStringBuilderを使用するかを決定するハイブリッド関数に変えることもできます(これはマイクロ最適化です。私の意見ではやる価値がなく、おそらく有益であるよりも有害ですが、この問題の例としてそれを使用しています):
public static string ToString<T>(this T[] array, string delimiter)
{
if (array != null)
{
// determine if the length of the array is greater than the performance threshold for using a stringbuilder
// 10 is just an arbitrary threshold value I've chosen
if (array.Length < 10)
{
// assumption is that for arrays of less than 10 elements
// this code would be more efficient than a StringBuilder.
// Note: this is a crazy/pointless micro-optimization. Don't do this.
string[] values = new string[array.Length];
for (int i = 0; i < values.Length; i++)
values[i] = array[i].ToString();
return string.Join(delimiter, values);
}
else
{
// for arrays of length 10 or longer, use a StringBuilder
StringBuilder sb = new StringBuilder();
sb.Append(array[0]);
for (int i = 1; i < array.Length; i++)
{
sb.Append(delimiter);
sb.Append(array[i]);
}
return sb.ToString();
}
}
else
{
return null;
}
}
この例では、パフォーマンスへの影響はおそらく気にする価値はありませんが、ポイントは、実際に操作のパフォーマンスに関係する必要がある状況にいる場合、それが何であれ、それはほとんどの場合です複雑なLinq式を使用するよりも、ユーティリティ関数内でそれを処理する方が簡単で読みやすいです。
そのユーティリティ関数は、まだ少し不格好に見えます。では、ハイブリッドなものを捨てて、これを実行しましょう。
// convert an enumeration of one type into an enumeration of another type
public static IEnumerable<TOut> Convert<TIn, TOut>(this IEnumerable<TIn> input, Func<TIn, TOut> conversion)
{
foreach (TIn value in input)
{
yield return conversion(value);
}
}
// concatenate the strings in an enumeration separated by the specified delimiter
public static string Delimit<T>(this IEnumerable<T> input, string delimiter)
{
IEnumerator<T> enumerator = input.GetEnumerator();
if (enumerator.MoveNext())
{
StringBuilder builder = new StringBuilder();
// start off with the first element
builder.Append(enumerator.Current);
// append the remaining elements separated by the delimiter
while (enumerator.MoveNext())
{
builder.Append(delimiter);
builder.Append(enumerator.Current);
}
return builder.ToString();
}
else
{
return string.Empty;
}
}
// concatenate all elements
public static string ToString<T>(this IEnumerable<T> input)
{
return ToString(input, string.Empty);
}
// concatenate all elements separated by a delimiter
public static string ToString<T>(this IEnumerable<T> input, string delimiter)
{
return input.Delimit(delimiter);
}
// concatenate all elements, each one left-padded to a minimum length
public static string ToString<T>(this IEnumerable<T> input, int minLength, char paddingChar)
{
return input.Convert(i => i.ToString().PadLeft(minLength, paddingChar)).Delimit(string.Empty);
}
現在、個別のかなりコンパクトなユーティリティ関数があり、それぞれが独自に有用であると主張できます。
最終的に、私のポイントは、Linqを使用するべきではないということではなく、単に小さくて、おそらく結果を返す1行のみを含む場合でも、独自のユーティリティ関数を作成する利点を忘れないでくださいLinqコードの行。他に何もない場合は、アプリケーションコードをLinqコードの行で達成できるよりもさらに凝縮した状態に保つことができます。また、複数の場所で使用している場合、ユーティリティ関数を使用すると出力を調整しやすくなります後で変更する必要がある場合に備えて。
この問題については、アプリケーションコードに次のようなものを書くだけです。
int[] arr = { 0, 1, 2, 3, 0, 1 };
// 012301
result = arr.ToString<int>();
// comma-separated values
// 0,1,2,3,0,1
result = arr.ToString(",");
// left-padded to 2 digits
// 000102030001
result = arr.ToString(2, '0');
余分な配列の作成を回避するには、次を実行できます。
var builder = new StringBuilder();
Array.ForEach(arr, x => builder.Append(x));
var res = builder.ToString();
string result = arr.Aggregate("", (s, i) => s + i.ToString());
(免責事項:桁数が(少なくとも数百)あり、パフォーマンスに関心がある場合は、JaredParの答えのように、このメソッドを避けてStringBuilder
を使用することをお勧めします。)
できるよ:
int[] arr = {0,1,2,3,0,1};
string results = string.Join("",arr.Select(i => i.ToString()).ToArray());
結果が得られます。
StringBuilder
をAggregate()
とともに使用するのが好きです。 「トリック」は、Append()
がStringBuilder
インスタンス自体を返すことです。
var sb = arr.Aggregate( new StringBuilder(), ( s, i ) => s.Append( i ) );
var result = sb.ToString();
私はこれを後世のためにここに残しましたが、ひどく読みにくいので、その使用はお勧めしません。しばらくして戻ってきて、それを書いたときに私が何を考えていたのか疑問に思っていたので、これは特に真実です)
string s = string.Concat(arr.Cast<object>().ToArray());
string.Join("", (from i in arr select i.ToString()).ToArray())
.NET 4.0では、string.Join
はIEnumerable<string>
を直接使用できます。
string.Join("", from i in arr select i.ToString())
最も効率的な方法は、各intを文字列に変換するのではなく、文字の配列から1つの文字列を作成することです。ガベージコレクターには、1つの新しい一時オブジェクトしかありません。
int[] arr = {0,1,2,3,0,1};
string result = new string(Array.ConvertAll<int,char>(arr, x => Convert.ToChar(x + '0')));
これが長い配列の場合、使用できます
var sb = arr.Aggregate(new StringBuilder(), ( s, i ) => s.Append( i ), s.ToString());