0

Let say I've a few string: CustomerName, CustomerID, ContactNumber,...How can I transform those string to Customer Name, Customer ID, Contact Number? Is there any built-in method can be used to achieve that in c#? Or should I do it one by one manually?

YWah
  • 571
  • 13
  • 38
  • 1
    Regex? Regex.Replace(<>, @"([a-z])([A-Z])", "$1 $2") – Sebastian Schumann Jul 07 '15 at 09:01
  • I'd possibly use attributes, like the `DescriptionAttribute` (to make use of a built-in one) or better yet, implement a `FriendlyName(string name)` attribute to apply. This means not having to implement what you're suggesting altogether. – Grant Thomas Jul 07 '15 at 09:03

3 Answers3

2

There's no built-in method for what you are trying to achieve other than a regex based one (see solution 2).


Solution 1 (no regex, custom method):

static string SeparateTitleCases(this string source, string separator = " ")
{
    var result = new StringBuilder();
    char previousChar = default(char);

    for (int i = 0; i < source.Length; i++)
    {
        char currentChar = source[i];

        if (char.IsLower(previousChar) && // Previous char is lowercase
            char.IsUpper(currentChar)) // Current char is uppercase
        {
            result.Append(separator); // Append separator
        }
        result.Append(currentChar);

        previousChar = currentChar;
    }

    return result.ToString();
}

Sample usage:

Console.WriteLine("CustomerName".SeparateTitleCases()); // Customer Name
Console.WriteLine("CustomerID".SeparateTitleCases()); // Customer ID
Console.WriteLine("CustomerName CustomerID".SeparateTitleCases()); // Customer Name Customer ID

Solution 2 (regex):

string pattern = @"([^\s])([A-Z]+[a-z]*)";
string replacement = "$1 $2";            

Console.WriteLine(Regex.Replace("CustomerName", pattern, replacement)); // Customer Name
Console.WriteLine(Regex.Replace("CustomerID", pattern, replacement)); // Customer ID
Console.WriteLine(Regex.Replace("CustomerName CustomerID", pattern, replacement)); // Customer Name Customer ID
Jaanus Varus
  • 3,508
  • 3
  • 31
  • 49
  • Yes, there's something like this implemented in various libraries. I think there's no reason to implement this again. – rdoubleui Jul 07 '15 at 09:04
1

You could use this extension method:

private static readonly HashSet<UnicodeCategory> SeparatorCharCategories = new HashSet<UnicodeCategory>{ UnicodeCategory.UppercaseLetter, UnicodeCategory.TitlecaseLetter, UnicodeCategory.DecimalDigitNumber };

public static String SeparateCharCategories(this string input, string delimiter = " ")
{
    StringBuilder sb = new StringBuilder(input.Length);
    UnicodeCategory lastCharCategory = Char.GetUnicodeCategory(input[0]);
    for(int i = 0; i < input.Length; i++)
    {
        UnicodeCategory charCategory = Char.GetUnicodeCategory(input[i]);
        if (lastCharCategory != charCategory && SeparatorCharCategories.Contains(charCategory))
            sb.Append(delimiter);
        sb.Append(input[i]);
        lastCharCategory = charCategory;
    }
    return sb.ToString();
}

Your samples and other edge cases:

var items = new[] { "CustomerName", "CustomerID", "ContactNumber", "PurchaseOrders", "purchaseOrders", "The2Unlimited", "Unlimited2", "222Unlimited", "The222Unlimited", "Unlimited222", "ATeam", "TheATeam", "TeamA", "HTMLGuide", "TheHTMLGuide", "TheGuideToHTML", "HTMLGuide5", "TheHTML5Guide", "TheGuideToHTML5", "TheUKAllStars", "AllStarsUK", "UKAllStars" };
foreach (string str in items)
    Console.WriteLine(str.SeparateCharCategories(" "));

You see that acronyms are not supported, so HTMLGuide remains HTMLGuide:

Customer Name
Customer ID
Contact Number
Purchase Orders
purchase Orders
The 2 Unlimited
Unlimited 2
222 Unlimited
The 222 Unlimited
Unlimited 222
ATeam
The ATeam
Team A
HTMLGuide
The HTMLGuide
The Guide To HTML
HTMLGuide 5
The HTML 5 Guide
The Guide To HTML 5
The UKAll Stars
All Stars UK
UKAll Stars
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • What to expect from `"BuildABear"` or `"RentAHouse"`? _Addition:_ `"CustomerIDNumber"` – Jeppe Stig Nielsen Jul 07 '15 at 09:20
  • [`IAMan`](https://en.wikipedia.org/wiki/I,_a_Man)? – Jeppe Stig Nielsen Jul 07 '15 at 09:25
  • This method returns `Customer IDNumber`, not sure what OP expects. – Tim Schmelter Jul 07 '15 at 09:35
  • 1
    @JeppeStigNielsen: what do you expect from `"CUSTOMER"`? Surely not `"C U S T O M E R"` – Tim Schmelter Jul 07 '15 at 10:09
  • You get my (unclear) point. It is not really well-defined what we want. I would expect `"CustomerIDNumber"` to become `"Customer ID Number"`, and [`"ASCIIEncoding"`](https://msdn.microsoft.com/en-us/library/system.text.asciiencoding.aspx) to become `"ASCII Encoding"`. But consecutive one-letter words as in `"IAMan"` ("I, a man") we cannot handle, so that would become `"IA Man"`, not `"I A Man"`. (English does not have that many one-letter words.) By the way, the naming `ASCIIEncoding` is bad. For acronyms with three or more letters, one should use the form `AsciiEncoding` instead. – Jeppe Stig Nielsen Jul 07 '15 at 10:36
  • Also, one could consider numerals, as in [`"UTF8Encoding"`](https://msdn.microsoft.com/en-us/library/system.text.utf8encoding.aspx) or [`"SHA384Cng"`](https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha384cng.aspx). – Jeppe Stig Nielsen Jul 07 '15 at 10:50
  • [This answer from a linked thread](http://stackoverflow.com/a/26876094/1336654) contains a list of test cases. If we cannot define the behavior we want, we can at least require that the test cases from that list pass. – Jeppe Stig Nielsen Jul 07 '15 at 11:04
  • This approach doesn't support acronyms. Period ;-) OP hasn't asked for a solution that supports acronyms (which language btw?). If he also wants to separate other characters like digits he could modify the `if(wasLower && (charType == ...`-part, f.e. to include `UnicodeCategory.DecimalDigitNumber`. – Tim Schmelter Jul 07 '15 at 11:13
  • @JeppeStigNielsen: i have edited my answer anyway. This approach is easier and more flexible. – Tim Schmelter Jul 07 '15 at 11:41
0

I'm assuming that CustomerName, CustomerID are property names? You would need some way to reflect those names and you could then use the Humanizer Lib to add the whitespaces.

Use it like this:

"CustomerName".Humanize(LetterCasing.Title) => "Customer Name";
rdoubleui
  • 3,554
  • 4
  • 30
  • 51