Utilisation de LINQ pour concaténer les chaînes

Quelle est la manière la plus efficace d'écrire la vieille école:

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

... à LINQ?

294
demandé sur Wayne Koorts 2008-10-20 12:46:11

17 réponses

utilisez les requêtes agrégées comme ceci:

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

Ce sorties:

one, two, three

Un agrégat est une fonction qui prend un ensemble de valeurs et qui renvoie une valeur scalaire. Les exemples de T-SQL incluent min, max et sum. VB et C# soutiennent les agrégats. VB et C# supportent les agrégats comme méthodes d'extension. En utilisant la notation par points, on appelle simplement une méthode sur un objet IEnumerable .

N'oubliez pas que les requêtes agrégées sont exécutées immédiatement.

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

parce que cela n'utilise pas un StringBuilder il aura une performance horrible pour des séquences très longues.

472
répondu Jorge Ferreira 2008-10-20 13:57:51
return string.Join(", ", strings.ToArray());

dans .Net 4, Il y a un nouveau surcharge pour string.Join qui accepte IEnumerable<string> . Le code ressemblerait alors à:

return string.Join(", ", strings);
281
répondu Amy B 2010-08-19 14:22:53

Pourquoi utiliser Linq?

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

qui fonctionne parfaitement et accepte n'importe quel IEnumerable<string> autant que je me souvienne. Pas besoin de Aggregate quelque chose ici qui est beaucoup plus lent.

114
répondu Armin Ronacher 2008-09-23 18:18:34

avez-vous regardé la méthode de L'extension globale?

var sa = (new[] { "yabba", "dabba", "doo" }).Aggregate((a,b) => a + "," + b);
74
répondu Robert S. 2008-09-23 18:09:49

exemple réel de mon code:

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

une requête est un objet qui a une propriété Nom qui est une chaîne, et je veux les noms de toutes les requêtes sur la liste sélectionnée, séparées par des virgules.

55
répondu Daniel Earwicker 2008-10-20 08:52:59

vous pouvez utiliser StringBuilder dans 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());

(le Select est là juste pour montrer que vous pouvez faire plus de choses LINQ.)

27
répondu jonathan.s 2010-05-19 20:42:05

Voici l'approche combinée Join / Linq que j'ai établie après avoir examiné les autres réponses et les questions abordées dans une question similaire (à savoir que L'agrégat et le concaténé échouent avec 0 éléments).

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

, ou si s n'est pas une chaîne de caractères)

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

  • Simple
  • facile à lire et à comprendre
  • fonctionne pour les éléments génériques
  • permet d'utiliser des objets ou des propriétés d'objet
  • traite le cas de 0-éléments de longueur
  • pourrait être utilisé avec un filtrage Linq supplémentaire
  • fonctionne bien (au moins dans mon expérience),
  • ne nécessite pas la création (manuelle) d'un objet supplémentaire (par exemple StringBuilder ) pour mettre en œuvre

et bien sûr Join s'occupe de la virgule finale qui se faufile parfois dans d'autres approches ( for , foreach ), c'est pour ça que je cherchais une solution Linq.

26
répondu brichins 2018-05-04 19:22:36

rapide des données de performance pour le StringBuilder vs Select & Agrégat de cas de plus de 3000 éléments:

essai unitaire-durée (secondes)

LINQ_StringBuilder-0.0036644

LINQ_Select.Total-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
répondu user337754 2018-05-04 23:49:08

j'utilise toujours la méthode de l'extension:

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
répondu Kieran Benton 2009-05-08 17:02:34

By ' 151970920" super-cool LINQ way ' vous pourriez parler de la façon dont LINQ rend la programmation fonctionnelle beaucoup plus acceptable avec l'utilisation de méthodes d'extension. Je veux dire, le sucre syntaxique qui permet des fonctions enchaîné dans un visuel linéaire (l'un après l'autre) au lieu de nidification (l'un dans l'autre). Par exemple:

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

peut s'écrire comme ceci:

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

Vous pouvez voir comment la deuxième exemple est plus facile à lire. Vous pouvez également voir comment plus de fonctions peuvent être ajoutées avec moins de problèmes d'indentation ou les Lispy parens de fermeture apparaissant à la fin de l'expression.

beaucoup d'autres réponses indiquent que le String.Join est la voie à suivre parce qu'il est le plus rapide ou le plus simple à lire. Mais si vous prenez mon interprétation de " super-cool LINQ way "alors la réponse est d'utiliser String.Join mais avoir enveloppé dans une méthode D'extension de style LINQ qui vous permettra d'enchaîner vos fonctions d'une manière visuellement agréable. Donc si vous voulez écrire sa.Concatenate(", ") vous avez juste besoin de créer quelque chose comme ceci:

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

cela fournira du code qui est aussi performant que l'appel direct (au moins en termes de complexité de l'algorithme) et dans certains cas peut rendre le code plus lisible (selon le contexte) surtout si d'autres codes dans le bloc utilise le style de la fonction enchaînée.

