Używanie LINQ do łączenia łańcuchów

Jaki jest najskuteczniejszy sposób pisania oldskulowego:

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

...w LINQ?

Author: Wayne Koorts, 2008-10-20

17 answers

Użyj zagregowanych zapytań takich jak:

string[] words = { "one", "two", "three" };
var res = words.Aggregate((current, next) => current + ", " + next);
Console.WriteLine(res);

To wyjście:

one, two, three

Agregat jest funkcją, która pobiera zbiór wartości i zwraca wartość skalarną. Przykłady z T-SQL obejmują min, max i sum. Zarówno VB, jak i C# mają wsparcie dla agregatów. Zarówno VB, jak i C# wspierają Agregaty jako metody rozszerzeń. Używając notacji kropkowej, po prostu wywołujemy metodę na obiekcie IEnumerable.

Pamiętaj, że zapytania zbiorcze są wykonywane natychmiast.

Http://msdn.microsoft.com/en-us/library/bb386914.aspx

Ponieważ to nie używa StringBuilder, będzie miało okropną wydajność dla bardzo długich sekwencji.

 472
Author: Jorge Ferreira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-20 13:57:51
return string.Join(", ", strings.ToArray());

W.Net 4 istnieje nowe przeciążenie dla string.Join, które akceptuje IEnumerable<string>. Kod będzie wtedy wyglądał następująco:

return string.Join(", ", strings);
 281
Author: Amy B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-19 14:22:53

Dlaczego używać Linq?

string[] s = {"foo", "bar", "baz"};
Console.WriteLine(String.Join(", ", s));
To działa doskonale i akceptuje wszelkie IEnumerable<string>, o ile pamiętam. Nie potrzeba tu niczego, co jest dużo wolniejsze.
 114
Author: Armin Ronacher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 18:18:34

Przyjrzałeś się metodzie rozszerzenia agregatu?

var sa = (new[] { "yabba", "dabba", "doo" }).Aggregate((a,b) => a + "," + b);
 74
Author: Robert S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 18:09:49

Prawdziwy przykład z mojego kodu:

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

Zapytanie jest obiektem, który ma właściwość Name, która jest ciągiem znaków i chcę, aby nazwy wszystkich zapytań na wybranej liście były oddzielone przecinkami.

 55
Author: Daniel Earwicker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-20 08:52:59

Możesz użyć StringBuilder w 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 jest tam tylko po to, aby pokazać, że możesz robić więcej rzeczy LINQ.)

 27
Author: jonathan.s,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-05-19 20:42:05

Oto połączone podejście Join / Linq, na które zdecydowałem się po przyjrzeniu się innym odpowiedziom i problemom poruszonym w podobnym pytaniu (mianowicie, że agregat i Konkatenat nie zawierają 0 elementów).

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

Or (if s is not a string)

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

  • proste
  • łatwy do odczytania i zrozumienia
  • Działa na elementy ogólne
  • pozwala na używanie obiektów lub właściwości obiektów
  • obsługuje przypadek 0-długości elementy
  • Może być używany z dodatkowym filtrowaniem Linq
  • działa dobrze (przynajmniej z mojego doświadczenia)
  • nie wymaga (ręcznego) tworzenia dodatkowego obiektu (np. StringBuilder) do implementacji

I oczywiście Join dba o brzydki końcowy przecinek, który czasami wkrada się w inne podejścia (for, foreach), dlatego w pierwszej kolejności szukałem rozwiązania Linq.

 26
Author: brichins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-04 19:22:36

Szybkie dane wydajności dla StringBuilder vs Wybierz & Zagreguj przypadek ponad 3000 elementów:

Test jednostkowy-czas trwania (sekundy)
LINQ_StringBuilder-0.0036644
LINQ_Select.Agregat - 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);
    }
 20
Author: user337754,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-04 23:49:08

Zawsze używam metody rozszerzenia:

public static string JoinAsString<T>(this IEnumerable<T> input, string seperator)
{
    var ar = input.Select(i => i.ToString()).ToArray();
    return string.Join(seperator, ar);
}
 15
Author: Kieran Benton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-08 17:02:34

By 'super-cool LINQ way' możesz mówić o sposobie, w jaki LINQ sprawia, że programowanie funkcjonalne jest o wiele bardziej smakowite za pomocą metod rozszerzeń. Chodzi mi o cukier składniowy, który pozwala funkcjom być przykutym w sposób wizualnie liniowy (jedna po drugiej) zamiast zagnieżdżać (jedna wewnątrz drugiej). Na przykład:

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

Można napisać tak:

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

Możesz zobaczyć, jak drugi przykład jest łatwiejszy do odczytania. Możesz również zobaczyć, jak wiele funkcji może być dodawane z mniejszymi problemami wcięć lub Lispy zamykające nawiasy pojawiające się na końcu wyrażenia.

Wiele innych odpowiedzi stwierdza, że String.Join jest drogą, ponieważ jest najszybsza lub najprostsza do odczytania. Ale jeśli przyjmiesz moją interpretację " super-cool LINQ way", to odpowiedzią jest użycie String.Join, ale owinięcie go w metodę rozszerzenia stylu LINQ, która pozwoli Ci połączyć swoje funkcje w przyjemny wizualnie sposób. Więc jeśli chcesz napisać sa.Concatenate(", ") wystarczy stworzyć coś takiego:

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

