IList <string>またはIEnumerable <string>からコンマ区切りのリストを作成する


851

IList<string>またはから文字列値のコンマ区切りのリストを作成する最もクリーンな方法は何IEnumerable<string>ですか?

String.Join(...)などで動作するstring[]ので、IList<string>またはなどの型をIEnumerable<string>文字列配列に簡単に変換できない場合は、扱いにくいことがあります。


5
ああ…おっと。3.5でのToArray拡張メソッドの追加を逃しました:public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source)
Daniel Fortunov

1
CSVの記述方法を探しているこの質問に出くわした場合、項目間にカンマを挿入するだけでは不十分であり、引用符とソースデータのカンマの場合に失敗することを覚えておく価値があります。
スペンダー

回答:


1448

.NET 4以降

IList<string> strings = new List<string>{"1","2","testing"};
string joined = string.Join(",", strings);

詳細とPre .Net 4.0ソリューション

IEnumerable<string>LINQ(.NET 3.5)で非常に簡単に文字列配列変換できます:

IEnumerable<string> strings = ...;
string[] array = strings.ToArray();

以下の必要がある場合は、同等のヘルパーメソッドを作成するのは簡単です。

public static T[] ToArray(IEnumerable<T> source)
{
    return new List<T>(source).ToArray();
}

次に、次のように呼び出します。

IEnumerable<string> strings = ...;
string[] array = Helpers.ToArray(strings);

その後、を呼び出すことができますstring.Join。もちろん、あなたがいない持っているヘルパーメソッドを使用します:

// C# 3 and .NET 3.5 way:
string joined = string.Join(",", strings.ToArray());
// C# 2 and .NET 2.0 way:
string joined = string.Join(",", new List<string>(strings).ToArray());

後者は少し一口ですが:)

これは、最も簡単な方法であり、パフォーマンスも非常に優れています。これを含めて(ただし、これに限定されません)、パフォーマンスの正確な状態については他にも質問があります。

.NET 4.0以降、で利用できるオーバーロードが増えるstring.Joinため、実際には次のように記述できます。

string joined = string.Join(",", strings);

はるかにシンプル:)


ヘルパーメソッドでは、2つの不要なリストを作成します。これは本当に問題を解決する最良の方法ですか?自分でforeachループに連結してみませんか?
エリック、

4
ヘルパーメソッドは、1つのリストと1つの配列のみを作成します。重要なのは、結果はリストではなく配列である必要があるということです...始める前に配列のサイズを知っておく必要があります。ベストプラクティスでは、必要がない限り、LINQでソースを複数回列挙しないでください。あらゆる種類の厄介なことを行う可能性があります。したがって、バッファに読み込んでサイズを変更するだけで済みます-これがまさに何をするかList<T>です。なぜ車輪を再発明するのですか?
ジョンスキート、

9
いいえ、ポイントは結果が連結された文字列である必要があるということです。この目標を達成するために、新しいリストや新しい配列を作成する必要はありません。この種の.NETの考え方は私を悲しくさせます。
エリック、

38
それでおしまい。すべての答えはジョン・スキートにつながります。var PurchaseBooks = AmazonContainer.Where(p => p.Author == "Jon Skeet")。Select();に移動します。
ザカリースコット

3
@ codeMonkey0110:クエリ式をそこに置いたり、を呼び出したりしても意味がありませんToList。使用してstring myStr = string.Join(",", foo.Select(a => a.someInt.ToString()))も問題ありません。
Jon Skeet、2014

179

参考までに、.NET 4.0バージョンのにstring.Join()はいくつかの追加のオーバーロードがありIEnumerable配列だけでなく、あらゆるタイプを処理できるものを含めて機能しますT

public static string Join(string separator, IEnumerable<string> values)
public static string Join<T>(string separator, IEnumerable<T> values)

2
これはT.ToString()メソッドを呼び出しますか?
Philippe Lavoie、2011年

ちょうどジョンの答えにこれをコメントしようとしていた。言及していただきありがとうございます。
Dan Bechard 2013年

