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?
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.
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.
avez-vous regardé la méthode de L'extension globale?
var sa = (new[] { "yabba", "dabba", "doo" }).Aggregate((a,b) => a + "," + b);
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.
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 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.
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);
}
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);
}
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.
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.
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!
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 .
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))
);
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();
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()
;
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(", ");