Uso de LINQ para concatenar cadenas


¿Cuál es la forma más eficiente de escribir la vieja escuela:

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 ¿en LINQ?

Author: Wayne Koorts, 2008-10-20

17 answers

Utilice consultas agregadas como esta:

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

Esto produce:

one, two, three

Un agregado es una función que toma una colección de valores y devuelve un valor escalar. Los ejemplos de T-SQL incluyen min, max y sum. Tanto VB como C# tienen soporte para agregados. Tanto VB como C# admiten agregados como métodos de extensión. Usando la notación de puntos, uno simplemente llama a un método en un objeto {[8] }umumerable.

Recuerde que las consultas agregadas se ejecutan inmediatamente.

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

Debido a que esto no usa un StringBuilder tendrá un rendimiento horrible para secuencias muy largas.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-20 13:57:51
return string.Join(", ", strings.ToArray());

En.Net 4, hay una nueva sobrecarga para string.Join que acepta IEnumerable<string>. El código entonces se vería como:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-19 14:22:53

¿Por qué usar Linq?

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

Que funciona perfectamente y acepta cualquier IEnumerable<string> que yo recuerde. No hay necesidad Aggregate nada aquí que sea mucho más lento.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-23 18:18:34

¿ Ha visto el método de extensión agregada?

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-23 18:09:49

Ejemplo real de mi código:

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

Una consulta es un objeto que tiene una propiedad Name que es una cadena, y quiero los nombres de todas las consultas en la lista seleccionada, separados por comas.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-20 08:52:59

Puedes usar StringBuilder en 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());

(El Select está ahí solo para mostrar que puedes hacer más cosas 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-19 20:42:05

Aquí está el enfoque combinado Join/Linq que decidí después de mirar las otras respuestas y las cuestiones abordadas en una pregunta similar (es decir, que el agregado y Concatenado fallan con 0 elementos).

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

O (si s no es una cadena)

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

  • Simple
  • fácil de leer y entender
  • funciona para elementos genéricos
  • permite usar objetos o propiedades de objetos
  • maneja el caso de 0 longitud elementos
  • podría utilizarse con un filtrado Linq adicional
  • funciona bien (al menos en mi experiencia)
  • no requiere la creación (manual) de un objeto adicional (por ejemplo, StringBuilder) para implementar

Y, por supuesto, Join se encarga de la molesta coma final que a veces se cuela en otros enfoques (for, foreach), es por eso que estaba buscando una solución Linq en primer lugar.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-04 19:22:36

Datos de rendimiento rápido para el StringBuilder vs Select & Aggregate case sobre 3000 elementos:

Duración de la prueba unitaria (segundos)
LINQ_StringBuilder-0.0036644
LINQ_Select.Agregado-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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-04 23:49:08

Siempre uso el método de extensión:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-08 17:02:34

Por ' super-cool LINQ way' podría estar hablando de la forma en que LINQ hace que la programación funcional sea mucho más agradable con el uso de métodos de extensión. Quiero decir, el azúcar sintáctico que permite que las funciones se encadenen de una manera visualmente lineal (una después de la otra) en lugar de anidar (una dentro de la otra). Por ejemplo:

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

Se puede escribir así:

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

Puedes ver cómo el segundo ejemplo es más fácil de leer. También puede ver cómo más funciones pueden ser añadido con menos de los problemas de sangría o el Lispy parens de cierre que aparecen al final de la expresión.

Muchas de las otras respuestas afirman que el String.Join es el camino a seguir porque es el más rápido o más simple de leer. Pero si tomas mi interpretación de' super-cool LINQ way ' entonces la respuesta es usar String.Join pero tenerlo envuelto en un método de extensión de estilo LINQ que te permitirá encadenar tus funciones de una manera visualmente agradable. Así que si quieres escribir sa.Concatenate(", ") solo necesitas crear algo como esto:

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

Esto proporcionará código que es tan eficaz como la llamada directa (al menos en términos de complejidad del algoritmo) y en algunos casos puede hacer que el código sea más legible (dependiendo del contexto) especialmente si otro código en el bloque está utilizando el estilo de función encadenada.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-04 15:43:10

Hay varias respuestas alternativas en esta pregunta anterior - que ciertamente estaba apuntando a una matriz entera como la fuente, pero recibió respuestas generalizadas.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:37

Aquí está usando LINQ puro como una sola expresión:

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

¡Y es bastante rápido!

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-03 04:34:09

Voy a engañar un poco y lanzar una nueva respuesta a esto que parece resumir lo mejor de todo aquí en lugar de meterlo dentro de un comentario.

Así que usted puede una línea esto:

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: Primero querrá comprobar si hay un enumerable vacío o agregar un .Replace("\a",string.Empty); al final de la expresión. Supongo que podría haber estado tratando de ser un poco demasiado inteligente.

La respuesta de @a. friend podría ser un poco más performante, no estoy seguro de qué Reemplazar lo hace bajo el capó en comparación con Quitar. La única otra advertencia si alguna razón quería concat cadenas que terminó en \a, usted podría perder su separadores... Me parece poco probable. Si ese es el caso, tienes otros caracteres de fantasía para elegir.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-07 15:38:39

Puede combinar LINQ y string.join() de manera bastante efectiva. Aquí estoy eliminando un elemento de una cadena. Hay mejores maneras de hacer esto también, pero aquí está:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-04 00:57:16

Muchas opciones aquí. Puede usar LINQ y un StringBuilder para obtener el rendimiento también de la siguiente manera:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-21 20:02:07

Hice lo siguiente rápido y sucio al analizar un archivo de registro de IIS usando linq, funcionó @ 1 millón de líneas bastante bien (15 segundos), aunque obtuve un error de memoria al intentar 2 millones de líneas.

    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 verdadera razón por la que usé linq fue para un() Yo neede distinto previamente:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-14 18:41:55

Escribí sobre esto hace un tiempo, lo que hice parece ser exactamente lo que estás buscando:

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

En la entrada del blog describe cómo implementar métodos de extensión que funcionan en Concumerable y se denominan Concatenate, esto te permitirá escribir cosas como:

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

O cosas más elaboradas como:

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-08 19:44:00