2
とにかく、オブジェクトのプロパティでこれを行うには?(例:IEnumerable <Employee>およびEmployeeオブジェクトには文字列.SSNプロパティがあり、SSNのコンマ区切りのリストを取得します。)
granadaCoder

1
最初に文字列を選択する必要がありますが、それを行う拡張メソッドを作成することもできます。str = emps.Select(e => e.SSN).Join(",")
Xavier Poinas 2014

65

これを行うために私が見ることができる最も簡単な方法は、LINQ Aggregateメソッドを使用することです。

string commaSeparatedList = input.Aggregate((a, x) => a + ", " + x)

20
これはToArray + Joinよりも複雑(IMO)であるだけでなく、いくらか非効率的でもあります。入力シーケンスが大きいと、パフォーマンスが非常に悪くなります。
Jon Skeet、

35
それでも、それは最も美しいです。
メリット

2
AggregateにStringBuilderシードをフィードすると、Aggregate FuncがになりFunc<StringBuilder,string,StringBuider>ます。次にToString()、返されたStringBuilderを呼び出します。もちろん、それほど美しくはありませんが:)
Matt Greer、

3
これは、質問が私見に要求したことを行う最も明確な方法です。
Derek Morrison

8
その用心input.Countする必要があります以上の1
Youngjae

31

文字列値のコンマ区切りのリストを作成する最もクリーンな方法は単純だと思います:

string.Join<string>(",", stringEnumerable);

ここに完全な例があります:

IEnumerable<string> stringEnumerable= new List<string>();
stringList.Add("Comma");
stringList.Add("Separated");

string.Join<string>(",", stringEnumerable);

ヘルパー関数を作成する必要はありません。これは.NET 4.0以降に組み込まれています。


4
これは.NET 4以降で適用されることに注意してください(Xavierが彼の回答で指摘したとおり)。
Derek Morrison

1か月未満の経験を持つ.NET 4初心者の観点からは、この答えは正確さと簡潔さの素晴らしい組み合わせでした
Dexygen

13

パフォーマンスで比較すると、勝者は「ループして、sb.Appendで後戻り」です。実際、「列挙可能で手動による次の移動」も同じです(標準偏差を考慮)。

BenchmarkDotNet=v0.10.5, OS=Windows 10.0.14393
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233539 Hz, Resolution=309.2587 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
  Core   : .NET Core 4.6.25009.03, 64bit RyuJIT


                Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
---------------------- |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
            StringJoin |  Clr |     Clr | 28.24 us | 0.4381 us | 0.3659 us | 27.68 us | 29.10 us | 28.21 us |    8 | 4.9969 |   16.3 kB |
 SeparatorSubstitution |  Clr |     Clr | 17.90 us | 0.2900 us | 0.2712 us | 17.55 us | 18.37 us | 17.80 us |    6 | 4.9296 |  16.27 kB |
     SeparatorStepBack |  Clr |     Clr | 16.81 us | 0.1289 us | 0.1206 us | 16.64 us | 17.05 us | 16.81 us |    2 | 4.9459 |  16.27 kB |
            Enumerable |  Clr |     Clr | 17.27 us | 0.0736 us | 0.0615 us | 17.17 us | 17.36 us | 17.29 us |    4 | 4.9377 |  16.27 kB |
            StringJoin | Core |    Core | 27.51 us | 0.5340 us | 0.4995 us | 26.80 us | 28.25 us | 27.51 us |    7 | 5.0296 |  16.26 kB |
 SeparatorSubstitution | Core |    Core | 17.37 us | 0.1664 us | 0.1557 us | 17.15 us | 17.68 us | 17.39 us |    5 | 4.9622 |  16.22 kB |
     SeparatorStepBack | Core |    Core | 15.65 us | 0.1545 us | 0.1290 us | 15.45 us | 15.82 us | 15.66 us |    1 | 4.9622 |  16.22 kB |
            Enumerable | Core |    Core | 17.00 us | 0.0905 us | 0.0654 us | 16.93 us | 17.12 us | 16.98 us |    3 | 4.9622 |  16.22 kB |

