Check the validity of a 10-digit telephone number

21

Your task is to write a program or function that checks if a string is a valid phone number.

Given a string (or list of characters/codepoints) input, you will output a truthy value if the input is a valid phone number. If it is not, output a falsey value. An input string is considered a valid phone number for this challenge if it meets all of these criteria:

  • It contains ten digits, which are grouped like this: ###-###-####
  • The strings separating the digit groups must be the same
  • The first and last characters must be part of the phone number (i.e. must not be part of a separator)

Test cases:

111-222-3333     truthy
123-456-7890     truthy (number groups can contain different numbers)
1112223333       truthy (can have 0 length separators)
111022203333     falsey (has more than 10 numbers)
111ABC222ABC3333 truthy (can have length>1 separators)
111-222_3333     falsey (non-matching separators)
111-22-23333     falsey (missing digit in middle group)
111--222--33333  falsey (extra digit in last group)
-111-222-3333    falsey (first character is not part of number)
111-222-3333a    falsey (last character is not part of number)
aaa-aaa-aaaa     falsey (not enough numbers)
11-2222-3333     falsey (wrong separator location)
111-1-222-1-3333 falsey (too many numbers)

This is a code golf challenge, shortest answer per language wins.

Redwolf Programs

Posted 2019-11-04T21:38:30.347

Reputation: 2 561

Can we return two consistent values instead of truthy/falsey? – Adám – 2019-11-04T21:54:57.480

@Adám Such as? I'd be fine with t/f or true/false – Redwolf Programs – 2019-11-07T13:27:35.467

How about "  " for true and "" for false? – Adám – 2019-11-07T13:30:22.477

@Adám I'd be fine with that, if it's the fairest solution in whatever language you are using – Redwolf Programs – 2019-11-07T13:31:26.833

I don't know how to judge fairest, but it is quite common here to allow any two values for [tag:decision-problem] challenges. It definitely isn't the normal true and false. – Adám – 2019-11-07T13:32:23.477

@Adám Makes sense. I'd accept " " and "", especially since I consider an empty string to be falsey anyway – Redwolf Programs – 2019-11-07T13:34:38.537

Answers

4

APL (Dyalog Unicode), 30 29 bytesSBCS

'^\d{3}(\D*)\d{3}\1\d{4}$'⎕S⍬

It simply ⎕Searches for the PCRE regex and returns a dummy value (the empty numeric list) for each match. If none are found, it returns a single empty numeric list.

Try it online! The effect of printing a list of empty lists is to print two spaces on a line, while the effect of printing an empty list is to print an empty line. Select all contents in the Output field to reveal the spaces, or use this to lead each printed line with and each space with ·.

Adám

Posted 2019-11-04T21:38:30.347

Reputation: 37 779

4

Jelly, 22 bytes

fØDȯ0ṁ334DR¤jḟØDŒHḢƊµƑ

A monadic Link accepting a list of characters which yields 1 if it is a valid telephone number and 0 otherwise.

Try it online! Or see a test-suite.

How?

fØDȯ0ṁ334DR¤jḟØDŒHḢƊµƑ - Link: list of characters, S  e.g. '123a456bc78de'
                     Ƒ - is S invariant under...
                    µ  - ...this function of S?:
f                      -   filter keep:
 ØD                    -     digit characters              '12345678'
   ȯ0                  -   OR zero (necessary as ṁ will error with an empty list)
           ¤           -   nilad followed by link(s) as a nilad:
      334              -     literal 334                   334
         D             -     to decimal                    [3,3,4]
          R            -     range (vectorises)            [[1,2,3],[1,2,3],[1,2,3,4]]
     ṁ                 -   mould (left) like (right)       ['123','456','7812']
                   Ɗ   -   last three links as a monad  - i.e. f(S):
             ḟ         -     filter discard:
              ØD       -       digit characters           'abcde'
                ŒH     -     split in halves              ['abc', 'de']
                  Ḣ    -     head (zero if empty)         'abc'
            j          -   join                           '123abc456abc7812'
                                                  (...not invariant as not equal to S)

Jonathan Allan

Posted 2019-11-04T21:38:30.347

Reputation: 67 804

4

Perl, 36 bytes

print/^\d{3}(\D*)\d{3}\1\d{4}$/?1:0

Run as

perl -ne 'print/^\d{3}(\D*)\d{3}\1\d{4}$/?1:0' <<< "111-222-3333"

