Concat all strings inside a List<string> using LINQ
Asked Answered
K

12

677

Is there any easy LINQ expression to concatenate my entire List<string> collection items to a single string with a delimiter character?

What if the collection is of custom objects instead of string? Imagine I need to concatenate on object.Name.

Kylynn answered 18/2, 2009 at 0:56 Comment(4)
Why linq and not string.Join() ?Gimp
string.Join is better but I think linq makes your code fun, that could be the why!Gleason
String.Join is better because it uses a StringBuilder and avoids the inherrent O(n^2) performance of repeated concatenation.Sago
performance issues using LINQ ?Alumnus
I
563

Warning - Serious Performance Issues

Though this answer does produce the desired result, it suffers from poor performance compared to other answers here. Be very careful about deciding to use it


By using LINQ, this should work;

string delimiter = ",";
List<string> items = new List<string>() { "foo", "boo", "john", "doe" };
Console.WriteLine(items.Aggregate((i, j) => i + delimiter + j));

class description:

public class Foo
{
    public string Boo { get; set; }
}

Usage:

class Program
{
    static void Main(string[] args)
    {
        string delimiter = ",";
        List<Foo> items = new List<Foo>() { new Foo { Boo = "ABC" }, new Foo { Boo = "DEF" },
            new Foo { Boo = "GHI" }, new Foo { Boo = "JKL" } };

        Console.WriteLine(items.Aggregate((i, j) => new Foo{Boo = (i.Boo + delimiter + j.Boo)}).Boo);
        Console.ReadKey();

    }
}

And here is my best :)

items.Select(i => i.Boo).Aggregate((i, j) => i + delimiter + j)
Inshrine answered 18/2, 2009 at 1:17 Comment(9)
O(n^2) time strikes again.Sago
If you can't see the Aggregate method, you need to add using System.Linq;Lathy
better using String.Format like items.Select(i => i.Boo).Aggregate((i, j) => string.Format("{0} {1} {2}", i, delimiter, j))Pincer
Problem is that above LinQ method does not work with empty or single-element list.Mercaptide
it will throw an InvalidOperationException in case items is empty.Planetarium
why not just use string.join? Please accept Sedat's answer so that anyone in a rush doesn't choose this solution when Sedat's is the better choice.Lamplighter
DO NOT USE THIS. This solution will immediately tank application performance on even a trivial set of strings. Use Sedat's answer with string.Join!Orientate
@PreguntonCojoneroCabrón: no (and I'm not going to go create them), but I was on the team responsible for performance improvements to an application and this was a big hot-button item, responsible for seconds of page-load performance with a modest (10k items) list.Orientate
When you have a small list of strings that could be null, then this is the only solution that can be used: items?.Select(i => i.Boo)?.Aggregate((i, j) => i + delimiter + j) ?? string.emptyFroma
H
1243
string result = String.Join(delimiter, list);

is sufficient.

Holism answered 18/2, 2009 at 0:58 Comment(9)
I am all for LINQ solutions but this is more efficient than LINQ and the Aggregate() method.Kingcraft
much cleaner! worked great for me! string.Join(", ", accs.Select(x => x.AccountID).ToArray()),Obelia
@KonstantinSalavatov I had posted my answer before OP had clarified that it had to be in LINQ. It is still perfectly valid for anyone who bumps into this answer while looking for a "not-necessarily-LINQ" solution on Google. Regarding this answer "not useful" in that context is unfair.Holism
This can also be used for things other than List<String>s and will call the ToString() method.Devonadevondra
doesn't work if this is a query going to the database. It only works on memory.Conditioned
@Conditioned well, a List<string> is never a query going to the database. that's an entirely different problem but you can always call .ToList() to a query and merge later.Holism
He said "using linq"Lindbom
@Blechdose see previous comments.Holism
You can always have a linq query produce a list, then call this solution here. Perfectly acceptable. This is what I used when I searched for LINQ + Join.Colocynth
I
563

Warning - Serious Performance Issues

Though this answer does produce the desired result, it suffers from poor performance compared to other answers here. Be very careful about deciding to use it


By using LINQ, this should work;

string delimiter = ",";
List<string> items = new List<string>() { "foo", "boo", "john", "doe" };
Console.WriteLine(items.Aggregate((i, j) => i + delimiter + j));

class description:

public class Foo
{
    public string Boo { get; set; }
}

Usage:

class Program
{
    static void Main(string[] args)
    {
        string delimiter = ",";
        List<Foo> items = new List<Foo>() { new Foo { Boo = "ABC" }, new Foo { Boo = "DEF" },
            new Foo { Boo = "GHI" }, new Foo { Boo = "JKL" } };

        Console.WriteLine(items.Aggregate((i, j) => new Foo{Boo = (i.Boo + delimiter + j.Boo)}).Boo);
        Console.ReadKey();

    }
}

And here is my best :)