コード:

public class BenchmarkStringUnion
{
    List<string> testData = new List<string>();
    public BenchmarkStringUnion()
    {
        for(int i=0;i<1000;i++)
        {
            testData.Add(i.ToString());
        }
    }
    [Benchmark]
    public string StringJoin()
    {
        var text = string.Join<string>(",", testData);
        return text;
    }
    [Benchmark]
    public string SeparatorSubstitution()
    {
        var sb = new StringBuilder();
        var separator = String.Empty;
        foreach (var value in testData)
        {
            sb.Append(separator).Append(value);
            separator = ",";
        }
        return sb.ToString();
    }

    [Benchmark]
    public string SeparatorStepBack()
    {
        var sb = new StringBuilder();
        foreach (var item in testData)
            sb.Append(item).Append(',');
        if (sb.Length>=1) 
            sb.Length--;
        return sb.ToString();
    }

    [Benchmark]
    public string Enumerable()
    {
        var sb = new StringBuilder();
        var e = testData.GetEnumerator();
        bool  moveNext = e.MoveNext();
        while (moveNext)
        {
            sb.Append(e.Current);
            moveNext = e.MoveNext();
            if (moveNext) 
                sb.Append(",");
        }
        return sb.ToString();
    }
}

https://github.com/dotnet/BenchmarkDotNetが使用されました


11

オブジェクトのリストのToString()ではなく、オブジェクトのリストの特定のプロパティに参加するために検索しているときにここに到達したので、ここに受け入れられた回答への追加があります。

var commaDelimited = string.Join(",", students.Where(i => i.Category == studentCategory)
                                 .Select(i => i.FirstName));

9

次に、別の拡張メソッドを示します。

    public static string Join(this IEnumerable<string> source, string separator)
    {
        return string.Join(separator, source);
    }

8

この議論に少し遅れて到着しましたが、これは私の貢献です。私はIList<Guid> OrderIdsCSV文字列に変換する必要がありますが、以下は一般的であり、他のタイプで変更せずに動作します:

string csv = OrderIds.Aggregate(new StringBuilder(),
             (sb, v) => sb.Append(v).Append(","),
             sb => {if (0 < sb.Length) sb.Length--; return sb.ToString();});

簡潔で、StringBuilderを使用して新しい文字列を作成し、StringBuilderの長さを1だけ短くして最後のコンマを削除し、CSV文字列を返します。

複数Append()のを使用して文字列+カンマを追加するようにこれを更新しました。Jamesのフィードバックから、Reflectorを使用してを確認しましたStringBuilder.AppendFormat()。結局のところAppendFormat()、StringBuilderを使用してフォーマット文字列を作成しているため、このコンテキストでは、複数Appends()のを使用するよりも効率が悪くなります。


Gavimped、ありがとうXavier .Net4でのその更新に気づかなかった。私が取り組んでいるプロジェクトはまだ飛躍していないので、その間は今の歩行者の例を引き続き使用します。
デビッドクラーク2010年

2
これは、ゼロ項目のIEnumerableソースで失敗します。sb.Length--境界チェックが必要です。
James Dunne

Jamesに感謝します。これを使用しているコンテキストでは、少なくとも1つのOrderIdがあることが "保証"されています。サンプルと自分のコードの両方を更新して、境界チェックを含めました(念のため)。
デビッドクラーク2010年

@ジェームズ私はsb.Lengthを呼び出すと思います-ハックは少し厳しいです。事実上、私はあなたの "if(notdone)"テストを各イテレーションで行うのではなく、最後まで避けています。
David Clarke

1
@ジェームズ私のポイントは、ここで尋ねられた質問に対する複数の正しい答えがしばしばあり、1つを「ハック」として言及することは、私が論争することは正しくないことを意味します。上記のダニエルの回答を連結している少数のGUIDについては、おそらく完全に適切であり、それは確かに私の回答よりも簡潔で読みやすいものです。私はこれをコードの1か所でのみ使用しており、区切り文字としてコンマのみを使用します。YAGNIは、必要のないものを構築しないでくださいと言います。DRYは、拡張メソッドを作成する時点で複数回行う必要がある場合に適用できます。HTH。
David Clarke

