Создание списка, разделенного запятыми, из IList или IEnumerable

Каков самый чистый способ создания списка строковых значений, разделенных запятыми, из IList или IEnumerable ?

String.Join(...) работает с string[] поэтому может работать с громоздким, когда типы, такие как IList или IEnumerable не могут быть легко преобразованы в строковый массив.

21 Solutions collect form web for “Создание списка, разделенного запятыми, из IList или IEnumerable”

.NET 4+

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

Решения Detail & Pre .Net 4.0

IEnumerable можно легко преобразовать в строковый массив с помощью LINQ (.NET 3.5):

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

Достаточно просто написать эквивалентный вспомогательный метод, если вам нужно:

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

Затем назовите его так:

 IEnumerable 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(strings).ToArray()); 

Последний немного глоток, хотя 🙂

Скорее всего, это самый простой способ сделать это, и он достаточно эффективен – есть другие вопросы о том, что такое производительность, в том числе (но не ограничиваясь этим ).

Начиная с .NET 4.0, в string.Join есть больше перегрузок, поэтому вы можете просто написать:

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

Гораздо проще 🙂

FYI, версия .NET 4.0 для string.Join() имеет некоторые дополнительные перегрузки , которые работают с IEnumerable а не только с массивами, в том числе с любым типом T :

 public static string Join(string separator, IEnumerable values) public static string Join(string separator, IEnumerable values) 

Самый простой способ, с помощью которого я могу это сделать, – использовать метод LINQ Aggregate :

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

Я думаю, что самый чистый способ создания списка строковых значений, разделенных запятыми, просто:

 string.Join(",", stringEnumerable); 

Вот полный пример:

 IEnumerable stringEnumerable= new List(); stringList.Add("Comma"); stringList.Add("Separated"); string.Join(",", stringEnumerable); 

Нет необходимости создавать вспомогательную функцию, встроенную в .NET 4.0 и выше.

Вот еще один метод расширения:

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

Так как я добрался сюда, ища для присоединения к определенному свойству списка объектов (а не ToString ()), то это дополнение к принятому ответу:

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

Придя немного поздно к этому обсуждению, но это мой вклад. У меня есть IList OrderIds должны быть преобразованы в строку CSV, но следующее является общим и работает без изменений с другими типами:

 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, использует StringBuilder для построения новой строки, сокращает длину StringBuilder на единицу для удаления последней запятой и возвращает CSV-строку.

Я обновил это, чтобы использовать несколько Append() для добавления строки + запятой. От обратной связи Джеймса я использовал Reflector, чтобы посмотреть на StringBuilder.AppendFormat() . Оказывается AppendFormat() использует StringBuilder для построения строки формата, которая делает ее менее эффективной в этом контексте, чем просто использование нескольких приложений Appends() .

Что-то немного уродливое, но оно работает:

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

Дает вам CSV из списка после того, как вы дадите ему конвертер (в этом случае d => d.DivisionID.ToString (“b”)).

Хакки, но работает – может быть, возможно, добавлен метод расширения?

Вот как я это сделал, используя то, как я это сделал на других языках:

 private string ToStringList(IEnumerable 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(); } 

Сравнивая по производительности, победитель получает «Loop it, Join it and do back step». Фактически «перечислимое и ручное перемещение вперед» одинаково хорошо (см. 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 testData = new List(); public BenchmarkStringUnion() { for(int i=0;i<1000;i++) { testData.Add(i.ToString()); } } [Benchmark] public string StringJoin() { var text = string.Join(",", 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 был использован

Конкретная потребность, когда мы должны окружать “, например:

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

У нас есть функция полезности, что-то вроде этого:

 public static string Join( string delimiter, IEnumerable collection, Func 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() ); 

Я просто решил эту проблему, прежде чем переходить к этой статье. Мое решение выглядит примерно так:

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

Вызывается как:

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

вы можете преобразовать IList в массив с помощью ToArray, а затем запустить команду string.join в массиве.

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

Их можно легко преобразовать в массив с использованием расширений Linq в .NET 3.5.

  var stringArray = stringList.ToArray(); 

Вы также можете использовать что-то вроде следующего после того, как вы преобразовали его в массив, используя один из методов, перечисленных другими:

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

Изменить: Вот еще один пример

Мой ответ подобен выше Агрегатное решение, но должно быть меньше количества вызовов, поскольку нет явных вызовов делегатов:

 public static string ToCommaDelimitedString(this IEnumerable 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 (), и я бы хотел реорганизовать его как прямолинейный цикл while через IEnumerable и использовать MoveNext (), чтобы определить, следует ли писать запятую. Я буду возиться и вынести это решение, если я это рассмотрю.


Вот что я хотел изначально:

 public static string ToDelimitedString(this IEnumerable source, string delimiter, Func 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-- hack.

В моей библиотеке я сделал несколько вариантов этой сигнатуры метода, каждая комбинация включает в себя delimiter и параметры converter с использованием значений "," и x.ToString() по умолчанию.

Надеюсь, это самый простой способ

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

Я пришел к этому обсуждению, ища хороший метод C # для объединения строк, как это делается с помощью метода MySql CONCAT_WS() . Этот метод отличается от метода string.Join() тем, что он не добавляет знак разделителя, если строки NULL или empty.

CONCAT_WS (‘,’, tbl.Lastname, tbl.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; } 

Я написал несколько методов расширения, чтобы сделать это эффективным образом:

  public static string JoinWithDelimiter(this IEnumerable 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; } 

Вы можете использовать .ToArray() в Lists и IEnumerables , а затем использовать String.Join() как вы хотели.

  • Как предотвратить java.lang.String.split () от создания ведущей пустой строки?
  • Почему исключение «#include » только иногда приводит к сбоям компиляции?
  • Как эффективно конкатенировать строки в Go?
  • В чем разница между изменяемой и неизменяемой строкой в ​​C #?
  • Как удалить последний символ из строки?
  • Что означает значение $ перед строкой?
  • Как получить строку между двумя символами?
  • Можно ли ссылаться на переменную со строкой и int?
  • Java - Лучший способ захватить ВСЕ строки между двумя строками? (регулярное выражение?)
  • Как проверить, содержит ли строка только цифры в Java
  • Сравнение Java с == двух строк является ложным?
  • Давайте будем гением компьютера.