web-dev-qa-db-ja.com

IList <文字列>またはIEnumerable <文字列>からのコンマ区切りリストの作成

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

String.Join(...)string[]に対して機能するため、IList<string>IEnumerable<string>などの型を文字列配列に簡単に変換できない場合は、作業が面倒になる可能性があります。

756
Daniel Fortunov

.NET 4+

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

詳細&プレ.NET 4.0ソリューション

IEnumerable<string>はLINQ(.NET 3.5)で簡単に文字列配列veryに変換できます。

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を呼び出すことができます。もちろん、ヘルパーメソッドを使うためにhaveを使うことはありません:

// 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);

もっとシンプル:)

1278
Jon Skeet

参考までに、string.Join()の.NET 4.0バージョンにはいくつかの 余分なオーバーロード があり、配列IEnumerableを処理できるものを含め、配列だけでなくTで動作します。

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

これを行うための最も簡単な方法は、LINQのAggregateメソッドを使うことです。

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

文字列値のカンマ区切りリストを作成する最も簡単な方法は、単純に次のようになると思います。

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

これが完全な例です。

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

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

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

31
Dan VanWinkle

パフォーマンスで比較すると、勝者は「ループして、sbを追加して、ステップを元に戻す」です。実際には "次の列挙型の手動移動"も同じです(stddevを考慮してください)。

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 を使用した

10

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

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

これは別の拡張方法です:

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

この議論に少し遅れて到着しましたが、これは私の貢献によるものです。私はCSV文字列に変換されるIList<Guid> OrderIdsを持っていますが、以下は一般的で他の型で修正されずに動作します:

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()を使うよりも効率が悪くなります。

8
David Clarke

他の言語で行った方法を使用して、私が行った方法は次のとおりです。

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
Dale King

Exで囲む必要がある場合の具体的な必要性

        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()));
7
serhio

ちょっとぎこちない何かが、それは動作します:

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

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

ハッキーだがうまくいく - おそらく拡張メソッドにすることができるだろうか?

7
Mike Kingscott

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

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() );

Intellisenseがコレクション型を取得するので、ラムダの前にコレクションパラメータがあります。

ToArrayを使用するだけで、文字列の列挙がすでにあります。

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

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

   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
SpaceKat

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

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

私の答えは上記のAggregateソリューションに似ていますが、明示的なデリゲート呼び出しがないため、コールスタックの負荷が少なくなるはずです。

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をループスルーwhileループにするようにリファクタリングし、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();
}

一時的な配列やリストの格納は不要で、StringBuilderRemove()Length--のハックも不要です。

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

3
James Dunne

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

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

firstnameが空の場合はLastnameのみを返します。

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;
    }
3

他の人がリストした方法の1つを使って配列に変換した後は、次のようなものも使用できます。

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();
        }
    }
}

編集: He​​re は別の例です

3
Brad

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

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

それらは.NET 3.5のLinq拡張を使って簡単に配列に変換することができます。

   var stringArray = stringList.ToArray();
3
Richard

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

    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;
    }
2
Paul Houle

ListsIEnumerables.ToArray()を使用してから、必要に応じてString.Join()を使用できます。

2
JoshJordan