7

少し曖昧なものですが、うまくいきます:

string divisionsCSV = String.Join(",", ((List<IDivisionView>)divisions).ConvertAll<string>(d => d.DivisionID.ToString("b")).ToArray());

コンバーターを指定した後、リストからCSVを取得します(この場合は、d => d.DivisionID.ToString( "b"))。

Hacky but works-おそらく拡張メソッドにすることができますか?


7

これが私が他の言語で行った方法を使用した方法です。

private string ToStringList<T>(IEnumerable<T> list, string delimiter)
{
  var sb = new StringBuilder();
  string separator = String.Empty;
  foreach (T value in list)
  {
    sb.Append(separator).Append(value);
    separator = delimiter;
  }
  return sb.ToString();
}

7

で囲む必要がある場合の特定のニーズ、例:

        string[] arr = { "jj", "laa", "123" };
        List<string> myList = arr.ToList();

        // 'jj', 'laa', '123'
        Console.WriteLine(string.Join(", ",
            myList.ConvertAll(m =>
                string.Format("'{0}'", m)).ToArray()));

4

次のようなユーティリティ関数があります。

public static string Join<T>( string delimiter, 
    IEnumerable<T> collection, Func<T, string> convert )
{
    return string.Join( delimiter, 
        collection.Select( convert ).ToArray() );
}

これは、多くのコレクションを簡単に結合するために使用できます。

int[] ids = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233};

string csv = StringUtility.Join(",", ids, i => i.ToString() );

ラムダの前にコレクションparamがあることに注意してください。これは、intellisenseがコレクションタイプを取得するためです。

文字列の列挙が既にある場合は、ToArrayのみを実行する必要があります。

string csv = string.Join( ",", myStrings.ToArray() );

2
:私はほぼ正確に同じことを行い拡張メソッド、非常に便利な持ちstackoverflow.com/questions/696850/...
LukeH

ええ、これは.ToDelimitedString拡張メソッドとして簡単に書くことができます。StringBuilderを使用して最後の文字をトリミングするのではなく、1行の文字列で参加します。
キース

3

ToArrayを使用してIListを配列に変換し、その配列に対してstring.joinコマンドを実行できます。

Dim strs As New List(Of String)
Dim arr As Array
arr = strs.ToArray

3

.NET 3.5のLinq拡張機能を使用して、配列に簡単に変換できます。

   var stringArray = stringList.ToArray();

3

他のユーザーがリストしたいずれかのメソッドを使用して配列に変換した後、次のようなものを使用することもできます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Configuration;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            CommaDelimitedStringCollection commaStr = new CommaDelimitedStringCollection();
            string[] itemList = { "Test1", "Test2", "Test3" };
            commaStr.AddRange(itemList);
            Console.WriteLine(commaStr.ToString()); //Outputs Test1,Test2,Test3
            Console.ReadLine();
        }
    }
}

編集: ここにもう一つの例です


3

この記事を読む前にこの問題を解決しました。私の解決策は以下のようになります:

   private static string GetSeparator<T>(IList<T> list, T item)
   {
       return (list.IndexOf(item) == list.Count - 1) ? "" : ", ";
   }

次のように呼び出されます:

List<thing> myThings;
string tidyString;

foreach (var thing in myThings)
{
     tidyString += string.format("Thing {0} is a {1}", thing.id, thing.name) + GetSeparator(myThings, thing);
}

そのように簡単に表現することもでき、効率も向上します。

