Question

Quel est le moyen le 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();

... dans LINQ?

Était-ce utile?

La solution

Cette réponse montre l'utilisation de LINQ (Aggregate) comme demandé dans la question et n'est pas destinée à un usage quotidien. Parce que cela n’utilise pas un StringBuilder, il aura une performance horrible pour de très longues séquences. Pour le code standard, utilisez String.Join comme indiqué dans l'autre answer

.

Utilisez des requêtes globales comme ceci:

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

Cette sortie:

one, two, three

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

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

Plus d'informations - MSDN: Aggregate Requêtes

Si vous voulez vraiment utiliser <=> utilisez la variante <<> proposée dans le commentaire de CodeMonkeyKing qui serait à peu près le même code que le standard <=>, y compris de bonnes performances pour un grand nombre d'objets:

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

Autres conseils

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

Dans .Net 4, il existe un nouveau surcharge pour string.Join qui accepte IEnumerable < string > . Le code ressemblerait alors à:

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

Pourquoi utiliser Linq?

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

Cela fonctionne parfaitement et accepte tout IEnumerable < string > pour autant que je m'en souvienne. Pas besoin de Agréger quoi que ce soit qui est beaucoup plus lent.

Avez-vous examiné la méthode d'extension Aggregate?

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

Exemple réel tiré 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é Name qui est une chaîne et je veux les noms de toutes les requêtes de la liste sélectionnée, séparés par des virgules.

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

Voici l’approche combinée Join / Linq sur laquelle j’ai opté après avoir examiné les autres réponses et les problèmes abordés dans dans une question similaire (à savoir que Aggregate et Concatenate échouent avec 0 élément).

chaîne Result = String.Join (",", split.Select (s = > s.Name));

ou (si s n'est pas une chaîne)

chaîne 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 d'éléments de longueur nulle
  • pourrait être utilisé avec un filtrage Linq supplémentaire
  • fonctionne bien (du moins d'après mon expérience)
  • ne nécessite pas la création (manuelle) d'un objet supplémentaire (par exemple, StringBuilder ) pour être implémenté

Et bien sûr, Join s'occupe de la dernière virgule qui se glisse parfois dans d'autres approches ( pour , pour chaque ), c'est pourquoi je recherchais une solution Linq en premier lieu.

données de performance rapides pour StringBuilder vs Select & amp; Cas agrégé de plus de 3000 éléments:

Test unitaire - Durée (secondes)
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);
    }

J'utilise toujours la méthode d'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);
}

Par la méthode LINQ super-cool ", vous parlez peut-être de la manière dont LINQ rend la programmation fonctionnelle beaucoup plus acceptable en utilisant des méthodes d'extension. Je veux dire, le sucre syntaxique qui permet d’enchaîner les fonctions de manière linéaire (l’une après l’autre) au lieu de l’imbrication (l’une dans l’autre). Par exemple:

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

peut être écrit comme ceci:

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

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

Beaucoup d’autres réponses indiquent que le String.Join est la voie à suivre car c’est la plus rapide ou la plus simple à lire. Mais si vous prenez mon interprétation de ' voie LINQ super-cool ', alors la réponse est d'utiliser String.Join , mais de l'envelopper dans une méthode d'extension de style LINQ qui permettra vous enchaînez vos fonctions de manière agréable. Donc, si vous voulez écrire sa.Concatenate (",", <)> il vous suffit 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);
   }
}

Ceci fournira un code aussi performant que l’appel direct (du moins en ce qui concerne la complexité de l’algorithme) et, dans certains cas, rendra le code plus lisible (selon le contexte), en particulier si un autre code du bloc utilise le code. style de fonction chaîné.

Il existe différentes réponses alternatives à cette question précédente - qui, certes, ciblait un tableau de nombres entiers comme source, mais recevait des réponses généralisées.

Ici, il utilise LINQ pur comme une expression unique:

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!

Je vais tricher un peu et proposer une nouvelle réponse à cette question qui semble résumer le meilleur de tout ce qui se trouve ici au lieu de la coller dans un commentaire.

Donc, vous pouvez en 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); 

Modifier: Vous devez d'abord rechercher un objet énumérable vide ou ajouter un .Replace (" \ a ", string.Empty); au fin de l'expression. J'imagine que j'essayais peut-être de devenir un peu trop intelligent.

La réponse de @ a.friend pourrait être légèrement plus performante, je ne sais pas ce que Remplacer fait sous le capot par rapport à Supprimer. La seule autre mise en garde si vous vouliez concaténer des chaînes qui se terminaient par un \ a vous perdriez vos séparateurs ... Je trouve cela peu probable. Si tel est le cas, vous avez autres personnages de fantaisie à choisir.

Vous pouvez combiner très efficacement LINQ et string.join () . Ici, je supprime un élément d'une chaîne. Il existe de meilleurs moyens de le faire aussi, mais le voici:

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

Beaucoup de choix ici. Vous pouvez utiliser LINQ et un StringBuilder pour que vous obteniez aussi les performances suivantes:

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

J’ai procédé comme suit rapidement et de manière malpropre lors de l’analyse d’un fichier journal IIS à l’aide de linq. Cela fonctionnait assez bien avec un million de lignes (15 secondes).

    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 vraie raison pour laquelle j'ai utilisé linq était pour Distinct () dont j'avais besoin auparavant:

<*>

J'ai blogué à ce sujet il y a quelque temps, ce que j'ai fait semble être exactement ce que vous recherchez:

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

Dans l'article de blog, décrivez comment implémenter des méthodes d'extension fonctionnant sous IEnumerable et nommées Concatenate. Cela vous permettra d'écrire des choses comme:

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

Ou des choses plus élaborées telles que:

var methodNames = typeof(IFoo).GetMethods().Select(x => x.Name);
string result = methodNames.Concatenate(", ");
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top