Pergunta

Qual é a maneira mais eficiente de escrever a velha escola:

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

... em Linq?

Foi útil?

Solução

Esta resposta mostra o uso de Linq (Aggregate) conforme solicitado na pergunta e não se destina ao uso diário. Porque isso não usa um StringBuilder Ele terá um desempenho horrível para seqüências muito longas. Para uso regular de código String.Join Como mostrado no outro responda

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

Isso sai:

one, two, three

Um agregado é uma função que pega uma coleção de valores e retorna um valor escalar. Exemplos do T-SQL incluem min, max e soma. Tanto o VB quanto o C# têm suporte para agregados. Tanto o VB quanto o C# suportam agregados como métodos de extensão. Usando a notação de ponto, simplesmente chama um método em um Ienumerable objeto.

Lembre -se de que as consultas agregadas são executadas imediatamente.

Mais Informações - MSDN: Consultas agregadas


Se você realmente quer usar Aggregate Use variante usando StringBuilder proposto em comentário por CodemonKeyking o que seria sobre o mesmo código que regular String.Join incluindo bom desempenho para grande número de objetos:

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

Outras dicas

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

No .NET 4, há um novo sobrecarga por string.Join que aceita IEnumerable<string>. O código seria então:

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

Por que usar o LINQ?

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

Que funciona perfeitamente e aceita qualquer IEnumerable<string> Até onde eu lembro. Não há necessidade Aggregate Qualquer coisa aqui que seja muito mais lenta.

Você já olhou para o método de extensão agregado?

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

Exemplo real do meu código:

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

Uma consulta é um objeto que possui uma propriedade de nome que é uma string, e eu quero os nomes de todas as consultas na lista selecionada, separadas por vírgulas.

Você pode usar StringBuilder dentro 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());

(O Select está lá apenas para mostrar que você pode fazer mais coisas do LINQ.)

Aqui está a abordagem combinada de junção/linq que eu decidi depois de analisar as outras respostas e os problemas abordados Em uma pergunta semelhante (ou seja, esse agregado e concatenado falha com 0 elementos).

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

ou se s não é uma string)

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

  • Simples
  • Fácil de ler e compreender
  • funciona para elementos genéricos
  • permite usar objetos ou propriedades de objeto
  • lida com o caso de elementos de 0 comprimento
  • pode ser usado com filtragem LINQ adicional
  • Se apresenta bem (pelo menos na minha experiência)
  • não requer a criação (manual) de um objeto adicional (por exemplo StringBuilder) implementar

E, é claro, ingressar, cuida da vírgula final que às vezes entra em outras abordagens (for, foreach), é por isso que eu estava procurando uma solução LINQ em primeiro lugar.

Dados de desempenho rápido para o caso StringBuilder vs Select & Agregate mais de 3000 elementos:

Teste de unidade - Duração (segundos)
Linq_stringBuilder - 0,0036644
Linq_Select.AGregate - 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);
    }

Eu sempre uso o método de extensão:

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

Por 'Caminho Linq Super-Cool'Você pode estar falando sobre a maneira como o LINQ torna a programação funcional muito mais palatável com o uso de métodos de extensão. Quero dizer, o açúcar sintático que permite que as funções sejam acorrentadas de uma maneira visualmente linear (uma após a outra) em vez de nidificar (uma dentro da outra). Por exemplo:

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

pode ser escrito assim:

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

Você pode ver como o segundo exemplo é mais fácil de ler. Você também pode ver como mais funções podem ser adicionadas com menos problemas de indentação ou o Lispy Fechando parens aparecendo no final da expressão.

Muitas das outras respostas afirmam que o String.Join é o caminho a seguir, porque é o mais rápido ou mais simples de ler. Mas se você aceitar minha interpretação de 'Caminho Linq Super-Cool'Então a resposta é usar String.Join Mas envie -o em um método de extensão de estilo LINQ que permitirá encadear suas funções de uma maneira visualmente agradável. Então, se você quiser escrever sa.Concatenate(", ") Você só precisa criar algo assim:

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

Isso fornecerá código tão executado quanto a chamada direta (pelo menos em termos de complexidade do algoritmo) e, em alguns casos .

Existem várias respostas alternativas neste pergunta anterior - que reconhecia ter como alvo uma matriz inteira como fonte, mas recebeu respostas generalizadas.

Aqui está usando o Pure Linq como uma única expressão:

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

E é muito rápido!

Vou trapacear um pouco e jogar fora uma nova resposta para isso que parece resumir o melhor de tudo aqui, em vez de enfiá -lo dentro de um comentário.

Então você pode alinhar isso:

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: Você vai querer verificar se há um enumerável vazio primeiro ou adicionar um .Replace("\a",string.Empty); até o final da expressão. Acho que eu poderia estar tentando ficar um pouco inteligente demais.

A resposta de @A.friend pode ser um pouco mais com desempenho, não tenho certeza do que a substituição faz sob o capô em comparação com a remoção. A única outra ressalva, se algum motivo que você quisesse concatar seqüências que terminassem em a que você perderia seus separadores ... acho isso improvável. Se for esse o caso que você tem Outros personagens sofisticados escolher a partir de.

Você pode combinar LINQ e string.join() bastante efetivamente. Aqui estou removendo um item de uma string. Existem maneiras melhores de fazer isso também, mas aqui está:

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

Muitas opções aqui. Você pode usar o LINQ e um StringBuilder para obter o desempenho também:

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

I did the following quick and dirty when parsing an IIS log file using linq, it worked @ 1 million lines pretty well (15 seconds), although got an out of memory error when trying 2 millions lines.

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

    }

The real reason I used linq was for a Distinct() I neede previously:

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

I blogged about this a while ago, what I did seams to be exactly what you're looking for:

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

In the blog post describe how to implement extension methods that works on IEnumerable and are named Concatenate, this will let you write things like:

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

Or more elaborate things like:

var methodNames = typeof(IFoo).GetMethods().Select(x => x.Name);
string result = methodNames.Concatenate(", ");
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top