18
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Can we return two consistent values instead of truthy/falsey? \$\endgroup\$ – Adám Nov 4 at 21:54
  • \$\begingroup\$ @Adám Such as? I'd be fine with t/f or true/false \$\endgroup\$ – Redwolf Programs Nov 7 at 13:27
  • \$\begingroup\$ How about "  " for true and "" for false? \$\endgroup\$ – Adám Nov 7 at 13:30
  • \$\begingroup\$ @Adám I'd be fine with that, if it's the fairest solution in whatever language you are using \$\endgroup\$ – Redwolf Programs Nov 7 at 13:31
  • \$\begingroup\$ I don't know how to judge fairest, but it is quite common here to allow any two values for decision-problem challenges. It definitely isn't the normal true and false. \$\endgroup\$ – Adám Nov 7 at 13:32

16 Answers 16

4
\$\begingroup\$

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)
\$\endgroup\$
4
\$\begingroup\$

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!

\$\endgroup\$
4
\$\begingroup\$

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 ·.

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

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.

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

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!

\$\endgroup\$
  • \$\begingroup\$ I like your RegEx. Do you mind if I use it for an answer, with proper attribution to you? \$\endgroup\$ – Ismael Miguel Nov 6 at 16:34
  • \$\begingroup\$ Heh, I think everyone ended up using the same regex in this challenge :) \$\endgroup\$ – XMark Nov 7 at 0:07
  • \$\begingroup\$ Thank you! I went ahead and used it. \$\endgroup\$ – Ismael Miguel Nov 7 at 9:55
2
\$\begingroup\$

JavaScript (ES6), 37 bytes

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

Try it online!

\$\endgroup\$
  • 2
    \$\begingroup\$ Out of interest, why wouldn't /^\d{3}(\D*)\d{3}\1\d{4}$/.test work as a standalone function? \$\endgroup\$ – caird coinheringaahing Nov 4 at 22:28
  • \$\begingroup\$ @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. \$\endgroup\$ – Arnauld Nov 4 at 22:56
  • \$\begingroup\$ @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 \$\endgroup\$ – Arnauld Nov 4 at 23:07
  • \$\begingroup\$ @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... \$\endgroup\$ – Redwolf Programs Nov 5 at 13:32
  • \$\begingroup\$ @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. \$\endgroup\$ – Arnauld Nov 5 at 15:27
2
\$\begingroup\$

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!

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

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!

\$\endgroup\$
  • \$\begingroup\$ Huh, 0 is truthy? That must get annoying (especially in code golf) \$\endgroup\$ – Redwolf Programs Nov 5 at 13:34
  • \$\begingroup\$ 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.) \$\endgroup\$ – Value Ink Nov 5 at 13:56
1
\$\begingroup\$

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.

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

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

\$\endgroup\$
  • 1
    \$\begingroup\$ re.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 \$\endgroup\$ – Value Ink Nov 5 at 2:51
  • 1
    \$\begingroup\$ Also 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. \$\endgroup\$ – Value Ink Nov 5 at 2:53
  • \$\begingroup\$ @Arnauld you right thank you for the suggestion \$\endgroup\$ – Delta Nov 5 at 8:49
  • \$\begingroup\$ @ValueInk I was not sure if None ist falsey and an Object truthy... Thank yoo for pointing that out :) \$\endgroup\$ – Delta Nov 5 at 8:50
1
\$\begingroup\$

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.

\$\endgroup\$
0
\$\begingroup\$

Retina 0.8.2, 24

Same regex as the other regex-based answers:

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

Try it online!

\$\endgroup\$
  • \$\begingroup\$ Aw, if only I had allowed omitting the trailing line break… \$\endgroup\$ – Adám Nov 7 at 13:52
0
\$\begingroup\$

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

\$\endgroup\$
0
\$\begingroup\$

Clojure, 46 bytes

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

Try it online!

Another day, another regex.

\$\endgroup\$
0
\$\begingroup\$

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.

\$\endgroup\$
0
\$\begingroup\$

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 ·.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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