21
\$\begingroup\$

In some parts of the world, phone numbers are fixed-length, apparently. That sounds boring, though.

For this challenge, your task is to take the digits of a what is presumed to be a valid phone number, and format it based on some simplified rules around what a phone number looks like.

Input format

Your input will consist of nothing except the digits 0 through 9, and the input will contain between five and ten of them (both inclusive). Every input will be a valid phone number, according to the following simplified rules:

  • A full phone number consists of eight to ten digits and can be divided into an optional area code of two to four digits (the first of which is 0), and a mandatory subscriber number of five to eight digits (the first of which is not 0)
  • When calling someone from a landline phone who has the same area code as you, you do not need to dial the area code. Thus, your program must be able to handle inputs consisting only of a subscriber number with no area code.
  • The subscriber number consists of 5 to 8 digits, and the first one isn't 0.
  • The area code, if present, begins with 0, and has 2 to 4 digits. For this challenge's purposes, the length depends on the second digit:
    • The second digit of the area code will never be 0.
    • If the second digit is 8, the entire area code will be 2 digits long (so the entire area code will be 08)
    • If the second digit is 1, 2, 3, 4, or 7, the area code will be 3 digits long.
    • If the second digit is 5, 6, or 9, the area code will be 4 digits long.
  • However, for some inputs, the above cutoff point would cause the subscriber number to be too short or begin with a 0. Since we know the input is a valid phone number (because I say so) we have to conclude that the length of the area code is different from the above. If the length listed above does not work, you should pick a length which works, preferring 3-digit codes over 4-digit codes, and preferring either of those to 2-digit codes. You will not need to handle inputs like 0801234, which are invalid for all those cutoff points.

Output format

Your output should be the area code, followed by a dash (only if an area code is present), followed by the subscriber number which should be grouped in one of the following manners (with the groups separated by a single space):

xxx xxx xx
xxx xx xx
xx xx xx
xxx xx

That means numbers with area codes specified will have one of the following formats:

0x-xxx xxx xx
0x-xxx xx xx
0x-xx xx xx
0xx-xxx xx xx
0xx-xx xx xx
0xx-xxx xx
0xxx-xx xx xx
0xxx-xxx xx

Test cases

All numbers were randomly generated, and some of them could potentially be in use, so while I don't expect any of them to belong to anyone in particular, please don't try to call them.

27983516    ->  279 835 16     # No area code, 8 digits
6278304     ->  627 83 04      # No area code, 7 digits
627830      ->  62 78 30       # No area code, 6 digits
11865       ->  118 65         # No area code, 5 digits
067934821   ->  0679-348 21    # Simple 4+5
0927586130  ->  0927-58 61 30  # Simple 4+6   
0374821096  ->  037-482 10 96  # Simple 3+7
042807961   ->  042-80 79 61   # Simple 3+6
04629873    ->  046-298 73     # Simple 3+5
0897106384  ->  08-971 063 84  # Simple 2+8
081859302   ->  08-185 93 02   # Simple 2+7
08379451    ->  08-37 94 51    # Simple 2+6
0480936572  ->  0480-93 65 72  # Area code couldn't be 048 to prevent subscriber number starting with 0
0957024168  ->  095-702 41 68  # Area code couldn't be 0957 for the same reason
06057291    ->  060-572 91     # Area code couldn't be 0605 because of subscriber number's length
04809714    ->  04-80 97 14    # Area code could neither be 048 nor 0480 didn't help
080062517   ->  0800-625 17    # Area code could neither be 08 nor 080
09106574    ->  09-10 65 74    # Area code had to be shortened twice from 0910 because shortening it to 091 didn't work

This is , shortest code in each language wins.

\$\endgroup\$
4
  • 2
    \$\begingroup\$ Why 0574821096 -> 057-482 10 96? Why not -> 0574-82 10 96? \$\endgroup\$
    – mazzy
    Nov 13, 2019 at 8:34
  • 2
    \$\begingroup\$ @mazzy Because at some point I changed the specification and forgot to update that particular test case - 0574-82 10 96 would be the correct output for that test case, thanks for pointing it out. \$\endgroup\$
    – Sara J
    Nov 13, 2019 at 8:52
  • 2
    \$\begingroup\$ Why are there no test cases for 2+5, 3+8, 4+7, 4+8? Is there an unstated rule that the total length is always 8, 9 or 10? \$\endgroup\$
    – Grimmy
    Nov 13, 2019 at 13:34
  • \$\begingroup\$ @Grimmy Err, yes, apparently that rule got lost somewhere along the way. Thanks for pointing that out. \$\endgroup\$
    – Sara J
    Nov 13, 2019 at 14:11