Try it online!

stephanmg

Posted 2019-11-04T21:38:30.347

Reputation: 261

329 bytes – Nahuel Fouilleul – 2019-11-05T11:07:27.273

3

05AB1E, 16 bytes

þ©м2äн®T∍ƵêS£sýQ

Port of @JonathanAllan's Jelly answer.
Thanks to @Grimy for fixing a bug.

Try it online or verify all test cases.

Explanation:

þ                 # Only leave the digits of the (implicit) input-string
 ©                # Store it in variable `®` (without popping)
  м               # Remove all digits from the (implicit) input-string
   2ä             # Split the remaining characters into two equal-sized parts
     н            # And only leave the first part
      ®           # Push the digits from variable `®` again
       T∍         # Extend this string of digits to size 10
         Ƶê       # Push compressed integer 334
           S      # Converted to a list of digits: [3,3,4]
            £     # And split the digits of the input into parts of that size
             s    # Swap to get the earlier string
              ý   # And join the digit-parts with this delimiter-string
               Q  # Check if it's now equal to the (implicit) input-string
                  # (after which the result is output implicitly)

See this 05AB1E tip of mine (section How to compress large integers?) to understand why Ƶê is 334.

Kevin Cruijssen

Posted 2019-11-04T21:38:30.347

Reputation: 67 575

3

PHP, 50 bytes

Just a simple regex, with a backreference to make sure the separator is the same.

<?=preg_match('/^\d{3}(\D*)\d{3}\1\d{4}$/',$argn);

Try it online!

XMark

Posted 2019-11-04T21:38:30.347

Reputation: 141

I like your RegEx. Do you mind if I use it for an answer, with proper attribution to you? – Ismael Miguel – 2019-11-06T16:34:42.193

Heh, I think everyone ended up using the same regex in this challenge :) – XMark – 2019-11-07T00:07:02.900

Thank you! I went ahead and used it. – Ismael Miguel – 2019-11-07T09:55:42.380

2

JavaScript (ES6), 37 bytes

s=>/^\d{3}(\D*)\d{3}\1\d{4}$/.test(s)

Try it online!

Arnauld

Posted 2019-11-04T21:38:30.347

Reputation: 111 334

2Out of interest, why wouldn't /^\d{3}(\D*)\d{3}\1\d{4}$/.test work as a standalone function? – caird coinheringaahing – 2019-11-04T22:28:41.260

@cairdcoinheringaahing f=/^\d{3}(\D*)\d{3}\1\d{4}$/.test is only assigning the method test from the RegExp object to f. Something that would work is this.

– Arnauld – 2019-11-04T22:56:59.427

@cairdcoinheringaahing Or this for 41 bytes, but that's still quite lengthy. Ironically, we can't do //.test.bind(...) because it would be a comment. :p

– Arnauld – 2019-11-04T23:07:24.570

@Arnauld But isn't that what this is doing? Outputting the output of the test method on a certain input? It seems the exact same as /^\d{3}(\D*)\d{3}\1\d{4}$/.test to me... – Redwolf Programs – 2019-11-05T13:32:59.587

@RedwolfPrograms The problem is that the value of this is lost. In other words, you get a reference to the test method without any regular expression attached to it.

– Arnauld – 2019-11-05T15:27:18.957

@Arnauld Now I feel stupid, just tested it and you're absolutely right – Redwolf Programs – 2019-11-05T16:20:51.480

@RedwolfPrograms For it to work, you would need to do (s,r=/^\d{3}(\D*)\d{3}\1\d{4}$/)=>r.test.call(r,s), which is an invalid and longer answer. – Ismael Miguel – 2019-11-06T16:37:44.227

2

Gema, 26 characters

<D3><-d><D3>$2<D4>\Z=t
*=f

There is no truthy / falsey concept in Gema, so just outputting “t” / “f”.

Sample run:

bash-5.0$ echo -n 111-222-3333 | gema '<D3><-d><D3>$2<D4>\Z=t;*=f'
t

Try it online! / Try all test cases online!

manatwork

Posted 2019-11-04T21:38:30.347

Reputation: 17 865

1

Python 3, 69 66 57 bytes

lambda n:re.match(r"\d{3}(\D*)\d{3}\1\d{4}$",n)
import re

Try it online!

Thanks To:
-@Arnauld and @Value Ink for saving me 3 bytes by using match instead of fullmatch
-@Value Ink for saving me another 6 bytes

