it-swarm.com.de

Verwenden von LINQ zum Verketten von Zeichenfolgen

Was ist der effizienteste Weg, um die alte Schule zu schreiben:

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

... in LINQ?

324
tags2k

Diese Antwort zeigt die Verwendung von LINQ (Aggregate), wie in der Frage angefordert, und ist nicht für den täglichen Gebrauch vorgesehen. Da dies kein StringBuilder verwendet, hat es eine schreckliche Leistung für sehr lange Sequenzen. Verwenden Sie für regulären Code String.Join Wie im anderen Antwort

Verwenden Sie aggregierte Abfragen wie folgt:

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

Dies gibt aus:

, eins zwei drei

Ein Aggregat ist eine Funktion, die eine Sammlung von Werten aufnimmt und einen skalaren Wert zurückgibt. Beispiele aus T-SQL sind min, max und sum. Sowohl VB als auch C # unterstützen Aggregate. Sowohl VB als auch C # unterstützen Aggregate als Erweiterungsmethoden. Mit der Punktnotation ruft man einfach eine Methode auf IEnumerable Objekt.

Denken Sie daran, dass aggregierte Abfragen sofort ausgeführt werden.

Weitere Informationen - MSDN: Aggregate Queries


Wenn Sie wirklich Aggregate verwenden möchten, verwenden Sie die Variante mit StringBuilder, die im Kommentar von CodeMonkeyKing vorgeschlagen wurde. Dies wäre ungefähr derselbe Code wie normal String.Join, Einschließlich gut Leistung für eine große Anzahl von Objekten:

 var res = words.Aggregate(
     new StringBuilder(), 
     (current, next) => current.Append(current.Length == 0? "" : ", ").Append(next))
     .ToString();
515
Jorge Ferreira
return string.Join(", ", strings.ToArray());

In .Net 4 gibt es ein neues Überladung für string.Join das akzeptiert IEnumerable<string>. Der Code würde dann so aussehen:

return string.Join(", ", strings);
329
Amy B

Warum Linq verwenden?

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

Das funktioniert perfekt und akzeptiert alle IEnumerable<string> so weit ich weiß. Hier brauchen Sie Aggregate nichts, was viel langsamer ist.

124
Armin Ronacher

Haben Sie sich die Erweiterungsmethode Aggregate angesehen?

var sa = (new[] { "yabba", "dabba", "doo" }).Aggregate((a,b) => a + "," + b);
75
Robert S.

Reales Beispiel aus meinem Code:

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

Eine Abfrage ist ein Objekt mit einer Name-Eigenschaft, bei der es sich um eine Zeichenfolge handelt. Ich möchte die Namen aller Abfragen in der ausgewählten Liste durch Kommas trennen.

56

Hier ist der kombinierte Join/Linq-Ansatz, den ich gewählt habe, nachdem ich mir die anderen Antworten und die behandelten Probleme angesehen habe in einer ähnlichen Frage (nämlich, dass Aggregate und Concatenate mit 0 Elementen fehlschlagen).

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

oder (wenn s keine Zeichenkette ist)

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

  • Einfach
  • leicht zu lesen und zu verstehen
  • funktioniert für generische Elemente
  • ermöglicht die Verwendung von Objekten oder Objekteigenschaften
  • behandelt den Fall von Elementen der Länge 0
  • könnte mit zusätzlicher Linq-Filterung verwendet werden
  • funktioniert gut (zumindest nach meiner Erfahrung)
  • für die Implementierung ist keine (manuelle) Erstellung eines zusätzlichen Objekts (z. B. StringBuilder) erforderlich

Und natürlich kümmert sich Join um das lästige Schlusskomma, das sich manchmal in andere Ansätze einschleicht (for, foreach), weshalb ich zunächst nach einer Linq-Lösung gesucht habe.

28
brichins

Sie können StringBuilder in Aggregate verwenden:

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

(Das Select ist nur dort, um zu zeigen, dass Sie mehr LINQ-Sachen machen können.)

28
jonathan.s

schnelle Leistungsdaten für den StringBuilder vs Select & Aggregate-Fall mit über 3000 Elementen:

Unit Test - Dauer (Sekunden)
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);
    }
22
user337754

Ich benutze immer die Erweiterungsmethode:

public static string JoinAsString<T>(this IEnumerable<T> input, string seperator)
{
    var ar = input.Select(i => i.ToString()).ToArray();
    return string.Join(seperator, ar);
}
16
Kieran Benton

