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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented 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\$ Commented Nov 13, 2019 at 9:46
  • 1
    \$\begingroup\$ they appered after shortening but fixed \$\endgroup\$ Commented Nov 13, 2019 at 10:18
  • \$\begingroup\$ nice -\K. █▬█ █ ▀█▀ \$\endgroup\$ Commented 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\$ Commented 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\$ Commented Oct 24, 2020 at 7:21
2
\$\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\$
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\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.