Zapewni to kod równie wydajny jak wywołanie bezpośrednie (przynajmniej pod względem złożoności algorytmu), a w niektórych przypadkach może sprawić, że kod będzie bardziej czytelny (w zależności od kontekstu), zwłaszcza jeśli inny kod w bloku używa stylu funkcji łańcuchowej.

 12
Author: tpower,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-04 15:43:10

Istnieją różne alternatywne odpowiedzi na to poprzednie pytanie - które co prawda było ukierunkowane na tablicę całkowitą jako źródło, ale otrzymywało uogólnione odpowiedzi.

 5
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:37

Tutaj używa się czystego LINQ jako pojedynczego wyrażenia:

static string StringJoin(string sep, IEnumerable<string> strings) {
  return strings
    .Skip(1)
    .Aggregate(
       new StringBuilder().Append(strings.FirstOrDefault() ?? ""), 
       (sb, x) => sb.Append(sep).Append(x));
}

I jest cholernie szybki!

 5
Author: cdiggins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-03 04:34:09

Zamierzam trochę oszukać i wyrzucić nową odpowiedź na to, że wydaje się podsumować najlepsze wszystko tutaj, zamiast wkładać to w komentarz.

Więc możesz jedną linijkę:

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

string concat = strings        
    .Aggregate(new StringBuilder("\a"), 
                    (current, next) => current.Append(", ").Append(next))
    .ToString()
    .Replace("\a, ",string.Empty); 

Edit: najpierw musisz sprawdzić, czy nie ma pustego wyliczenia, albo dodać .Replace("\a",string.Empty); na końcu wyrażenia. Chyba starałem się być trochę za mądry.

Odpowiedź od @ A. friend może być nieco bardziej wydajna, Nie wiem co zastąpić robi pod maską w porównaniu do usunięcia. Jedyne inne zastrzeżenie, jeśli z jakiegoś powodu chcesz konkat ciągów kończących się \a, stracisz swoje separatory... To mało prawdopodobne. Jeśli tak jest, masz inne fantazyjne postacie do wyboru.

 3
Author: Chris Marisic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-07 15:38:39

Można łączyć LINQ i string.join() dość skutecznie. Tutaj usuwam element z łańcucha. Są lepsze sposoby na to, ale tutaj jest:

filterset = String.Join(",",
                        filterset.Split(',')
                                 .Where(f => mycomplicatedMatch(f,paramToMatch))
                       );
 2
Author: Andiih,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-04 00:57:16

Tutaj jest wiele opcji. Możesz użyć LINQ i Stringbuildera, aby uzyskać wydajność tak:

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

MyList.ForEach(w => builder.Append(builder.Length > 0 ? ", " + w : w));
return builder.ToString();
 1
Author: Kelly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-21 20:02:07

Zrobiłem następujące szybkie i brudne podczas parsowania pliku dziennika IIS za pomocą linq, to działało @ 1 milion linii całkiem dobrze( 15 sekund), chociaż dostałem błąd z pamięci podczas próby 2 miliony linii.

    static void Main(string[] args)
    {

        Debug.WriteLine(DateTime.Now.ToString() + " entering main");

        // USED THIS DOS COMMAND TO GET ALL THE DAILY FILES INTO A SINGLE FILE: copy *.log target.log 
        string[] lines = File.ReadAllLines(@"C:\Log File Analysis\12-8 E5.log");

        Debug.WriteLine(lines.Count().ToString());

        string[] a = lines.Where(x => !x.StartsWith("#Software:") &&
                                      !x.StartsWith("#Version:") &&
                                      !x.StartsWith("#Date:") &&
                                      !x.StartsWith("#Fields:") &&
                                      !x.Contains("_vti_") &&
                                      !x.Contains("/c$") &&
                                      !x.Contains("/favicon.ico") &&
                                      !x.Contains("/ - 80")
                                 ).ToArray();

        Debug.WriteLine(a.Count().ToString());

        string[] b = a
                    .Select(l => l.Split(' '))
                    .Select(words => string.Join(",", words))
                    .ToArray()
                    ;

        System.IO.File.WriteAllLines(@"C:\Log File Analysis\12-8 E5.csv", b);

        Debug.WriteLine(DateTime.Now.ToString() + " leaving main");

    }

Prawdziwym powodem, dla którego użyłem linq, było to, że wcześniej potrzebowałem distinctive ():

string[] b = a
    .Select(l => l.Split(' '))
    .Where(l => l.Length > 11)
    .Select(words => string.Format("{0},{1}",
        words[6].ToUpper(), // virtual dir / service
        words[10]) // client ip
    ).Distinct().ToArray()
    ;
 1
Author: Andy S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-14 18:41:55

Pisałam o tym jakiś czas temu, co zrobiłam, żeby było dokładnie tym, czego szukasz:

Http://ondevelopment.blogspot.com/2009/02/string-concatenation-made-easy.html

W poście na blogu opisz jak zaimplementować metody rozszerzeń, które działają na IEnumerable i są nazwane Concatenate, to pozwoli Ci napisać takie rzeczy jak:

var sequence = new string[] { "foo", "bar" };
string result = sequence.Concatenate();

Lub bardziej wyszukane rzeczy jak:

var methodNames = typeof(IFoo).GetMethods().Select(x => x.Name);
string result = methodNames.Concatenate(", ");
 0
Author: Patrik Hägne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-08 19:44:00