views:

165

answers:

5

Hi all,

I am trying to transform a string made of words starting with an uppercase letter. I want to separate each word with a space and keep only the first uppercase letter. All other letters should be lowercase.

For example, "TheQuickBrownFox" would become "The quick brown fox".

Obviously, I could use a simple foreach and build a string by checking each character, but I am trying to do it using LINQ.

Would you know how to solve this elegantly using LINQ?

Thank you for your help.

+1  A: 

This will not be ideal for such situations as "PaulTookTheSAT" or "McDonald'sIsNasty".

string input = "TheQuickBrownFox";
string output  = input.Select((c, i) => i == 0 ? c.ToString() : char.IsUpper(c) ? " " + c.ToString().ToLower() : c.ToString()).Aggregate((a,b) => a + b);

Edit: Using (mostly) query expression syntax

string output = (from item in input.Select((c,i) => new { c, i })
                let x = item.i == 0 
                            ? item.c.ToString() 
                            : (char.IsUpper(item.c) ? " " + item.c.ToString().ToLower() : item.c.ToString())
                select x).Aggregate((a, b) => a + b);
Anthony Pegram
+6  A: 

You can split the words using a regular expression (and little LINQ):

public string SplitAsWords(string original)
{
    var matches = Regex.Matches(original, "[A-Z][a-z]*").Cast<Match>();
    var str = string.Join(" ", matches.Select(match => match.Value));
    str = str[0] + str.Substring(1).ToLower();
    return str;
}

A usage example:

[Test]
public void Example()
{
    string str = SplitAsWords("TheQuickBrownFox");
    Assert.That(str, Is.EqualTo("The quick brown fox"));
}

An alternative implementation using regular expression can be (with no LINQ):

public string SplitAsWords(string original)
{
    var str = Regex.Replace(original, "[a-z][A-Z]", 
        new MatchEvaluator(match => match.Value.ToLower().Insert(1, " ")));

    return str;
}
Elisha
I am accepting your answer because it is nice and clean. Thank you!
Johann Blais
+1  A: 
string s = "TheQuickBrownFox";
string transformed = s.Aggregate(new StringBuilder(), (sb, c) =>
    Char.IsUpper(c) && sb.Length > 0 ? sb.AppendFormat(" {0}", Char.ToLower(c)) : sb.Append(c)).ToString();
Lee
A: 

With System.Text.RegularExpressions.Regexp

System.Text.RegularExpressions.Regex.Split("TheQuickBrownFox","([A-Z][^A-Z]+)").Where(s=>s!="");
dvhh
A: 

How about this ?

        string phrase = "TheQuickBrownFox";

        var invalidChars = from ch in phrase
                           where char.IsUpper(ch)
                           select ch;

        foreach (char ch in invalidChars)
        {
            int index = phrase.IndexOf(ch);

            phrase = phrase.Remove(index, 1);
            phrase = phrase.Insert(index, " " + ch);
        }

        Console.WriteLine(phrase);
        Console.ReadLine();
AS-CII
Won't the indexes become obsolete after the first insertion in the foreach loop?
Johann Blais
Yes, if you want you can also select only the char and then call an IndexOf inside the foreach loop.
AS-CII