7 Answers 7

10
\$\begingroup\$

Retina, 78 73 bytes

(0(8|[569].?.?|..?0?)(?=[^0]....))?(...?)?(...?)(..)$
$1$#1*-$3$#3* $4 $5

Inspired by both @mazzy's PowerShell answer and @NahuelFouilleul's Perl 5 answer, so make sure to upvote them both as well!
-5 bytes thanks to @Neil.

Try it online.

Explanation:

Match the digit groups like this:

(                                )? # An optional group 1, which consists of:
 0                                  #  A leading 0
  ( |         |     )               #  followed by either (ignored inner group 2):
   8                                #   A single 8
    |[569]                          #   or one of 569,
          .?.?                      #      with one or two optional chars
              |.                    #   or any character,
                .?                  #      with one optional character,
                  0?                #      and an optional trailing 0
                     (?=        )   #  followed by an uncaptured inner group with:
                        [^0]        #   A character which is NOT a 0
                            ....    #   followed by any four characters
(    )?                             # An optional group 3, which consists of:
 ..                                 #  Two characters,
   .?                               #  followed by an optional character
(    )                              # A mandatory group 4,
 ...?                               #  which is similar as group 3
(  )                                # Followed by a mandatory group 5
    $                               # (at the end of the string), which consists of:
 ..                                 #  Any two characters

Then replace it with:

$1                                  # The content of group 1 (possibly empty)
  $#1*                              # If group 1 isn't empty:
      -                             #  A "-"
       $3                           # The content of group 3 (possibly empty)
         $#3*                       # If group 3 isn't empty:
             <sp>                   #  A " "
                 $4                 # The content of group 4
                   <sp>             # A " "
                       $5           # The content of group 5
\$\endgroup\$
3
  • \$\begingroup\$ nice .... instead .{4} \$\endgroup\$
    – mazzy
    Nov 13, 2019 at 12:22
  • 1
    \$\begingroup\$ You can use $1$#1*-$3$#3* $4 $5 to save 5 bytes (it makes the second stage unnecessary). \$\endgroup\$
    – Neil
    Nov 13, 2019 at 13:30
  • \$\begingroup\$ @Neil Ah, that's how you do it! I figured it should be possible to do something like that. I had $1$.1*-$3$.3* $4 $5 with ([- ])+ -> $1 at first, and knew it should be somehow possible to do something like "if there is a match, have a -", but I must have skipped past it in the docs. Thanks! \$\endgroup\$ Nov 13, 2019 at 13:35
8
\$\begingroup\$

Perl 5 (-p), 92, 84 bytes

s/(0(8|[569].?.?|..?0?)(?=.{5})(?!0))?(...?)?(...?)(..)$/$1-$3 $4 $5/;s/^\D*|-\K //g

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ thanks, fixed +2 bytes \$\endgroup\$ Nov 13, 2019 at 9:46
  • 1
    \$\begingroup\$ they appered after shortening but fixed \$\endgroup\$ Nov 13, 2019 at 10:18
  • \$\begingroup\$ nice -\K. █▬█ █ ▀█▀ \$\endgroup\$
    – mazzy
    Nov 13, 2019 at 10:23
7
\$\begingroup\$

PowerShell, 135 127 118 107 105 bytes

-1 byte thanks @Kevin Cruijssen

$args-replace'(0(8|[569].?.?|..?0?)(?=[^0].{4}))?(...?)?(...?)(..)$','$1-$3 $4 $5'-replace'^\D+|(?<=\D) '

Try it online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can remove the leading ^, only the trailing $ is necessary. \$\endgroup\$ Nov 13, 2019 at 10:26
3
\$\begingroup\$

Python 3.8, 554 bytes

