11

if i have:

 List<Car>

where car is:

 public class Car
 {
      public int Year;
      public string Name;
 }

and i want to take this array and create a concatenated string by ","

so it would return:

"Toyota, Ford, Chevy"

i can do it manually like this:

  private static string CreateConcatenatedList(List<Car> parts_)
    {
        StringBuilder b = new StringBuilder();
        foreach (Car bp in parts_)
        {
            b.Append(bp.Name + ", ");
        }
        b.Remove(b.Length - 2, 2);
        return b.ToString();
    }

but i thought there might be a more elegant way

1

6 Answers 6

10
List<Car> cars = //whatever;
string concat = String.Join(",", cars.Select(c => c.Name).ToArray());

EDIT: you could also use Aggregate if you're worried about creating the intermediate array:

string concat = cars.Select(c => c.Name).Aggregate(new StringBuilder(), (sb, current) =>
{
    return sb.Length == 0 ? sb.Append(current) : sb.AppendFormat(",{0}", current);
}).ToString();
Sign up to request clarification or add additional context in comments.

4 Comments

@oo - It creates an intermediate array which is a little inefficient, although you probably won't notice unless you have a lot of elements in the list. You can use Aggregate to avoid this.
.NET 4.0 contains a string.join overload accepting an IEnumberable: msdn.microsoft.com/en-us/library/dd783876.aspx
Using aggregate doesnt pay off, as one could expect (I expected it to be much faster than the 'first create array then join it' method, too). See the update in my answer.
@Dykam: thats great. That will make even more of an improvement in those cases like we have here. I dont have .net 4.0 yet. Unfortunatelly I cant compare it with the other methods.
6

Because you asked in a comment to Lees answer whether it is faster/slower or just less code. I tried a bit, and wrote a small car class:

public class Car
{
    public string Name { get; set; }
    public Car(string name) { Name = name; }
}

Tested it with randomly generated strings of length 5-10:

private static Random random = new Random((int)DateTime.Now.Ticks);
private static string RandomString(int min, int max)
{
    string str = "";
    int size = random.Next(min, max + 1);
    for (int i = 0; i < size; i++)
        str += Convert.ToChar(Convert.ToInt32(
                       Math.Floor(26 * random.NextDouble() + 65)));
    return str;
}

public static void MeassureTicks(int numberCars, int minLength, int maxLength)
{
    // Generate random list
    List<Car> cars = Enumerable.Range(0, numberCars)
                     .Select(x => new Car(RandomString(
                             minLength, maxLength))).ToList();

    Stopwatch sw1 = new Stopwatch(), sw2 = new Stopwatch(),
              sw3 = new Stopwatch(), sw4 = new Stopwatch();

    sw1.Start();
    string concat1 = CreateConcatenatedList(cars);
    sw1.Stop();
    sw2.Start();
    string concat2 = String.Join(",", cars.Select(c => c.Name).ToArray());
    sw2.Stop();
    sw3.Start();
    if (numberCars <= 5000)
    {
        string concat3 = cars.Select(c => c.Name).Aggregate("",
                (str, current) =>
                {
                    return str.Length == 0 ? str = current :
                           str += "," + current;
                }).ToString();
    }
    sw3.Stop();
    sw4.Start();
    string concat4 = cars.Select(c => c.Name).Aggregate(
            new StringBuilder(), (sb, current) =>
            {
                return sb.Length == 0 ? sb.Append(current) :
                       sb.AppendFormat(",{0}", current);
            }).ToString();
    sw4.Stop();

    Console.WriteLine(string.Format("{0} car strings joined:\n" +
                "\tYour method:                  {1} ticks\n" + 
                "\tLinq+String.Join:             {2} ticks\n" + 
                "\tLinq+Aggregate+String.Concat: {3} ticks\n" + 
                "\tLinq+Aggregate+StringBuilder: {4} ticks\n",
                cars.Count, sw1.ElapsedTicks, sw2.ElapsedTicks, 
                numberCars <= 5000 ? sw3.ElapsedTicks.ToString() : "-", 
                sw4.ElapsedTicks));

Update: I am now trying both methods that are using aggregate, too.

The outputs are on my pc for some different number of cars:

5 car strings joined:
        Your method:                  14 ticks
        Linq+String.Join:             20 ticks
        Linq+Aggregate+String.Concat: 11 ticks
        Linq+Aggregate+StringBuilder: 15 ticks

50 car strings joined:
        Your method:                  50 ticks
        Linq+String.Join:             45 ticks
        Linq+Aggregate+String.Concat: 70 ticks
        Linq+Aggregate+StringBuilder: 73 ticks

500 car strings joined:
        Your method:                  355 ticks
        Linq+String.Join:             348 ticks
        Linq+Aggregate+String.Concat: 5365 ticks
        Linq+Aggregate+StringBuilder: 619 ticks

5000 car strings joined:
        Your method:                  3584 ticks
        Linq+String.Join:             3357 ticks
        Linq+Aggregate+String.Concat: 379635 ticks
        Linq+Aggregate+StringBuilder: 6078 ticks

50000 car strings joined:
        Your method:                  33705 ticks
        Linq+String.Join:             34082 ticks
        Linq+Aggregate+String.Concat: - ticks
        Linq+Aggregate+StringBuilder: 92839 ticks

500000 car strings joined:
        Your method:                  508439 ticks
        Linq+String.Join:             376339 ticks
        Linq+Aggregate+String.Concat: - ticks
        Linq+Aggregate+StringBuilder: 616048 ticks

The Linq+String.Join method is indeed a bit faster and less code. Aggregate together with the StringBuilter scales very well (not like the string concatenation), but is a bit slower. So either use your method, or Linq+String.Join, which is a nice oneliner and also easily readable.

Comments

5
List<Car> cars = ....
var result = string.Join(",", cars.Select(car => car.Name).ToArray());

1 Comment

Also note that in .NET 4.0 you don't need .ToArray(), as String.Join now has an overload with IEnumerable<string>: msdn.microsoft.com/en-us/library/dd783876.aspx
1

I think that what you really want is:

"Toyota, Ford, Chevy" 

and not:

"Toyota", "Ford", "Chevy" 

as written in your question. This can be achieved like so:

var cars = new List<Car>();

var delimitedString = string.Join(", ", cars.Select(c => c.Name).ToArray());

Comments

1
ArrayList<Car> cars = ...
string finalValue = string.Join(",", cars.Select(c => c.Name).ToArray());

Comments

1

I wrote the following extention method for this type of case. It uses a string builder and Aggregate instead of string.Join and an array for a slight improvement in performance.

public static string Concatenate(
    this IEnumerable<string> collection, 
    string separator)
{
    return collection
        .Skip(1)
        .Aggregate(
            new StringBuilder().Append(collection.First()),
            (b, s) => b.Append(separator).Append(s))
        .ToString();
}

Then in your case it's just

cars.Select(c=>Name).Concatenate(", ");

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.