views:

716

answers:

7

Consider the following code:

string[] s = new[] { "Rob", "Jane", "Freddy" };

string joined = string.Join(", ", s);

// joined equals "Rob, Jane, Freddy"

For UI reasons I might well want to display the string "Rob, Jane or Freddy".

Any suggestions about the most concise way to do this?

Edit

I am looking for something that is concise to type. Since I am only concatenating small numbers (<10) of strings I am not worried about run-time performance here.

+3  A: 

Create an extension method on string[] that implement the same logic as string.Join but the last item will be appended with "or".

string[] s = new[] { "Rob", "Jane", "Freddy" };

Console.WriteLine(s.BetterJoin(", ", " or "));

// ---8<----

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static string BetterJoin(this string[] items, string separator, string lastSeparator)
        {
            StringBuilder sb = new StringBuilder();

            int length = items.Length - 2;
            int i = 0;

            while (i < length)
            {
                sb.AppendFormat("{0}{1}", items[i++], separator);
            }

            sb.AppendFormat("{0}{1}", items[i++], lastSeparator);
            sb.AppendFormat("{0}", items[i]);

            return sb.ToString();
        }
    }
}
Fabian Vilers
The code provided is not bullet proof, it is just there to give you the way.
Fabian Vilers
+5  A: 

Concatenate all but the last one. Do the last one manually.

Rob Kennedy
+7  A: 

Concise meaning to type? or to run? The fastest to run will be hand-cranked with StringBuilder. But to type, probably (edit handle 0/1 etc):

string joined;
switch (s.Length) {
    case 0: joined = ""; break;
    case 1: joined = s[0]; break;
    default:
        joined = string.Join(", ", s, 0, s.Length - 1)
               + " or " + s[s.Length - 1];
        break;
}


The StringBuilder approach might look something like:

static string JoinOr(string[] values) {
    switch (values.Length) {
        case 0: return "";
        case 1: return values[0];
    }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < values.Length - 2; i++) {
        sb.Append(values[i]).Append(", ");
    }
    return sb.Append(values[values.Length-2]).Append(" or ")
        .Append(values[values.Length-1]).ToString();
}
Marc Gravell
Usually, you would separate the last item with ", or" in english, except when there are only two items. Your first solution (which looks good to me) would need one more case for this.
Svante
@Svante: In English, either way is acceptable. It is up to personal preference to include the comma on the last separator.
Samuel
Not necessary personal preference. The company may have a preferred style, so check with the technical writers. Svante, it's called the Oxford comma, among other things, and it's a subject of much discussion, none of which is on topic here.
Rob Kennedy
Agreed that discussion of the Oxford comma is off-topic! :-)
Richard Ev
+1  A: 

What about:

if (s.Length > 0)
{
    uiText = string.Format("{0} and {1}", string.Join(s, ", ", 0, s.Length - 1), s[s.Length - 1]));
}
else
{
    uiText = "";
}
dommer
+1  A: 

The most memory efficient and scalable would be using a StringBuilder and precalculating the length of the final string to elliminate buffer reallocations. (This is similar to how the String.Concat method works.)

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[i]);
  switch (items.Length - i) {
   case 1: break;
   case 2: builder.Append(lastSeparator); break;
   default: builder.Append(separator); break;
  }
 }
 return builder.ToString();
}

Usage:

string joined = Join(s, ", ", " or ");

An interresting solution would be using a recursive algorithm. It works well for a reasonably small number of strings, but it doesn't scale very well.

public static string Join(string[] items, int index , string separator, string lastSeparator) {
 return items[index++]  + (index == items.Length-1 ? lastSeparator + items[index] : separator + Join(items, index, separator, lastSeparator));
}

Usage:

string joined = Join(s, 0, ", ", " or ");
Guffa
+1  A: 

Generic solution for any type T.

static class IEnumerableExtensions
{
  public static string Join<T>(this IEnumerable<T> items,
                               string seperator, string lastSeperator)
  {
    var sep = "";
    return items.Aggregate("", (current, item) =>
      {
        var result = String.Concat(current,
          // not first  OR not last
          current == "" || !items.Last().Equals(item) ? sep : lastSeperator,
          item.ToString());
        sep = seperator;
        return result;
      });
  }
}

Usage:

var three = new string[] { "Rob", "Jane", "Freddy" };
var two = new string[] { "Rob", "Jane" };
var one = new string[] { "Rob" };
var threeResult = three.Join(", ", " or "); // = "Rob, Jane or Freddy"
var twoResult = two.Join(", ", " or "); // = "Rob or Jane"
var oneResult = one.Join(", ", " or "); // = "Rob"
Samuel
A: 

who can do this??? a program with two character arrays string1 and string2. Your program should be able to compare the two arrays by character using the index numbers. To compare the characters, you must create a function compareChar which takes two parameters of type char. The first parameter should correspond to a character at string1 while the second parameter should correspond to a character at string2 at index n. Your function should check if the characters at index n are the same and must return 1 to the main function and 0 if they are not. Once the value of compareChar is 0, it should stop comparing the rest of the characters and must output to the user the index number where mismatch is found.

mommy ngit
Welcome to Stack Overflow. You seem to have confused the "answer this question" command with the "ask a new question" command. What you've written here is not an answer to the question on this page.
Rob Kennedy
See the "Ask Question" button at the top of the page.
Marc Gravell