Pregunta

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

... en LINQ?

¿Fue útil?

Solución

Esta respuesta muestra el uso de LINQ (Aggregate) según lo solicitado en la pregunta y no está destinado al uso diario. Debido a que esto no usa un StringBuilder tendrá un rendimiento horrible para secuencias muy largas. Para el código regular, use String.Join como se muestra en la otra respuesta

Use consultas agregadas como esta:

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

Esto genera:

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 punto, uno simplemente llama a un método en un IEnumerable objeto.

Recuerde que las consultas agregadas se ejecutan inmediatamente.

Más información - MSDN: Agregado Consultas


Si realmente quiere usar <=> use la variante usando <=> propuesta en comentario por CodeMonkeyKing que sería aproximadamente el mismo código que el <=> normal, incluido un buen rendimiento para una gran cantidad de objetos:

 var res = words.Aggregate(
     new StringBuilder(), 
     (current, next) => current.Append(current.Length == 0? "" : ", ").Append(next))
     .ToString();

Otros consejos

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

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

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

¿Por qué usar Linq?

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

Eso funciona perfectamente y acepta cualquier IEnumerable < string > , según recuerdo. No hay necesidad de Aggregate aquí, lo cual es mucho más lento.

¿Has mirado el método de extensión Agregado?

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

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 de Nombre que es una cadena, y quiero los nombres de todas las consultas en la lista seleccionada, separados por comas.

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í para mostrar que puedes hacer más cosas LINQ.)

Aquí está el enfoque de Unir / Linq combinado que decidí después de ver las otras respuestas y los problemas abordados en una pregunta similar (es decir, que el Agregado y la Concatenación fallan con 0 elementos).

cadena Resultado = String.Join (", " ;, split.Select (s = > s.Name));

o (si s no es una cadena)

cadena Resultado = 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 elementos de longitud 0
  • se podría usar con filtrado Linq adicional
  • se desempeña 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 ocupa de la molesta coma final que a veces se cuela en otros enfoques ( for , foreach ), por lo que estaba buscando una solución Linq en primer lugar.

datos de rendimiento rápidos para StringBuilder vs Select & amp; Caso agregado de más de 3000 elementos:

Prueba unitaria - Duración (segundos)
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);
    }

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

Con ' super-cool LINQ way ' puede estar hablando de la forma en que LINQ hace que la programación funcional sea mucho más aceptable con el uso de métodos de extensión. Quiero decir, el azúcar sintáctico que permite que las funciones se encadenen de forma visualmente lineal (una tras 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 se pueden agregar más funciones con menos problemas de sangrado o el par de cierre Lispy que aparece al final de la expresión.

Muchas de las otras respuestas dicen que el String.Join es el camino a seguir porque es el más rápido o el más simple de leer. Pero si tomas mi interpretación de ' super-cool LINQ way ', entonces la respuesta es usar String.Join pero envuélvela con un método de extensión de estilo LINQ que permita para encadenar sus funciones de una manera visualmente agradable. Así que si desea escribir sa.Concatenate (" ;, ") solo necesita 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á un código que es tan eficaz como la llamada directa (al menos en términos de complejidad de 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 Estilo de función encadenado.

Hay varias respuestas alternativas en este pregunta anterior , que admitía que estaba apuntando a una matriz de enteros como origen, pero recibió respuestas generalizadas.

Aquí se usa 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!

Voy a hacer trampa un poco y lanzar una nueva respuesta a esto que parece resumir lo mejor de todo aquí en lugar de pegarlo dentro de un comentario.

Así que puedes hacer una línea con 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); 

Editar: Deseará buscar primero un enumerable vacío o agregar un .Replace (" \ a " string.Empty); a la Fin de la expresión. Supongo que podría haber intentado ser un poco demasiado inteligente.

La respuesta de @ a.friend puede ser un poco más eficaz, no estoy seguro de lo que Reemplace hace debajo del capó en comparación con Eliminar. La única otra advertencia: si por alguna razón quisiera concatear cadenas que terminaron en \ a's, perdería sus separadores ... Lo encuentro poco probable. Si ese es el caso, tiene otros personajes de fantasía para elegir.

Puedes combinar LINQ y string.join () con bastante eficacia. Aquí estoy eliminando un elemento de una cadena. También hay mejores formas de hacer esto, pero aquí está:

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

Muchas opciones aquí. Puedes usar LINQ y un StringBuilder para que obtengas el rendimiento también:

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

Hice lo siguiente rápido y sucio al analizar un archivo de registro de IIS usando linq, funcionó bastante bien en 1 millón de líneas (15 segundos), aunque tuve un error de falta 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
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()
    ;
quot;) && !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 razón real por la que usé linq fue para un Distinct () que necesitaba anteriormente:

<*>

Hace un blog sobre esto hace un tiempo, lo que hice para ser exactamente lo que estás buscando:

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

En la publicación del blog, describe cómo implementar métodos de extensión que funcionen en IEnumerable y se llamen 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(", ");
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top