Verwenden von LINQ zum Verketten von Zeichenfolgen

Was ist der effizienteste Weg, die Old-School 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?

Verwenden Sie Aggregatabfragen wie folgt:

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

Dies ergibt:

  eins zwei drei 

Ein Aggregat ist eine function, die eine Sammlung von Werten übernimmt und einen Skalarwert zurückgibt. Beispiele aus T-SQL enthalten 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 wird einfach eine Methode für ein IEnumerable- Objekt aufgerufen .

Denken Sie daran, dass Aggregatabfragen sofort ausgeführt werden.

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

Da dies keinen StringBuilder , wird es für sehr lange Sequenzen eine schreckliche performance haben.

 return string.Join(", ", strings.ToArray()); 

In .Net 4 gibt es eine neue Überladung für string.Join , die IEnumerable akzeptiert. Der Code würde dann so aussehen:

 return string.Join(", ", strings); 

Warum Linq verwenden?

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

Das funktioniert perfekt und akzeptiert IEnumerable soweit ich mich erinnere. Keine Notwendigkeit Aggregate hier etwas, das viel langsamer ist.

Haben Sie sich die Methode der Aggregaterweiterung angesehen?

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

Echtes Beispiel aus meinem Code:

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

Eine Abfrage ist ein Objekt mit einer Eigenschaft Name, die eine Zeichenfolge ist, und ich möchte die Namen aller Abfragen in der ausgewählten Liste, durch Kommas getrennt.

Sie können StringBuilder im Aggregate :

  List strings = new List() { "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()); 

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

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

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

oder (wenn s keine Zeichenfolge 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)
  • erfordert kein (manuelles) Erstellen eines zusätzlichen Objekts (z. B. StringBuilder ), das implementiert werden soll

Und natürlich kümmert sich Join um das nervtötende Komma, das sich manchmal in andere Ansätze schleicht ( for foreach ), weshalb ich überhaupt nach einer Linq-Lösung gesucht habe.

schnelle performancesdaten für den Fall StringBuilder vs. Select & Aggregate über 3000 Elemente:

Komponententest – Dauer (Sekunden)
LINQ_StringBuilder – 0.0036644
LINQ_Select.Aggregate – 1.8012535

  [TestMethod()] public void LINQ_StringBuilder() { IList ints = new List(); 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 ints = new List(); for (int i = 0; i < 3000; i++) { ints.Add(i); } string ids = ints.Select(query => query.ToString()) .Aggregate((a, b) => a + ", " + b); } 

Ich benutze immer die Erweiterungsmethode:

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

Mit ‘ super-cool LINQ way ‘ könnten Sie über die Art und Weise sprechen, wie LINQ die funktionale Programmierung mit der Verwendung von Erweiterungsmethoden viel schmackhafter macht. Ich meine, der syntaktische Zucker, der es ermöglicht, functionen visuell hintereinander zu verketten anstatt ineinander zu verschachteln. Beispielsweise:

 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 functionen hinzugefügt werden können, mit weniger Einrückungsproblemen oder Lispy Closing Parens am Ende des Ausdrucks.

Viele der anderen Antworten geben an, dass der String.Join der String.Join Weg ist, da er am schnellsten oder am einfachsten zu lesen ist. Aber wenn Sie meine Interpretation von ” super-cool LINQ way ” nehmen, dann ist die Antwort, String.Join zu verwenden, aber Sie müssen es in eine LINQ-Stil-Erweiterungsmethode String.Join haben, die es Ihnen erlaubt, Ihre functionen in einer visuell ansprechenden Weise zu String.Join . Wenn Sie also sa.Concatenate(", ") schreiben sa.Concatenate(", ") Sie nur sa.Concatenate(", ") erstellen:

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

Dies liefert einen Code, der genauso leistungsfähig ist wie der direkte Aufruf (zumindest hinsichtlich der Komplexität des Algorithmus) und in einigen Fällen den Code (abhängig vom Kontext) lesbarer macht, insbesondere wenn anderer Code in dem Block den verketteten functionsstil verwendet .

Bei dieser vorherigen Frage gibt es verschiedene alternative Antworten, die zwar auf ein Integer-Array als Quelle abzielten, aber generalisierte Antworten erhielten.

Hier wird reines LINQ als einzelner Ausdruck verwendet:

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

Und es ist verdammt schnell!

Ich werde ein wenig schummeln und eine neue Antwort auf dieses Thema casting, das das Beste von allem hier zusammenfasst, anstatt es in einen Kommentar zu stecken.

Du kannst das also folgendermaßen ausmalen:

 List strings = new List() { "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 einem leeren .Replace("\a",string.Empty); oder ein .Replace("\a",string.Empty); bis zum Ende des Ausdrucks. Ich schätze, ich hätte versucht, ein wenig zu schlau zu werden.

Die Antwort von @ a.friend könnte etwas leistungsfähiger sein, ich bin mir nicht sicher, was Replace unter der Haube im Vergleich zu Remove tut. Der einzige andere Vorbehalt, wenn Sie einen Grund haben wollten, Strings zu dekonstruieren, die in \ a enden, Sie würden Ihre Separatoren verlieren … Ich finde das unwahrscheinlich. Wenn das der Fall ist, haben Sie andere ausgefallene Charaktere zur Auswahl.

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

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

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

 StringBuilder builder = new StringBuilder(); List MyList = new List() {"one","two","three"}; MyList.ForEach(w => builder.Append(builder.Length > 0 ? ", " + w : w)); return builder.ToString(); 

Ich habe das Folgende schnell und schmutzig gemacht, als ich eine IIS-Protokolldatei mit linq analysierte, es funktionierte bei 1 Million Zeilen ziemlich gut (15 Sekunden), obwohl beim Versuch, 2 Millionen Zeilen auszugeben, ein Speichererrors auftrat.

  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 wahre Grund, warum ich linq verwendet habe, war für ein Distinct (), das ich vorher 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() ; 

Ich habe vor einer Weile darüber gebloggt, was ich getan habe, genau das zu sein, wonach du suchst:

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

Beschreiben Sie im Blogpost, wie Sie Erweiterungsmethoden implementieren, die auf IEnumerable funktionieren und den Namen Concatenate erhalten. So können Sie beispielsweise folgende Dinge schreiben:

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

Oder kompliziertere Dinge wie:

 var methodNames = typeof(IFoo).GetMethods().Select(x => x.Name); string result = methodNames.Concatenate(", ");