web-dev-qa-db-fra.com

Création d'une liste séparée par des virgules à partir de IList <string> ou IEnumerable <string>

Quel est le moyen le plus propre de créer une liste de valeurs de chaîne séparées par des virgules à partir de IList<string> ou IEnumerable<string>?

String.Join(...) opère sur un string[] et peut donc être fastidieux lorsque des types tels que IList<string> ou IEnumerable<string> ne peuvent pas être facilement convertis en tableau de chaînes.

779
Daniel Fortunov

. NET 4 +

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

Detail & Pre .Net 4.0 Solutions

_IEnumerable<string>_ peut être converti en un tableau de chaînes très facilement avec LINQ (.NET 3.5):

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

Il est assez facile d'écrire la méthode d'assistance équivalente si vous devez:

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

Puis appelez ça comme ça:

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

Vous pouvez alors appeler _string.Join_. Bien sûr, vous n'avez pas pour utiliser une méthode d'assistance:

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

Ce dernier est un peu bouchée cependant :)

C’est probablement la façon la plus simple de le faire, et elle est tout aussi performante. Il existe d’autres questions sur la nature exacte des performances, notamment (mais sans s'y limiter) celle-ci .

Depuis .NET 4.0, il y a plus de surcharges disponibles dans string.Join , vous pouvez donc simplement écrire:

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

Beaucoup plus simple :)

1336
Jon Skeet

Pour votre information, la version .NET 4.0 de string.Join() dispose de surcharges supplémentaires , fonctionnant avec IEnumerable au lieu de simples tableaux, y compris celui qui peut traiter n’importe quel type T:

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

La façon la plus simple de procéder consiste à utiliser la méthode LINQ Aggregate:

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

Je pense que le moyen le plus propre de créer une liste de valeurs de chaîne séparées par des virgules est simplement:

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

Voici un exemple complet:

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

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

Il n’est pas nécessaire de créer une fonction d’aide, elle est intégrée à .NET 4.0 et versions ultérieures.

31
Dan VanWinkle

En comparant les performances, le gagnant est "Bouclez-le, sb.Ajoutez-le, et faites un pas en arrière". En fait, "le mouvement suivant énumérable et manuel" est le même bien (considérez 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 |

Code:

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 a été utilisé

11

Depuis que je suis arrivé ici tout en cherchant à rejoindre une propriété spécifique d'une liste d'objets (et non sa ToString ()), voici un ajout à la réponse acceptée:

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

Voici une autre méthode d'extension:

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

Arriver un peu en retard à cette discussion mais c’est ma contribution fwiw. J'ai un IList<Guid> OrderIds à convertir en chaîne CSV, mais ce qui suit est générique et fonctionne sans modification avec d'autres types:

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

Short and sweet, utilise StringBuilder pour construire une nouvelle chaîne, réduit la longueur de StringBuilder d'une unité pour supprimer la dernière virgule et renvoie la chaîne CSV.

J'ai mis à jour ceci pour utiliser plusieurs Append() afin d'ajouter chaîne + virgule. D'après les commentaires de James, j'ai utilisé Reflector pour examiner StringBuilder.AppendFormat(). Il s'avère que AppendFormat() utilise un StringBuilder pour construire la chaîne de format, ce qui la rend moins efficace dans ce contexte que la simple utilisation de plusieurs Appends() '.

8
David Clarke

Voici comment je l'ai fait, en l'utilisant dans d'autres langues:

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

Besoin spécifique quand on devrait s'entourer de ', par 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

Quelque chose d'un peu moche, mais ça marche:

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

Vous donne un fichier CSV à partir d'une liste après lui avoir fourni le convertisseur (dans ce cas, d => d.DivisionID.ToString ("b")).

Hacky mais works - pourrait être transformé en une méthode d'extension peut-être?

7
Mike Kingscott

Nous avons une fonction utilitaire, quelque chose comme ceci:

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

Qui peut être utilisé pour rejoindre facilement de nombreuses collections:

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

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

Notez que nous avons le paramètre collection avant le lambda car intellisense prend ensuite le type de collection.

Si vous avez déjà une énumération de chaînes, tout ce que vous devez faire est ToArray:

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

Je viens de résoudre ce problème avant de passer à travers cet article. Ma solution va quelque chose comme ci-dessous:

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

Appelé comme:

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

J'aurais pu aussi bien m'exprimer comme tel et j'aurais aussi été plus efficace:

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

J'espère que c'est le moyen le plus simple

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

Ma réponse est comme ci-dessus solution globale mais devrait être moins lourde en pile d'appels puisqu'il n'y a pas d'appels de délégués explicites:

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

Bien sûr, on peut étendre la signature pour qu’elle soit indépendante du délimiteur. Je ne suis vraiment pas un fan de l'appel sb.Remove () et j'aimerais le refactoriser pour le transformer en une boucle while-up sur un IEnumerable et utiliser MoveNext () pour déterminer s'il convient ou non d'écrire une virgule. Je vais bidouiller et poster cette solution si je viens sur elle.


Voici ce que je voulais au départ:

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

Aucun stockage temporaire de tableau ou de liste n'est requis, ni StringBuilderRemove() ni Length-- hack requis.

Dans ma bibliothèque d'infrastructure, j'ai fait quelques variations sur cette signature de méthode, chaque combinaison d'inclusion des paramètres delimiter et converter avec l'utilisation de "," et x.ToString() par défaut, respectivement. .

3
James Dunne

Je suis venu sur cette discussion en cherchant une bonne méthode C # pour joindre des chaînes comme avec la méthode MySql CONCAT_WS(). Cette méthode diffère de la méthode string.Join() en ce qu'elle n'ajoute pas le signe de séparation si les chaînes sont NULL ou vides.

CONCAT_WS (',', tbl.nom, tbl.nom)

retournera seulement Lastname si le prénom est vide, alors que

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

retournera strLastname + ", " dans le même cas.

Voulant le premier comportement, j'ai écrit les méthodes suivantes:

    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
Håkon Seljåsen

Vous pouvez également utiliser quelque chose comme ce qui suit après l'avoir converti en tableau à l'aide de l'une des méthodes répertoriées par d'autres:

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

Edit: Here est un autre exemple

3
Brad

vous pouvez convertir IList en un tableau à l'aide de ToArray, puis exécuter une commande string.join sur le tableau.

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

Ils peuvent être facilement convertis en tableau à l’aide des extensions Linq de .NET 3.5.

   var stringArray = stringList.ToArray();
3
Richard

J'ai écrit quelques méthodes d'extension pour le faire de manière efficace:

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

Cela dépend de

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

        return s+delim+item;
    }
2
Paul Houle

Vous pouvez utiliser .ToArray() sur Lists et IEnumerables, puis utiliser String.Join() comme vous le souhaitez.

2
JoshJordan