Mit 'super-cooler LINQ-Weg' könnten Sie darüber sprechen, wie LINQ die funktionale Programmierung durch die Verwendung von Erweiterungsmethoden wesentlich angenehmer macht. Ich meine, der syntaktische Zucker, mit dem Funktionen auf visuell lineare Weise (nacheinander) verkettet werden können, anstatt (ineinander) zu schachteln. Zum Beispiel:

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

kann so geschrieben werden:

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

Sie können sehen, wie das zweite Beispiel leichter zu lesen ist. Sie können auch sehen, wie mehr Funktionen hinzugefügt werden können, wenn weniger Einrückungsprobleme vorliegen oder die Lispy schließenden Parenen am Ende des Ausdrucks angezeigt werden.

Viele der anderen Antworten geben an, dass String.Join Der richtige Weg ist, da es am schnellsten oder einfachsten zu lesen ist. Aber wenn Sie meine Interpretation von 'super-cooler LINQ-Weg' nehmen, dann ist die Antwort, String.Join Zu verwenden, aber es in eine LINQ-artige Erweiterungsmethode einzuwickeln, die es Ihnen ermöglicht, Ihre zu verketten funktioniert optisch ansprechend. Wenn Sie also sa.Concatenate(", ") schreiben möchten, müssen Sie nur so etwas erstellen:

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

Dies liefert Code, der genauso performant ist wie der direkte Aufruf (zumindest in Bezug auf die Komplexität des Algorithmus) und in einigen Fällen die Lesbarkeit des Codes verbessert (abhängig vom Kontext), insbesondere wenn anderer Code im Block den verketteten Funktionsstil verwendet .

12
tpower

Hier wird reines LINQ als einzelner Ausdruck verwendet:

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

Und es ist verdammt schnell!

5
cdiggins

Zu dieser vorherigen Frage , die zwar auf ein ganzzahliges Array als Quelle abzielte, aber verallgemeinerte Antworten erhielt, gibt es verschiedene alternative Antworten.

5
Jon Skeet

Ich werde ein wenig schummeln und eine neue Antwort darauf herausbringen, die das Beste von allem hier zusammenfasst, anstatt es in einen Kommentar zu stecken.

So können Sie eine Zeile dies:

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

Bearbeiten: Sie möchten entweder zuerst nach einer leeren Aufzählung suchen oder am Ende des Ausdrucks eine .Replace("\a",string.Empty); einfügen. Vermutlich habe ich versucht, ein bisschen zu schlau zu werden.

Die Antwort von @ a.friend könnte etwas performanter sein, ich bin mir nicht sicher, was Replace unter der Haube im Vergleich zu Remove macht. Die einzige andere Einschränkung, wenn Sie aus irgendeinem Grund Zeichenfolgen, die mit\a enden, zusammenfassen wollten, würden Sie Ihre Trennzeichen verlieren ... Ich halte das für unwahrscheinlich. In diesem Fall haben Sie andere ausgefallene Zeichen zur Auswahl.

3
Chris Marisic

Sie können LINQ und string.join() sehr effektiv kombinieren. Hier entferne ich einen Gegenstand aus einer Zeichenkette. Es gibt auch bessere Möglichkeiten, dies zu tun, aber hier ist es:

filterset = String.Join(",",
                        filterset.Split(',')
                                 .Where(f => mycomplicatedMatch(f,paramToMatch))
                       );
2
Andiih

Viele Möglichkeiten hier. Sie können LINQ und einen StringBuilder verwenden, damit Sie die Leistung auch so erhalten:

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
Kelly

Ich habe das folgende schnell und schmutzig gemacht, als ich eine IIS= Logdatei mit linq geparst habe, es hat bei 1 Million Zeilen ziemlich gut funktioniert (15 Sekunden), obwohl ein Fehler wegen Speichermangels aufgetreten ist, als ich 2 Millionen Zeilen ausprobiert habe .

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

    }

Der eigentliche Grund, warum ich Linq verwendet habe, war für ein Distinct (), das ich zuvor brauchte:

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
Andy S.

Ich habe vor einiger Zeit darüber gebloggt, was ich gemacht habe, um genau das zu sein, was du suchst:

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

Beschreiben Sie im Blog-Beitrag, wie Erweiterungsmethoden implementiert werden, die in IEnumerable funktionieren und als Verketten bezeichnet werden. Auf diese Weise können Sie Folgendes schreiben:

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

Oder ausführlichere Dinge wie:

var methodNames = typeof(IFoo).GetMethods().Select(x => x.Name);
string result = methodNames.Concatenate(", ");
0
Patrik Hägne