12
répondu tpower 2016-01-04 15:43:10

il y a plusieurs réponses alternatives à cette question précédente - qui ciblait certes un tableau entier comme source, mais a reçu des réponses généralisées.

5
répondu Jon Skeet 2017-05-23 10:31:37

ici, il utilise LINQ pur comme une seule expression:

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

et c'est sacrément rapide!

5
répondu cdiggins 2012-09-03 04:34:09

je vais tricher un peu et de jeter une nouvelle réponse à ce qui semble résumer le mieux de tout ici, au lieu de coller à l'intérieur d'un commentaire.

donc vous pouvez une ligne ceci:

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: vous Vous voulez vérifier un vide énumérable première ou ajouter un .Replace("\a",string.Empty); à la fin de l'expression. Je suppose que j'ai peut-être essayé d'être un peu trop intelligent.

La réponse de @A. ami pourrait être légèrement plus performant, Je ne suis pas sûr ce que le remplacement fait sous le capot par rapport à enlever. La seule autre mise en garde si une raison que vous vouliez concatter des cordes qui ont fini dans \A vous perdriez vos séparateurs... Je trouve que peu probable. Si c'est le cas, vous avez le choix entre et d'autres caractères Fantaisie .

3
répondu Chris Marisic 2013-01-07 15:38:39

vous pouvez combiner LINQ et string.join() très efficacement. Ici, je suis à la suppression d'un élément dans une chaîne de caractères. Il y a de meilleures façons de le faire aussi, mais le voici:

filterset = String.Join(",",
                        filterset.Split(',')
                                 .Where(f => mycomplicatedMatch(f,paramToMatch))
                       );
2
répondu Andiih 2011-12-04 00:57:16

Beaucoup de choix ici. Vous pouvez utiliser LINQ et un StringBuilder donc vous obtenez la performance trop comme cela:

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
répondu Kelly 2010-04-21 20:02:07

j'ai fait le suivant rapide et sale lors de l'analyse D'un fichier journal IIS en utilisant linq, il a fonctionné @ 1 million de lignes assez bien (15 secondes), bien que obtenu une hors de l'erreur de mémoire lors de l'essai de 2 millions de lignes.

    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-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-8 E5.csv", b);

        Debug.WriteLine(DateTime.Now.ToString() + " leaving main");

    }

la vraie raison pour laquelle j'ai utilisé linq était pour un() j'ai besoin auparavant:

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
répondu Andy S. 2011-12-14 18:41:55

j'ai blogué à ce sujet il y a un certain temps, ce que j'ai fait semble être exactement ce que vous cherchez:

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

dans le blog décrire comment mettre en œuvre des méthodes d'extension qui fonctionne sur IEnumerable et sont nommés Concatenate, ce qui vous permettra d'écrire des choses comme:

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

ou des choses plus élaborées comme:

var methodNames = typeof(IFoo).GetMethods().Select(x => x.Name);
string result = methodNames.Concatenate(", ");
0
répondu Patrik Hägne 2010-07-08 19:44:00