string.Join(“,”, myThings.Select(t => string.format(“Thing {0} is a {1}”, t.id, t.name)); 

3

私の答えは集約ソリューションのようなものですが、明示的なデリゲートコールがないため、コールスタックの負荷は低くなるはずです。

public static string ToCommaDelimitedString<T>(this IEnumerable<T> items)
{
    StringBuilder sb = new StringBuilder();
    foreach (var item in items)
    {
        sb.Append(item.ToString());
        sb.Append(',');
    }
    if (sb.Length >= 1) sb.Length--;
    return sb.ToString();
}

もちろん、区切り文字に依存しないように署名を拡張できます。私は実際にはsb.Remove()呼び出しのファンではありません。IEnumerableを真っすぐにループするようにリファクタリングし、コンマを書き込むかどうかを決定するためにMoveNext()を使用したいと思います。私はそれを見つけたら、いじくり回してその解決策を投稿します。


これが私が最初に欲しかったものです:

public static string ToDelimitedString<T>(this IEnumerable<T> source, string delimiter, Func<T, string> converter)
{
    StringBuilder sb = new StringBuilder();
    var en = source.GetEnumerator();
    bool notdone = en.MoveNext();
    while (notdone)
    {
        sb.Append(converter(en.Current));
        notdone = en.MoveNext();
        if (notdone) sb.Append(delimiter);
    }
    return sb.ToString();
}

いいえ、一時的な配列またはリストストレージは必要ありませんし、何StringBuilder Remove()Length--必要なハック。

私のフレームワークライブラリーでは、私は、このメソッドのシグネチャを含むのすべての組み合わせには、いくつかのバリエーション作られたdelimiterconverterの使用とパラメータ","x.ToString()、それぞれのデフォルト値、などを。


3

うまくいけば、これが最も簡単な方法です

 string Commaseplist;
 string[] itemList = { "Test1", "Test2", "Test3" };
 Commaseplist = string.join(",",itemList);
 Console.WriteLine(Commaseplist); //Outputs Test1,Test2,Test3

3

MySqlメソッドで行われるように文字列を結合するための適切なC#メソッドを探しているときに、この議論に出会いましたCONCAT_WS()。このメソッドは、string.Join()文字列がNULLまたは空の場合、区切り記号を追加しない点でメソッドとは異なります。

CONCAT_WS( '、'、tbl.Lastname、tbl.Firstname)

Lastnamefirstnameが空の場合にのみ返されますが、

string.Join( "、"、strLastname、strFirstname)

strLastname + ", "同じ場合に戻ります。

最初の動作が必要なため、次のメソッドを記述しました。

    public static string JoinStringsIfNotNullOrEmpty(string strSeparator, string strA, string strB, string strC = "")
    {
        return JoinStringsIfNotNullOrEmpty(strSeparator, new[] {strA, strB, strC});
    }

    public static string JoinStringsIfNotNullOrEmpty(string strSeparator, string[] arrayStrings)
    {
        if (strSeparator == null)
            strSeparator = "";
        if (arrayStrings == null)
            return "";
        string strRetVal = arrayStrings.Where(str => !string.IsNullOrEmpty(str)).Aggregate("", (current, str) => current + (str + strSeparator));
        int trimEndStartIndex = strRetVal.Length - strSeparator.Length;
        if (trimEndStartIndex>0)
            strRetVal = strRetVal.Remove(trimEndStartIndex);
        return strRetVal;
    }

2

私は効率的な方法でそれを行うためにいくつかの拡張メソッドを書きました:

    public static string JoinWithDelimiter(this IEnumerable<String> that, string delim) {
        var sb = new StringBuilder();
        foreach (var s in that) {
            sb.AppendToList(s,delim);
        }

        return sb.ToString();
    }

これは

    public static string AppendToList(this String s, string item, string delim) {
        if (s.Length == 0) {
            return item;
        }

        return s+delim+item;
    }

3
+演算子を使用して文字列を連結することは、毎回新しい文字列が割り当てられるため、すばらしいことではありません。さらに、StringBuilderは暗黙的に文字列にキャストできますが、頻繁に(ループを繰り返すたびに)キャストすると、文字列ビルダーを持つ目的が大幅に損なわれます。
ダニエルフォルトゥノフ2009年


0

結合したい文字列がオブジェクトのリストにある場合、次のようなこともできます:

var studentNames = string.Join(", ", students.Select(x => x.name));
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.