Using LINQ to concatenate strings

匿名 (未验证) 提交于 2019-12-03 01:17:01

问题:

What is the most efficient way to write the old-school:

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

...in LINQ?

回答1:

Use aggregate queries like this:

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

This outputs:

one, two, three

An aggregate is a function that takes a collection of values and returns a scalar value. Examples from T-SQL include min, max, and sum. Both VB and C# have support for aggregates. Both VB and C# support aggregates as extension methods. Using the dot-notation, one simply calls a method on an IEnumerable object.

Remember that aggregate queries are executed immediately.

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

Because this does not use a StringBuilder it will have horrible performance for very long sequences.



回答2:

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

In .Net 4, there's a new overload for string.Join that accepts IEnumerable. The code would then look like:

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


回答3:

Why use Linq?

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

That works perfectly and accepts any IEnumerable as far as I remember. No need Aggregate anything here which is a lot slower.



回答4:

Have you looked at the Aggregate extension method?

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


回答5:

Real example from my code:

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

A query is an object that has a Name property which is a string, and I want the names of all the queries on the selected list, separated by commas.



回答6:

You can use StringBuilder in Aggregate:

  List strings = new List() { "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()); 

(The Select is in there just to show you can do more LINQ stuff.)



回答7:

Since this question had some activity a couple weeks ago, I decided it was okay for me to throw out the combined Join/Linq approach I settled on after looking at the above answers and the issues addressed in the answer to a similar question (namely that Aggregate and Concatenate fail with 0 elements).

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

or (if s is not a string)

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

  • Simple
  • easy to read and understand
  • works for generic elements
  • allows using objects or object properties
  • handles the case of 0-length elements
  • could be used with additional Linq filtering
  • performs well (at least in my experience)
  • doesn't require (manual) creation of an additional object (e.g. StringBuilder) to implement

And of course Join takes care of the pesky final comma that sometimes sneaks into other approaches (for, foreach), which is why I was looking for a Linq-y solution in the first place.

Of course, if anyone sees any problems with this approach, I'd love to adopt any suggestions or improvements they may have.



回答8:

quick performance data for the stingbuilder vs Select case over 3000 elements:

unit test Duration (seconds) LINQ_SELECT 00:00:01.8012535
LINQ_StringBuilder 00:00:00.0036644

    [TestMethod()]     public void LINQ_StringBuilder()     {         IList ints = new List();         for (int i = 0; i  ints = new List();         for (int i = 0; i  query.ToString()).Aggregate((a, b) => a + ", " + b);     } 


回答9:

I always use the extension method:

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


回答10:

By 'super-cool LINQ way' you might be talking about the way that LINQ makes functional programming a lot more palatable with the use of extension methods. I mean, the syntactic sugar that allows functions to be chained in a visually linear way (one after the other) instead of nesting (one inside the other). For example:

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

can be written like this:

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

You can see how the second example is easier to read. You can also see how more functions can be added with less of the indentation problems or the Lispy closing parens appearing at the end of the expression.

A lot of the other answers state that the String.Join is the way to go because it is the fastest or simplest to read. But if you take my interpretation of 'super-cool LINQ way' then the answer is to use String.Join but have it wrapped in a LINQ style extension method that will allow you to chain your functions in a visually pleasing way. So if you want to write sa.Concatenate(", ") you just need to create something like this:

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

This will provide code that is as performant as the direct call (at least in terms of algorithm complexity) and in some cases may make the code more readable (depending on the context) especially if other code in the block is using the chained function style.



回答11:

There are various alternative answers at this previous question - which admittedly was targeting an integer array as the source, but received generalised answers.



回答12:

Here it is using pure LINQ as a single expression:

static string StringJoin(string sep, IEnumerable strings) {   return strings     .Skip(1)     .Aggregate(        new StringBuilder().Append(strings.FirstOrDefault() ?? ""),         (sb, x) => sb.Append(sep).Append(x)); } 

And its pretty damn fast!



回答13:

I'm going to cheat a little and throw out a new answer to this that seems to sum up the best of everything on here instead of sticking it inside of a comment.

So you can one line this:

List strings = new List() { "one", "two", "three" };  string concat = strings             .Aggregate(new StringBuilder("\a"),                      (current, next) => current.Append(", ").Append(next))     .ToString()     .Replace("\a, ",string.Empty);  

Edit: You'll either want to check for an empty enumerable first or add an .Replace("\a",string.Empty); to the end of the expression. Guess I might have been trying to get a little too smart.

The answer from @a.friend might be slightly more performant, I'm not sure what Replace does under the hood compared to Remove. The only other caveat if some reason you wanted to concat strings that ended in \a's you would lose your separators... I find that unlikely. If that is the case you do have other fancy characters to choose from.



回答14:

You can combine LINQ and string.join() quite effectively. Here I am removing an item from a string. There are better ways of doing this too but here it is:

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


回答15:

Lots of choices here. You can use LINQ and a StringBuilder so you get the performance too like so:

StringBuilder builder = new StringBuilder(); List MyList = new List() {"one","two","three"};  MyList.ForEach(w => builder.Append(builder.Length > 0 ? ", " + w : w)); return builder.ToString(); 


回答16:

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


回答17:

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


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!