items.Select(i => i.Boo).Aggregate((i, j) => i + delimiter + j)
Inshrine answered 18/2, 2009 at 1:17 Comment(9)
O(n^2) time strikes again.Sago
If you can't see the Aggregate method, you need to add using System.Linq;Lathy
better using String.Format like items.Select(i => i.Boo).Aggregate((i, j) => string.Format("{0} {1} {2}", i, delimiter, j))Pincer
Problem is that above LinQ method does not work with empty or single-element list.Mercaptide
it will throw an InvalidOperationException in case items is empty.Planetarium
why not just use string.join? Please accept Sedat's answer so that anyone in a rush doesn't choose this solution when Sedat's is the better choice.Lamplighter
DO NOT USE THIS. This solution will immediately tank application performance on even a trivial set of strings. Use Sedat's answer with string.Join!Orientate
@PreguntonCojoneroCabrón: no (and I'm not going to go create them), but I was on the team responsible for performance improvements to an application and this was a big hot-button item, responsible for seconds of page-load performance with a modest (10k items) list.Orientate
When you have a small list of strings that could be null, then this is the only solution that can be used: items?.Select(i => i.Boo)?.Aggregate((i, j) => i + delimiter + j) ?? string.emptyFroma
W
150

Note: This answer does not use LINQ to generate the concatenated string. Using LINQ to turn enumerables into delimited strings can cause serious performance problems

Modern .NET (since .NET 4)

This is for an array, list or any type that implements IEnumerable:

string.Join(delimiter, enumerable);

And this is for an enumerable of custom objects:

string.Join(delimiter, enumerable.Select(i => i.Boo));

Old .NET (before .NET 4)

This is for a string array:

string.Join(delimiter, array);

This is for a List<string>:

string.Join(delimiter, list.ToArray());

And this is for a list of custom objects:

string.Join(delimiter, list.Select(i => i.Boo).ToArray());
Wynd answered 18/2, 2009 at 5:53 Comment(3)
String.Join has an overload that takes an IEnumerable, so you don't need the ToArray() callKillion
Keep in mind the IEnumerable overload only exists in 4.0 or later. If you're using an older version you will still need ToArray().Imp
Ah! That last overload was the one I was looking for. I knew there had to be a way to extract a specific property. :)Poliard
O
60
using System.Linq;

public class Person
{
  string FirstName { get; set; }
  string LastName { get; set; }
}

List<Person> persons = new List<Person>();

string listOfPersons = string.Join(",", persons.Select(p => p.FirstName));
Ocotillo answered 21/6, 2010 at 20:55 Comment(0)
F
27

Good question. I've been using

List<string> myStrings = new List<string>{ "ours", "mine", "yours"};
string joinedString = string.Join(", ", myStrings.ToArray());

It's not LINQ, but it works.

Fizz answered 18/2, 2009 at 1:0 Comment(2)
Why to do you have to call .ToArray() ?Cioffred
Because back in the bad ole days of 2009, string.Join didn't have an extension that accepted an IEnumerable.Fizz
C
10

You can simply use:

List<string> items = new List<string>() { "foo", "boo", "john", "doe" };

Console.WriteLine(string.Join(",", items));

Happy coding!

Cold answered 13/9, 2018 at 12:44 Comment(0)
B
8

I think that if you define the logic in an extension method the code will be much more readable:

public static class EnumerableExtensions { 
  public static string Join<T>(this IEnumerable<T> self, string separator) {  
    return String.Join(separator, self.Select(e => e.ToString()).ToArray()); 
  } 
} 

public class Person {  
  public string FirstName { get; set; }  
  public string LastName { get; set; }  
  public override string ToString() {
    return string.Format("{0} {1}", FirstName, LastName);
  }
}  

// ...

List<Person> people = new List<Person>();
// ...
string fullNames = people.Join(", ");
string lastNames = people.Select(p => p.LastName).Join(", ");
Bignoniaceous answered 19/10, 2010 at 17:37 Comment(0)
C
7
List<string> strings = new List<string>() { "ABC", "DEF", "GHI" };
string s = strings.Aggregate((a, b) => a + ',' + b);
Corot answered 18/2, 2009 at 1:3 Comment(0)
T
4

I have done this using LINQ:

var oCSP = (from P in db.Products select new { P.ProductName });

string joinedString = string.Join(",", oCSP.Select(p => p.ProductName));
Technique answered 13/8, 2018 at 7:41 Comment(0)
L
2

Put String.Join into an extension method. Here is the version I use, which is less verbose than Jordaos version.

  • returns empty string "" when list is empty. Aggregate would throw exception instead.
  • probably better performance than Aggregate
  • is easier to read when combined with other LINQ methods than a pure String.Join()

Usage

var myStrings = new List<string>() { "a", "b", "c" };
var joinedStrings = myStrings.Join(",");  // "a,b,c"

Extensionmethods class

public static class ExtensionMethods
{
    public static string Join(this IEnumerable<string> texts, string separator)
    {
        return String.Join(separator, texts);
    }
}
Lindbom answered 4/7, 2020 at 11:5 Comment(0)
E
2

This answer aims to extend and improve some mentions of LINQ-based solutions. It is not an example of a "good" way to solve this per se. Just use string.Join as suggested when it fits your needs.

Context

This answer is prompted by the second part of the question (a generic approach) and some comments expressing a deep affinity for LINQ.

Given that there is no answer matching all these requirements, I propose an implementation that is based on LINQ, running in linear time, works with enumerations of arbitrary length, and supports generic conversions to string for the elements.

So, LINQ or bust? Okay.
static string Serialize<T>(IEnumerable<T> enumerable, char delim, Func<T, string> toString)
{
    return enumerable.Aggregate(
        new StringBuilder(),
        (sb, t) => sb.Append(toString(t)).Append(delim),
        sb =>
        {
            if (sb.Length > 0)
            {
                sb.Length--;
            }

            return sb.ToString();
        });
}

This implementation is more involved than many alternatives, predominantly because we need to manage the boundary conditions for the delimiter (separator) in our own code.

It should run in linear time, traversing the elements at most twice.

Once for generating all the strings to be appended in the first place, and zero to one time while generating the final result during the final ToString call. This is because the latter may be able to just return the buffer that happened to be large enough to contain all the appended strings from the get go, or it has to regenerate the full thing (unlikely), or something in between. See e.g. What is the Complexity of the StringBuilder.ToString() on SO for more information.

Final Words

Just use string.Join as suggested if it fits your needs, adding a Select when you need to massage the sequence first.

This answer's main intent is to illustrate that it is possible to keep the performance in check using LINQ. The result is (probably) too verbose to recommend, but it exists.

Erector answered 7/7, 2021 at 11:45 Comment(0)
A
1

You can use Aggregate, to concatenate the strings into a single, character separated string but will throw an Invalid Operation Exception if the collection is empty.

You can use Aggregate function with a seed string.

var seed = string.Empty;
var seperator = ",";

var cars = new List<string>() { "Ford", "McLaren Senna", "Aston Martin Vanquish"};

var carAggregate = cars.Aggregate(seed,
                (partialPhrase, word) => $"{partialPhrase}{seperator}{word}").TrimStart(',');

you can use string.Join doesn’t care if you pass it an empty collection.

var seperator = ",";

var cars = new List<string>() { "Ford", "McLaren Senna", "Aston Martin Vanquish"};

var carJoin = string.Join(seperator, cars);

Altimeter answered 19/6, 2020 at 20:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.