List to String

S

shapper

Hello,

I have a List<Subject> Subjects where each subject has an Id and a
Name.
I want to create a String from that list as follows:

"Subject1, Subject2 and Subject3" or "Subject1" if only one exists.

I tried the following
:
String subjects = String.Join(", ", source.Select(s =>
s.Name).ToArray());
Int32 index = subjects.LastIndexOf(",");
subjects.Remove(index).Insert(index, " and ");
return subjects;

The "and" is being added but the last subject removed. What is the
correct way to do this?

And if there is only one subject I get an error ...
.... Well I think I can solve this by using:

if (source.Count == 1)
return source.FirstOrDefault().Name.ToString();

I am not sure if this is the best way ...

In fact, source can be empty or null ...

Thanks,
Miguel
 
G

Göran Andersson

shapper said:
Hello,

I have a List<Subject> Subjects where each subject has an Id and a
Name.
I want to create a String from that list as follows:

"Subject1, Subject2 and Subject3" or "Subject1" if only one exists.

I tried the following
:
String subjects = String.Join(", ", source.Select(s =>
s.Name).ToArray());
Int32 index = subjects.LastIndexOf(",");
subjects.Remove(index).Insert(index, " and ");
return subjects;

The "and" is being added but the last subject removed. What is the
correct way to do this?

And if there is only one subject I get an error ...
... Well I think I can solve this by using:

if (source.Count == 1)
return source.FirstOrDefault().Name.ToString();

I am not sure if this is the best way ...

In fact, source can be empty or null ...

Thanks,
Miguel

I have a method lying around for that:

public static string Join(string[] items, string separator, string
lastSeparator) {
int len = separator.Length * (items.Length - 2) + lastSeparator.Length;
foreach (string s in items) len += s.Length;
StringBuilder builder = new StringBuilder(len);
for (int i = 0; i < items.Length; i++) {
builder.Append(items);
switch (items.Length - i) {
case 1: break;
case 2: builder.Append(lastSeparator); break;
default: builder.Append(separator); break;
}
}
return builder.ToString();
}

Just feed it an array of subjects:

string subjects = Join(source.Select(s => s.Name).ToArray(), ", ", " and ");
 
G

Göran Andersson

Göran Andersson said:
shapper said:
Hello,

I have a List<Subject> Subjects where each subject has an Id and a
Name.
I want to create a String from that list as follows:

"Subject1, Subject2 and Subject3" or "Subject1" if only one exists.

I tried the following
:
String subjects = String.Join(", ", source.Select(s =>
s.Name).ToArray());
Int32 index = subjects.LastIndexOf(",");
subjects.Remove(index).Insert(index, " and ");
return subjects;

The "and" is being added but the last subject removed. What is the
correct way to do this?

And if there is only one subject I get an error ...
... Well I think I can solve this by using:

if (source.Count == 1)
return source.FirstOrDefault().Name.ToString();

I am not sure if this is the best way ...

In fact, source can be empty or null ...

Thanks,
Miguel

I have a method lying around for that:

public static string Join(string[] items, string separator, string
lastSeparator) {
int len = separator.Length * (items.Length - 2) + lastSeparator.Length;
foreach (string s in items) len += s.Length;
StringBuilder builder = new StringBuilder(len);
for (int i = 0; i < items.Length; i++) {
builder.Append(items);
switch (items.Length - i) {
case 1: break;
case 2: builder.Append(lastSeparator); break;
default: builder.Append(separator); break;
}
}
return builder.ToString();
}

Just feed it an array of subjects:

string subjects = Join(source.Select(s => s.Name).ToArray(), ", ", " and
");


Note: If the case where there is only one item in the list is common,
you should just check for that first in the method so that you can
simply return that string instead of going through the string builder.
 
P

Paul

I'm suprised your code works at all. Strings are immutable.

If you are calling this a lot you would be better implementing your own
function using a stringbuilder.

The below works for me.
List<string> source = new List<string>();

source.Add("Subject1");

source.Add("Subject2");

source.Add("Subject3");

String subjects = String.Join(",", source.ToArray());



Int32 index = subjects.LastIndexOf(",");

if (index > 0)

{

subjects = subjects.Insert(index + 1, " and ");

subjects = subjects.Remove(index, 1);

}





String.Join

Please, see my code ... I am already using String.Join ...
 
S

shapper

shapper said:
I have a List<Subject> Subjects where each subject has an Id and a
Name.
I want to create a String from that list as follows:
"Subject1, Subject2 and Subject3" or "Subject1" if only one exists.
I tried the following
:
      String subjects = String.Join(", ", source.Select(s =>
s.Name).ToArray());
      Int32 index = subjects.LastIndexOf(",");
      subjects.Remove(index).Insert(index, " and ");
      return subjects;
The "and" is being added but the last subject removed. What is the
correct way to do this?
And if there is only one subject I get an error ...
... Well I think I can solve this by using:
      if (source.Count == 1)
        return source.FirstOrDefault().Name.ToString();
I am not sure if this is the best way ...
In fact, source can be empty or null ...
Thanks,
Miguel

I have a method lying around for that:

public static string Join(string[] items, string separator, string
lastSeparator) {
        int len = separator.Length * (items.Length - 2) + lastSeparator.Length;
        foreach (string s in items) len += s.Length;
        StringBuilder builder = new StringBuilder(len);
        for (int i = 0; i < items.Length; i++) {
                builder.Append(items);
                switch (items.Length - i) {
                        case 1: break;
                        case 2: builder.Append(lastSeparator); break;
                        default: builder.Append(separator); break;
                }
        }
        return builder.ToString();

}

Just feed it an array of subjects:

string subjects = Join(source.Select(s => s.Name).ToArray(), ", ", " and ");


Hi,

I found a really interesting blog post about this:
http://blogs.msdn.com/ericlippert/archive/2009/04/15/comma-quibbling.aspx

What do you think?

I am reading all the code examples ... Uff ... I was paying more
attention to these two:

public static string Quibble(this IEnumerable<string> items)
{
var backwards = new LinkedList<string>();
var forwards = new LinkedList<string>();
foreach (var s in items)
backwards.AddFirst(s);
// "}" is always the last character in the result string.
push it

forwards.AddFirst("}");
// Initially no counter was used. However the choice
between " and " and ", ",
// and when to pop the final seperator was based on
unintuative comparisons
// to stack.Count so a counter was introduced.

var count = 0;
foreach (string s in backwards)
{
forwards.AddFirst(s);
// push " and " the first time, ", " every other time.
forwards.AddFirst(count == 0 ? " and " : ", ");
count++;
}

// if we've pushed at least one pair of items, the top of
the stack
// is a garbage seperator. Pop it.

if (count > 0)
forwards.RemoveFirst();
// The first item is always a "{". Push it.
forwards.AddFirst("{");

StringBuilder sb = new StringBuilder();
foreach (string s in forwards)
sb.Append(s);
return sb.ToString();
}

And:

static string buildString(IEnumerable<string> mlist) {

var count = mlist.Count();
var res = mlist.Select((mitem, index) => new { str =
mitem + ((count - 1) == index ? "" : ( (count - 2) == index ? " and
" : "," )) });
return res.Aggregate(new StringBuilder("{"), (builder,
pitem) => builder.Append(pitem.str)).Append("}").ToString();
}

Anyway, did you ever used or advice any of the code examples on that
post?

These two caught my atention as they use StringBuilder ...
.... And the first a for loop (Because Pete says to me often to use
Loops and not always Lambda expressions)

Thanks,
Miguel
 
S

shapper

I have a method lying around for that:

public static string Join(string[] items, string separator, string
lastSeparator) {
        int len = separator.Length * (items.Length - 2) + lastSeparator.Length;
        foreach (string s in items) len += s.Length;
        StringBuilder builder = new StringBuilder(len);
        for (int i = 0; i < items.Length; i++) {
                builder.Append(items);
                switch (items.Length - i) {
                        case 1: break;
                        case 2: builder.Append(lastSeparator); break;
                        default: builder.Append(separator); break;
                }
        }
        return builder.ToString();

}

Just feed it an array of subjects:

string subjects = Join(source.Select(s => s.Name).ToArray(), ", ", " and ");


Thank You Goran for the code.

In fact I was looking at a lot of code examples for that to ... Check
my previous post.
You might consider it interesting ...
 
G

Göran Andersson

Paul said:
I'm suprised your code works at all. Strings are immutable.

The posted code does of course not work. He must have forgotten
something when pasting it...
If you are calling this a lot you would be better implementing your own
function using a stringbuilder.

The below works for me.
List<string> source = new List<string>();

source.Add("Subject1");

source.Add("Subject2");

source.Add("Subject3");

String subjects = String.Join(",", source.ToArray());



Int32 index = subjects.LastIndexOf(",");

if (index > 0)

{

subjects = subjects.Insert(index + 1, " and ");

subjects = subjects.Remove(index, 1);

}

However, if the last item contains a comma, it replaces that comma
instead...
 
K

kndg

shapper said:
Hello,

I have a List<Subject> Subjects where each subject has an Id and a
Name.
I want to create a String from that list as follows:

"Subject1, Subject2 and Subject3" or "Subject1" if only one exists.

I tried the following
:
String subjects = String.Join(", ", source.Select(s =>
s.Name).ToArray());
Int32 index = subjects.LastIndexOf(",");
subjects.Remove(index).Insert(index, " and ");
return subjects;

The "and" is being added but the last subject removed. What is the
correct way to do this?

And if there is only one subject I get an error ...
.... Well I think I can solve this by using:

if (source.Count == 1)
return source.FirstOrDefault().Name.ToString();

I am not sure if this is the best way ...

In fact, source can be empty or null ...

Thanks,
Miguel

Hi, (just can't resist the temptation)

static string FormatOutput1(ICollection<Subject> source)
{
if (source == null || source.Count < 1) return String.Empty;

var nonEmptyNames = source.Where(x => x != null &&
!String.IsNullOrEmpty(x.Name)).ToList();
var formattedNames = nonEmptyNames.Select((x, index) => index < 1 ?
x.Name : (index == nonEmptyNames.Count - 1 ? " and " : ", ") + x.Name);

var sb = new StringBuilder();

foreach (var item in formattedNames) sb.Append(item);

return sb.ToString();
}

It handles below condition
- list is null or empty
- Subject is null
- Subject.Name is null or empty

Regards.
 
K

kndg

Paul said:
I'm suprised your code works at all. Strings are immutable.

If you are calling this a lot you would be better implementing your own
function using a stringbuilder.
Hi Paul,

I did a timing and found that your method is quite simple and reasonably
fast. Upon examining through the Reflector, String.Join is using pointer
to speed up the operation.
 
K

kndg

shapper said:
shapper said:
Hello,
I have a List<Subject> Subjects where each subject has an Id and a
Name.
I want to create a String from that list as follows:
"Subject1, Subject2 and Subject3" or "Subject1" if only one exists.
I tried the following
:
String subjects = String.Join(", ", source.Select(s =>
s.Name).ToArray());
Int32 index = subjects.LastIndexOf(",");
subjects.Remove(index).Insert(index, " and ");
return subjects;
The "and" is being added but the last subject removed. What is the
correct way to do this?
And if there is only one subject I get an error ...
... Well I think I can solve this by using:
if (source.Count == 1)
return source.FirstOrDefault().Name.ToString();
I am not sure if this is the best way ...
In fact, source can be empty or null ...
Thanks,
Miguel
I have a method lying around for that:

public static string Join(string[] items, string separator, string
lastSeparator) {
int len = separator.Length * (items.Length - 2) + lastSeparator.Length;
foreach (string s in items) len += s.Length;
StringBuilder builder = new StringBuilder(len);
for (int i = 0; i < items.Length; i++) {
builder.Append(items);
switch (items.Length - i) {
case 1: break;
case 2: builder.Append(lastSeparator); break;
default: builder.Append(separator); break;
}
}
return builder.ToString();

}

Just feed it an array of subjects:

string subjects = Join(source.Select(s => s.Name).ToArray(), ", ", " and ");


Hi,

I found a really interesting blog post about this:
http://blogs.msdn.com/ericlippert/archive/2009/04/15/comma-quibbling.aspx

What do you think?

I am reading all the code examples ... Uff ... I was paying more
attention to these two:

public static string Quibble(this IEnumerable<string> items)
{
var backwards = new LinkedList<string>();
var forwards = new LinkedList<string>();
foreach (var s in items)
backwards.AddFirst(s);
// "}" is always the last character in the result string.
push it

forwards.AddFirst("}");
// Initially no counter was used. However the choice
between " and " and ", ",
// and when to pop the final seperator was based on
unintuative comparisons
// to stack.Count so a counter was introduced.

var count = 0;
foreach (string s in backwards)
{
forwards.AddFirst(s);
// push " and " the first time, ", " every other time.
forwards.AddFirst(count == 0 ? " and " : ", ");
count++;
}

// if we've pushed at least one pair of items, the top of
the stack
// is a garbage seperator. Pop it.

if (count > 0)
forwards.RemoveFirst();
// The first item is always a "{". Push it.
forwards.AddFirst("{");

StringBuilder sb = new StringBuilder();
foreach (string s in forwards)
sb.Append(s);
return sb.ToString();
}

And:

static string buildString(IEnumerable<string> mlist) {

var count = mlist.Count();
var res = mlist.Select((mitem, index) => new { str =
mitem + ((count - 1) == index ? "" : ( (count - 2) == index ? " and
" : "," )) });
return res.Aggregate(new StringBuilder("{"), (builder,
pitem) => builder.Append(pitem.str)).Append("}").ToString();
}

Anyway, did you ever used or advice any of the code examples on that
post?


1. LinkList is not a correct data structure for this problem.
2. Linq is okay, but their performance cannot beat a simple loop

Overall, I found that Goran's method is the fastest on my system.
The compiler/CLR optimize the switch statement and make the code faster.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Populate dataGrid 2
List 2
ToString and FromString 10
Trim String 9
Configuration Section 3
Associations in DLinq Select() extension method 11
IEnumerable - Where 1
Expand Range into List 2

Top