Использование LINQ для объединения строк - PullRequest
315 голосов
/ 20 октября 2008

Какой самый эффективный способ написания старой школы:

StringBuilder sb = new StringBuilder();
if (strings.Count > 0)
{
    foreach (string s in strings)
    {
        sb.Append(s + ", ");
    }
    sb.Remove(sb.Length - 2, 2);
}
return sb.ToString();

... в LINQ?

Ответы [ 17 ]

501 голосов
/ 20 октября 2008

Этот ответ показывает использование LINQ (Aggregate) в соответствии с запросом в вопросе и не предназначен для повседневного использования. Поскольку это не использует StringBuilder, это будет иметь ужасную производительность для очень длинных последовательностей. Для обычного кода используйте String.Join, как показано в другом ответе

Используйте сводные запросы, например:

string[] words = { "one", "two", "three" };
var res = words.Aggregate(
   "", // start with empty string to handle empty list case.
   (current, next) => current + ", " + next);
Console.WriteLine(res);

Это выводит:

one, two, three

Агрегат - это функция, которая принимает коллекцию значений и возвращает скалярное значение. Примеры из T-SQL включают min, max и sum. И VB, и C # поддерживают агрегаты. И VB, и C # поддерживают агрегаты в качестве методов расширения. Используя точечную запись, просто вызывается метод объекта IEnumerable .

Помните, что агрегатные запросы выполняются немедленно.

Дополнительная информация - MSDN: Совокупные запросы


Если вы действительно хотите использовать Aggregate, используйте вариант с использованием StringBuilder, предложенный в комментарии CodeMonkeyKing , что будет примерно таким же кодом, как обычный String.Join, включая хорошую производительность для большого количества объектов:

 var res = words.Aggregate(
     new StringBuilder(), 
     (current, next) => current.Append(current.Length == 0? "" : ", ").Append(next))
     .ToString();
319 голосов
/ 20 октября 2008
return string.Join(", ", strings.ToArray());

В .Net 4 появилась новая перегрузка для string.Join, которая принимает IEnumerable<string>. Код будет выглядеть так:

return string.Join(", ", strings);
121 голосов
/ 23 сентября 2008

Зачем использовать Linq?

string[] s = {"foo", "bar", "baz"};
Console.WriteLine(String.Join(", ", s));

Это прекрасно работает и принимает любые IEnumerable<string>, насколько я помню. Нет необходимости Aggregate здесь что-то намного медленнее.

75 голосов
/ 23 сентября 2008

Вы смотрели на метод совокупного расширения?

var sa = (new[] { "yabba", "dabba", "doo" }).Aggregate((a,b) => a + "," + b);
56 голосов
/ 20 октября 2008

Реальный пример из моего кода:

return selected.Select(query => query.Name).Aggregate((a, b) => a + ", " + b);

Запрос - это объект, имеющий свойство Name, представляющее собой строку, и я хочу, чтобы имена всех запросов в выбранном списке были разделены запятыми.

28 голосов
/ 04 октября 2012

Вот комбинированный подход Join / Linq, на котором я остановился, посмотрев другие ответы и вопросы, затронутые в аналогичном вопросе (а именно, что Aggregate и Concatenate терпят неудачу с 0 элементами).

string Result = String.Join(",", split.Select(s => s.Name));

или (если s не строка)

string Result = String.Join(",", split.Select(s => s.ToString()));

  • Simple
  • легко читать и понимать
  • работает для родовых элементов
  • позволяет использовать объекты или свойства объекта
  • обрабатывает регистр элементов 0-длины
  • может использоваться с дополнительной фильтрацией Linq
  • хорошо работает (по крайней мере, по моему опыту)
  • не требует (ручного) создания дополнительного объекта (например, StringBuilder) для реализации

И, конечно, Join позаботится о досадной последней запятой, которая иногда проникает в другие подходы (for, foreach), поэтому я в первую очередь искал решение Linq.

28 голосов
/ 20 мая 2010

Вы можете использовать StringBuilder в Aggregate:

  List<string> strings = new List<string>() { "one", "two", "three" };

  StringBuilder sb = strings
    .Select(s => s)
    .Aggregate(new StringBuilder(), (ag, n) => ag.Append(n).Append(", "));

  if (sb.Length > 0) { sb.Remove(sb.Length - 2, 2); }

  Console.WriteLine(sb.ToString());

(Select там только для того, чтобы показать, что вы можете делать больше вещей LINQ.)

22 голосов
/ 11 мая 2010

быстрых данных о производительности для случая StringBuilder и Select & Aggregate для 3000 элементов:

Юнит тест - Продолжительность (секунды)
LINQ_StringBuilder - 0,0036644
LINQ_Select.Aggregate - 1.8012535

    [TestMethod()]
    public void LINQ_StringBuilder()
    {
        IList<int> ints = new List<int>();
        for (int i = 0; i < 3000;i++ )
        {
            ints.Add(i);
        }
        StringBuilder idString = new StringBuilder();
        foreach (int id in ints)
        {
            idString.Append(id + ", ");
        }
    }
    [TestMethod()]
    public void LINQ_SELECT()
    {
        IList<int> ints = new List<int>();
        for (int i = 0; i < 3000; i++)
        {
            ints.Add(i);
        }
        string ids = ints.Select(query => query.ToString())
                         .Aggregate((a, b) => a + ", " + b);
    }
15 голосов
/ 08 мая 2009

Я всегда использую метод расширения:

public static string JoinAsString<T>(this IEnumerable<T> input, string seperator)
{
    var ar = input.Select(i => i.ToString()).ToArray();
    return string.Join(seperator, ar);
}
12 голосов
/ 16 мая 2012

По ' супер-крутому способу LINQ ' вы можете говорить о том, как LINQ делает функциональное программирование намного более приемлемым с использованием методов расширения. Я имею в виду синтаксический сахар, который позволяет связывать функции визуально линейным образом (один за другим) вместо вложения (один внутри другого). Например:

int totalEven = Enumerable.Sum(Enumerable.Where(myInts, i => i % 2 == 0));

можно записать так:

int totalEven = myInts.Where(i => i % 2 == 0).Sum();

Вы видите, как легче читать второй пример. Вы также можете увидеть, как можно добавить больше функций с меньшим количеством проблем с отступами или с закрывающими скобками Lispy , появляющимися в конце выражения.

Во многих других ответах говорится, что String.Join - это путь, потому что он самый быстрый или простой для чтения. Но если вы возьмете мою интерпретацию ' супер-крутой способ LINQ ', тогда ответом будет использование String.Join, но оберните его в метод расширения стиля LINQ, который позволит вам визуально связать ваши функции приятный путь. Поэтому, если вы хотите написать sa.Concatenate(", "), вам просто нужно создать что-то вроде этого:

public static class EnumerableStringExtensions
{
   public static string Concatenate(this IEnumerable<string> strings, string separator)
   {
      return String.Join(separator, strings);
   }
}

Это обеспечит код, который является таким же быстродействующим, как прямой вызов (по крайней мере, с точки зрения сложности алгоритма), и в некоторых случаях может сделать код более читабельным (в зависимости от контекста), особенно если другой код в блоке использует стиль цепочки функций.

...