Utilizzo di LINQ per concatenare stringhe

Qual è il modo più efficace per scrivere la vecchia scuola:

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?

Utilizza query aggregate come questa:

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

Questo produce:

  uno due tre 

Un aggregato è una funzione che accetta una raccolta di valori e restituisce un valore scalare. Esempi da T-SQL includono min, max e sum. Sia VB che C # hanno il supporto per gli aggregati. Sia VB che C # supportano gli aggregati come metodi di estensione. Usando la notazione a punti, si chiama semplicemente un metodo su un object IEnumerable .

Ricorda che le query aggregate vengono eseguite immediatamente.

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

Perché questo non usa un StringBuilder avrà prestazioni orribili per sequenze molto lunghe.

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

In .Net 4, c’è un nuovo overload per string.Join che accetta IEnumerable . Il codice sarebbe quindi simile a:

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

Perché usare Linq?

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

Funziona perfettamente e accetta qualsiasi IEnumerable per quanto mi ricordo. Non serve Aggregate nulla qui che è molto più lento.

Hai esaminato il metodo di estensione Aggregate?

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

Esempio reale dal mio codice:

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

Una query è un object che ha una proprietà Name che è una stringa, e voglio i nomi di tutte le query sulla lista selezionata, separate da virgole.

È ansible utilizzare StringBuilder in 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()); 

(Il Select è lì solo per mostrare che puoi fare più cose LINQ.)

Ecco l’approccio combinato Join / Linq su cui mi sono concentrato dopo aver esaminato le altre risposte e i problemi affrontati in una domanda simile (vale a dire che Aggregazione e Concatenazione falliscono con 0 elementi).

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

o (se s non è una stringa)

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

  • Semplice
  • facile da leggere e capire
  • funziona per elementi generici
  • consente di utilizzare oggetti o proprietà dell’object
  • gestisce il caso di elementi di lunghezza 0
  • potrebbe essere utilizzato con filtraggio Linq aggiuntivo
  • funziona bene (almeno nella mia esperienza)
  • non richiede la creazione (manuale) di un object aggiuntivo (ad es. StringBuilder ) da implementare

E ovviamente Join si prende cura della fastidiosa virgola finale che a volte si insinua in altri approcci ( for , foreach ), ed è per questo che stavo cercando una soluzione Linq in primo luogo.

dati sulle prestazioni rapide per il caso StringBuilder vs Select & Aggregate su 3000 elementi:

Test unitario – Durata (secondi)
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); } 

Io uso sempre il metodo di estensione:

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

Con ‘ super-cool LINQ way ‘ potresti parlare del modo in cui LINQ rende la programmazione funzionale molto più appetibile con l’uso di metodi di estensione. Voglio dire, lo zucchero sintattico che permette alle catene di essere incatenate in modo visivamente lineare (uno dopo l’altro) invece di nidificare (uno dentro l’altro). Per esempio:

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

può essere scritto così:

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

Puoi vedere come il secondo esempio è più facile da leggere. Puoi anche vedere come più funzioni possono essere aggiunte con meno problemi di indentazione o i parenti di chiusura di Lispy che appaiono alla fine dell’espressione.

Molte altre risposte affermano che String.Join è la strada da percorrere perché è la più veloce o la più semplice da leggere. Ma se prendi la mia interpretazione del ‘ modo LINQ super cool ‘ allora la risposta è usare String.Join ma averlo avvolto in un metodo di estensione in stile LINQ che ti consentirà di concatenare le tue funzioni in modo visivamente piacevole. Quindi se vuoi scrivere sa.Concatenate(", ") devi solo creare qualcosa del genere:

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

Ciò fornirà un codice performante come la chiamata diretta (almeno in termini di complessità dell’algoritmo) e in alcuni casi potrebbe rendere il codice più leggibile (a seconda del contesto), specialmente se l’altro codice nel blocco utilizza lo stile di funzione concatenato .

Ci sono varie risposte alternative a questa domanda precedente – che ammettevano come objective un array intero come sorgente, ma hanno ricevuto risposte generalizzate.

Qui sta usando LINQ puro come una singola espressione:

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

E è abbastanza dannatamente veloce!

Ho intenzione di imbrogliare un po ‘e lanciare una nuova risposta a questo che sembra riassumere il meglio di tutto qui invece di incollarlo all’interno di un commento.

Quindi puoi una riga questo:

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

Modifica: o vuoi prima controllare un enumerable vuoto o aggiungere un .Replace("\a",string.Empty); alla fine dell’espressione. Credo che avrei cercato di diventare un po ‘troppo intelligente.

La risposta di @ a.friend potrebbe essere leggermente più performante, non sono sicuro di quello che Replace fa sotto la cappa rispetto a Remove. L’unica altra avvertenza se per qualche ragione tu volessi concatenare le stringhe che sono finite in \ a, perderai i tuoi separatori … lo trovo improbabile. Se questo è il caso, hai altri personaggi fantasiosi tra cui scegliere.

È ansible combinare LINQ e string.join() abbastanza efficace. Qui sto rimuovendo un object da una stringa. Ci sono modi migliori per farlo, ma eccolo qui:

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

Molte scelte qui. Puoi usare LINQ e un StringBuilder in modo da ottenere le prestazioni in questo modo:

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

Durante l’analisi di un file di registro di IIS utilizzando linq, ho eseguito la procedura seguente rapidamente e in modo anomalo, ha funzionato su un milione di righe piuttosto bene (15 secondi), sebbene abbia rilevato un errore di memoria insufficiente durante il tentativo di 2 milioni di righe.

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

La vera ragione per cui ho usato linq era per un Distinct () ho bisogno in precedenza:

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

Ne ho parlato un po ‘di tempo fa, quello che ho fatto per essere esattamente quello che stai cercando:

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

Nel post del blog descrivi come implementare i metodi di estensione che funzionano su IEnumerable e si chiamano Concatenate, questo ti permetterà di scrivere cose come:

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

O cose più elaborate come:

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