#it's the source patterns
s=r'''xxx xxx xx
xxx xx xx
xx xx xx
xxx xx
0x-xxx xxx xx
0x-xxx xx xx
0x-xx xx xx
0xx-xxx xx xx
0xx-xx xx xx
0xx-xxx xx
0xxx-xx xx xx
0xxx-xxx xx'''.split('\n')
import re
#now we make pattern matchers for each of them,
#assuming 0 does not start a short number (^) or the first group after '-'
m=[re.compile('^'+
              re.sub(r'(^)?(\-)?(x+)',
                         lambda m:
                         (lambda x:
                          [r'(\d{%d})'%x,r'([1-9]\d{%d})'%(x-1)]
                          [(m.group(2)!=None)or(m.group(1)!=None)])
                         (len(m.group(3))),i)
              .replace(' ','')
              +'$')
   for i in s]
f=lambda i:(lambda k,v:re.sub('x+','%s',k)%v)(*(
    #then we sort matching patterns by 
    #first character in ['','8','12347','569'][len(first_group)]
    #and by len(first_group)
    #and take the last elem. It will be with key (True,n) or
    #(False,max(n)) if n is the first group's length 
    sorted(
    [(k,v.groups()) for j,k in zip(m,s) if (v:=j.match(i))],
    key=lambda x:((g:=x[1][0])[0] in ['','8','12347','569'][len(g)],
                                len(g)) )[-1]))

Try it online!
I see it can be shortened by sum(len(j)-1 for i in s for j in re.findall('x+',i))==50 bytes by replacing 'x' repeating n times by n, but I just wanted to demonstrate the approach.

\$\endgroup\$
2
\$\begingroup\$

C# (Visual C# Interactive Compiler), 246 bytes

s=>{int a=s[1]-'0',b=0,c=s.Length,d,e;bool f=s[4]==48,g=s[2]==48;var h="";if(s[0]==48){b=a==8?2:a<5|a==7?3:f?3:4;b=s[3]==48?!g&c-b<6?2:f?3:4:g?3:b;h+=s.Substring(0,b)+"-";}for(d=b;d<c-1;d+=e)h+=$"{s.Substring(d,e=c-b==6?2:c-d<5?2:3)} ";return h;}

Try it online!

int a = s[1]-'0'   //int value of second char
    b = 0,         //area code length
    c = s.Length,  //length of the input
    d,             //for loop iterator
    e;             //length of the current number group 

bool f = s[4]==48, //5th digit is 0
     g = s[2]==48; //3rd digit is 0

var h="";          //output string

if(s[0]==48)       //starts with 0 / is area code
{
    b=a==8?2:a<5|a==7?3:f?3:4;          //set area code length from subscriber number
    b=s[3]==48?g&c-b<6?2:f?3:4:!g?3:b;  //modify area code length according to special 0's
    h+=s.Substring(0,b)+"-";            //append area code to output
}

for(d=b;d<c-1;d+=e) //iterate the non area code numbers
{
    h+=$"{s.Substring(d,e=c-b==6?2:c-d<5?2:3)} "; //append number group of 'e' length
}

return h;
\$\endgroup\$
1
  • \$\begingroup\$ Suggest g|c-b>5?f?3:4:2 instead of !g&c-b<6?2:f?3:4 \$\endgroup\$
    – ceilingcat
    Oct 24, 2020 at 7:21
1
\$\begingroup\$

Python 3, 128 bytes

lambda x:re.sub(r'^\D+|(?<=\D) ','',re.sub(r'(0(8|[569].?.?|..?0?)(?=[^0].{4}))?(...?)?(...?)(..)$',r'\1-\3 \4 \5',x))
import re

Try it online!

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

JavaScript (Node.js), 284 267 bytes

(n,B=I=>+n[I]&&n.length-I>4,A=a=>a.includes(n[1]),b=B(0),L=A('8')?2:A('569')?4:3)=>(b?'':n.slice(0,B(L)?L:L=L>3?B(3)?3:2:L>2?B(4)?4:2:B(4)?4:3))+(b?'':'-')+n.slice(b?0:L).replace(/./g,(e,i,T,U=e+' ')=>T[5]?T[6]?T[7]?i==2|i==5?U:e:i==2|i==4?U:e:i==1|i==3?U:e:i==2?U:e)

Try it online!

No regex except for the basic /./g, and that's the way I want it to be.

\$\endgroup\$

Your Answer

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

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