Delta

Posted 2019-11-04T21:38:30.347

Reputation: 543

1re.match actually already anchors to the beginning of the string, so you just need the $ and you save 3 bytes. But other than that I agree with @Arnauld – Value Ink – 2019-11-05T02:51:39.800

1Also since the match object is truthy and None is falsey, I think you can cut !=None entirely since there's no mandate for you to have 2 specific values to distinguish true and false. – Value Ink – 2019-11-05T02:53:57.973

@Arnauld you right thank you for the suggestion – Delta – 2019-11-05T08:49:31.707

@ValueInk I was not sure if None ist falsey and an Object truthy... Thank yoo for pointing that out :) – Delta – 2019-11-05T08:50:59.323

1

Ruby -n, 28 bytes

Same as basically every other regex solution here. Note that in Ruby, 0 is actually truthy. (Only false and nil are falsey in Ruby)

p~/^\d{3}(\D*)\d{3}\1\d{4}$/

Try it online!

Value Ink

Posted 2019-11-04T21:38:30.347

Reputation: 10 608

Huh, 0 is truthy? That must get annoying (especially in code golf) – Redwolf Programs – 2019-11-05T13:34:03.870

Sometimes it's a problem, sometimes it it's a benefit. For example, this very program -- the regex check operator string =~ regex returns the index of the first match, or nil if not found, and there's no need to do an explicit check for the "invalid" value because 0 is already truthy :D (In -n mode, ~regex is an alias for $_ =~ regex where $_ is the last read line from STDIN, which is fetched automatically.) – Value Ink – 2019-11-05T13:56:02.213

1

Red, 64 bytes

func[s][d: charset[#"0"-#"9"]parse s[3 d copy t to d 3 d t 4 d]]

Try it online!

As always Red's parse is much longer than regex.

Galen Ivanov

Posted 2019-11-04T21:38:30.347

Reputation: 13 815

1

Java (JDK), 43 bytes

s->s.matches("\\d{3}(\\D*)\\d{3}\\1\\d{4}")

Try it online!

Apparently I came to the same solution as others: a simple regex is the key.

Olivier Grégoire

Posted 2019-11-04T21:38:30.347

Reputation: 10 647

0

QuadS, 28 25 bytesSBCS

^\d{3}(\D*)\d{3}\1\d{4}$

It simply ⎕Searches for the PCRE regex and returns a dummy value (the empty string) for each match. If none are found, it returns a single empty numeric list.

Try it online! The effect of printing a list of empty strings is to print two spaces on a line, while the effect of printing an empty list is to print an empty line. Select all contents in the Output field to reveal the spaces, or use this to lead the printed line with and each space with ·.

Adám

Posted 2019-11-04T21:38:30.347

Reputation: 37 779

0

Retina 0.8.2, 24

Same regex as the other regex-based answers:

^\d{3}(\D*)\d{3}\1\d{4}$

Try it online!

Digital Trauma

Posted 2019-11-04T21:38:30.347

Reputation: 64 644

Aw, if only I had allowed omitting the trailing line break… – Adám – 2019-11-07T13:52:04.243

0

Kotlin (JDK), 60 bytes

{s: String->s.matches(Regex("\\d{3}(\\D*)\\d{3}\\1\\d{4}"))}

Try it online!

Really similar to the Java one

Uriel Salischiker

Posted 2019-11-04T21:38:30.347

Reputation: 1

0

Clojure, 46 bytes

(fn[x](re-matches #"\d{3}(\D*)\d{3}\1\d{4}"x))

Try it online!

Another day, another regex.

Thoma

Posted 2019-11-04T21:38:30.347

Reputation: 71

0

SimpleTemplate, 56 bytes

This was actually a very simple challenge, using XMark's RegEx in the PHP answer.

{@ifargv.0 is matches"@^\\d{3}(\\D*)\\d{3}\\1\\d{4}$@"}1

Sadly, due to the need to escape the slashes, this code is 5 bytes longer :/

This code outputs 1 in case the input matches (truthy), or nothing (falsy).

You can try the golfed and ungolfed versions in http://sandbox.onlinephpfunctions.com/code/ca206ad7cc82351283e6c319ce5f9ae03f87b695
You can also check the generated PHP code, if you are curious.

Ismael Miguel

Posted 2019-11-04T21:38:30.347

Reputation: 6 797