2
\$\begingroup\$

I wrote a function that takes an array of 10 integers (from 0 to 9) that returns a string of those numbers as a phone number.

Example:

Kata.CreatePhoneNumber(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0})
// => returns "(123) 456-7890".

The program is fully working, but I want to make this code shorter and clearer.

class Program
{
    static void Main()
    {
        int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
        CreatePhoneNumber(numbers); // => returns "(123) 456-7890"
    }

    public static string CreatePhoneNumber(int[] numbers)
    {
        return ($"({numbers[0]}{numbers[1]}{numbers[2]}) {numbers[3]}{numbers[4]}{numbers[5]}-{numbers[6]}{numbers[7]}{numbers[8]}{numbers[9]}");
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Why would you get an array of ints? A phone number is a string. \$\endgroup\$
    – BCdotWEB
    Feb 20, 2022 at 14:34
  • \$\begingroup\$ Using an int array is a really inefficient method. You only need 4 bits to denote the 10 possible digits; and each int is a 32 bit representation. A string makes much more sense here. \$\endgroup\$
    – Flater
    Mar 1, 2022 at 9:32

4 Answers 4

4
\$\begingroup\$

Two things I'd do there:

  1. Pull out the parts into local vars
  2. use the range operator

So more like this:

public static string CreatePhoneNumber(int[] numbers)
{
   var areaCode = string.Concat(numbers[0..3]);
   var middlePart = string.Concat(numbers[3..6]);
   var lastPart = string.Concat(numbers[6..]);
   return $"({areaCode}) {middlePart}-{lastPart}";
}

This makes the code not shorter (more lines) but much clearer in my eyes. The line that adds the formatting stuff is short and one can easily see the parenthesis and dash getting added.

\$\endgroup\$
3
\$\begingroup\$

This one is probably the most effective solution.

public static string CreatePhoneNumber(int[] digits)
{
    const string template = "(###) ###-####";
    return string.Create(template.Length, digits, (span, d) =>
    {
        int j = 0;
        for (int i = 0; i < span.Length; i++)
        {
            char c = template[i];
            span[i] = c switch
            {
                '#' => (char)(d[j++] + '0'),
                _ => c
            };
        }
    });
}

Also you can easily move the template to method's argument to format the other types of phones.

I know that it isn't the short one but the effectiveness of the code is important too. The slowest part of other solutions including initial one is (implicitly) calling .ToString() for each digit which means 10 redundant string allocations in memory. The accepted answer generates redundant 13 strings. My answer creates no redundant strings but only one string instance to return.

Just for fun, here's an alternative solution

static string CreatePhoneNumber(int[] digits)
{
    const string template = "(000) 000-0000";
    long num = 0;
    long offset = 1;
    for (int i = digits.Length - 1; i >= 0 ; i--)
    {
        num += digits[i] * offset;
        offset *= 10;
    }
    return num.ToString(template);
}

Custom numeric format strings

If the input was a number not array, the whole solution would be:

long number = 1234567890;
Console.WriteLine(number.ToString("(000) 000-0000");
\$\endgroup\$
1
  • 1
    \$\begingroup\$ I agree that my solution is not the most memory efficient, but just as a reference point it can still do 1 million phone numbers per second on my machine :-) \$\endgroup\$ Feb 28, 2022 at 11:56
1
\$\begingroup\$

The current code works for the North American number format.

A good exercise would be to allow the user to supply a template string representing the number format for the desired region.

(I know that's not making the code shorter, but I hope there's a learning opportunity here about assuming that one's regional conventions are actually universal.)

\$\endgroup\$
1
\$\begingroup\$

string.Format has an overload that takes object[] which it can be used to give you the desired results something like this :

public static string CreatePhoneNumber(int[] numbers)
{
    return string.Format("({0}{1}{2}) {3}{4}{5}-{6}{7}{8}{9}", numbers.Cast<object>().ToArray());
}

you can go further by expanding it with a little help of enum you can do something like this :

public enum PhoneNumberFormat
{
    NorthAmerican,
    OtherFormat
}

public static string CreatePhoneNumber(int[] numbers, PhoneNumberFormat format)
{
    string template = null;
    
    switch(format)
    {
        case PhoneNumberFormat.NorthAmerican:
            template = "({0}{1}{2}) {3}{4}{5}-{6}{7}{8}{9}";
            break;
        case PhoneNumberFormat.OtherFormat:
            template = "{0}{1}{2}-{3}{4}{5}{6}-{7}{8}{9}";
            break;  
        default: 
            return null;
    }
    
    return string.Format(template, numbers.Cast<object>().ToArray())
}
\$\endgroup\$
0

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.