Letters in phone numbers

23

2

Problem:

You are making a new phone where people can type in specialized phone numbers, for example, 1-800-program, and they would be converted automatically to a usable phone number, like 1-800-7764726 (for the previous example).

Your program will recieve a string if any length with numbers, letters and dashes, and convert all the letters to their corresponding numbers.

Here is a keypad, for reference:

keypad

Rules:

  • Your program will receive a string
  • It will process it and return/print another string
  • Any language is accepted
  • Since it is , the shortest code wins

TheDoctor

Posted 2014-02-18T23:51:28.397

Reputation: 7 793

Should the program handle both upper and lower case letters in the input? – mattnewport – 2014-02-19T00:38:10.300

3@mattnewport - no, assume the variable has been turned into lowercase already – TheDoctor – 2014-02-19T03:36:57.457

Answers

8

GolfScript, 24 chars

{.96>{,91,'qx'+-,3/`}*}%

Test input:

0123456789-abcdefghijklmnopqrstuvwxyz

Test output:

0123456789-22233344455566677778889999

Explanation:

  • { }% applies the code between the braces to each character of the input.

  • .96>{ }* executes the code between the inner braces if and only if the ASCII code of the character is greater than 96 (i.e. it is a lowercase letter).

  • The first , turns the character into a list of all characters with lower ASCII codes, and 91,'qx'+- filters out all characters with ASCII codes less than 91, as well as the letters q and x, from the list. Thus, for example, the character a gets turned into the 6-character list [\]^_`, while z gets turned into the 29-character list [\]^_`abcdefghijklmnoprstuvwy.

  • The second , counts the elements remaining in the list, and 3/ divides this count by three (rounding down). Finally, the ` turns the resulting number (in the range 2 – 9) into a string.

Thus, as per spec, hyphens and numbers are left unchanged, while lowercase letters are mapped into numbers according to the reference keypad diagram. The code will actually cleanly pass through all printable ASCII characters except for lowercase letters (which as mapped as described) and the characters {, | and } (which are mapped to the two-character string 10). Non-ASCII 8-bit input will produce all sorts of weird numeric output.

After all this, it's a bit disappointing that this only beats the trivial bash solution by just six chars.

Ilmari Karonen

Posted 2014-02-18T23:51:28.397

Reputation: 19 513

50

Bash, 30

Edit: Thank you Doorknob for eliminating 3 chars

tr a-z 22233344455566677778889

Example:

user12205

Posted 2014-02-18T23:51:28.397

Reputation: 8 752

10Can't you remove the last 3 9s? – Doorknob – 2014-02-19T01:01:00.123

16

C, 83 78 77 65 63 62

main(c){for(;~(c=getchar());putchar(c>96?20-c/122+5*c/16:c));}

http://ideone.com/qMsIFQ

mattnewport

Posted 2014-02-18T23:51:28.397

Reputation: 1 579

3Nice math. Just wanna say that you can reduce 1 char by assuming EOF is -1 and do ~(c=getchar()) – user12205 – 2014-02-19T08:31:13.400

Couldn't you use getch() instead of getchar()? – starsplusplus – 2014-02-19T10:15:43.457

Strictly speaking, getch() isn't standard C, as a result I guess it doesn't link in ideone. I tested it in MSVC anyway though and it doesn't really work sadly - as it directly consumes keyboard input there's no way to exit the program, although it does translate what you type on the fly which is kind of neat. – mattnewport – 2014-02-19T16:05:27.793

4

Javascript - 103 characters

alert(prompt().replace(/[a-z]/g,function(y){y=y.charCodeAt(0)-91;return y>27?9:y>24?8:y>20?7:~~(y/3)}))

Victor Stafusa

Posted 2014-02-18T23:51:28.397

Reputation: 8 612

1I didn't know you could do that with .replace. Up vote for you! – SuperJedi224 – 2015-06-11T21:56:58.660

You can replace charCodeAt(0) with charCodeAt() and you can use arrow function for function(y)... to save few bytes and for ~~(y/3) you can use y/3|0 – chau giang – 2019-09-20T10:49:14.607

3

Ruby, 75 chars

gets.chars{|c|$><<"22233344455566677778889999#{c}"[[*?a..?z].index(c)||-1]}

Uses the deprecated chars with block, and prints each letter individually with $><<. I also like [[*?a..?z].index(c)||-1]; it grabs the character corresponding to that letter of the alphabet if it's a letter, and the last character (which happens to be the test character unchanged) if not.

Ruby, 43 (or 35) chars

Blatantly stealing from @ace ;)

puts gets.tr'a-z','22233344455566677778889'

Shave off 8 chars if I can run in IRB with the variable s as the string:

s.tr'a-z','22233344455566677778889'

Doorknob

Posted 2014-02-18T23:51:28.397

Reputation: 68 138

3

C++ - 222 chars

Longest solution so far:

#include<iostream>
#include<string>
#define o std::cout<<
int main(){std::string s;std::cin>>s;for(int i=0;i<s.size();i++){int j=s[i]-97;if(j<0)o s[i];if(0<=j&j<15)o 2+j/3;if(14<j&j<19)o 7;if(18<j&j<22)o 8;if(21<j&j<26)o 9;}}

user10766

Posted 2014-02-18T23:51:28.397

Reputation:

1Lol, I don't think longest solution is the goal here... – Danny – 2014-02-19T14:36:01.113

@Danny C++ does not lend itself well to [tag:code-golf]. Java and C# are the only languages that are worse, that I know of (all the classes, object creation, and long names for output...). – None – 2014-02-19T15:53:26.317

I know, I just thought it was funny that you mentioned "Longest solution". – Danny – 2014-02-19T15:55:00.133

3

Frink, 92

A rather verbose language, I know. This checks 8 values instead of 26 without having to type out the compares. Can any of the above "222333444.." solutions be reduced in a similar way?

Using built in structures, 107

co=new OrderedList
co.insertAll[charList["cfilosv{"]]
println[input[""]=~%s/([a-z])/co.binarySearch[$1]+2/eg]

Using a custom recursive function, 92

fn[x,a]:=x<=(charList["cfilosv{"])@a?a+2:fn[x,a+1]
println[input[""]=~%s/([a-z])/fn[$1,0]/eg]

maybeso

Posted 2014-02-18T23:51:28.397

Reputation: 51

+1 for reducing the string-translation method to an 8-character search. Nice touch. – Jonathan Van Matre – 2014-02-25T01:23:55.873

2

Smalltalk, 79 70

input is s:

s collect:[:c|' 22233344455566677778889999'at:1put:c;at:(($ato:$z)indexOf:c)+1]

probably not a candidate for being shortest - but may be of interest for an old trick to avoid a test for a not-found condition (indexOf: returns 0 in this case). So no special test for letters is needed. Some Smalltalks however, have immutable strings, and we need 4 more chars ("copy").

Oh, a better version, which even deals with immutable strings in 70 chars:

s collect:[:c|c,'22233344455566677778889999'at:(($ato:$z)indexOf:c)+1]

blabla999

Posted 2014-02-18T23:51:28.397

Reputation: 1 869

2

Mathematica 90

This follows the logic of @ace's solution:

StringReplace[#,Thread[CharacterRange["A","Z"]->Characters@"22233344455566677778889999"]]&

Example

StringReplace[#1,Thread[CharacterRange["A","Z"]-> 
Characters@"22233344455566677778889999"]]&["VI37889"]

8437889

DavidC

Posted 2014-02-18T23:51:28.397

Reputation: 24 524

Your arrow character representation isn't accepted by Mma in a copy/paste – Dr. belisarius – 2014-02-19T03:18:42.577

Also,you don't need the 1 in #1:) – Dr. belisarius – 2014-02-19T03:19:48.627

Belisarius, I changed the arrow back and removed the 1. Still 90 char but cut and paste will work. You understand of course the motivation to use the single char arrow. – DavidC – 2014-02-19T10:36:56.910

Been there, done that :) – Dr. belisarius – 2014-02-19T14:06:26.753

2

Perl, 50

Another obvious copy of Ace's bash answer

($_)=@ARGV;y/a-z/22233344455566677778889999/;print

Rob Hoare

Posted 2014-02-18T23:51:28.397

Reputation: 141

1This code is working correctly but there's room for improvement. Let's get rid of $ARGV[0] and use -p switch instead which lets you nicely go through each line of stdin. While we're at it, the range in y/// doesn't need to be put in square brackets. We can also get rid of three 9's leaving just one and remove the final semi-colon:

`-p y/a-z/22233344455566677778889/`

There you go, 30 + 1 for -p. Thank you for using Enterprise Chinese Perl Golfing and Optimization Services and have a nice day. – chinese perl goth – 2014-02-19T14:11:08.897

2

PHP, 141

Not the shortest, but more fun:

<?php foreach(str_split($argv[1])as$c){$v=ord($c);if($v>114){$v--;}if($v==121){$v--;}if($v<123&$v>96){echo chr(ceil($v/3+17));}else{echo$c;}}

More readable:

<?php 
foreach (str_split($argv[1]) as $c) {
  $v=ord($c);
  if ($v>114) {$v--;}
  if ($v==121){$v--;}
  if ($v<123 & $v>96){
    echo chr(ceil($v/3+17));
    } else {echo $c;}
}

Rob Hoare

Posted 2014-02-18T23:51:28.397

Reputation: 141

OP said that the input is already in lowercase, so you can remove the strtolower – Einacio – 2014-02-20T19:00:56.453

2

R, very long but fun

foo <- '1-800-splurghazquieaobuer57'
oof <- unlist(strsplit(foo,''))
#don't count that part - it's input formatting :-) 
digout <- unlist(strsplit('22233344455566677778889999','')) 
oof[oof%in%letters[1:26]] <- unlist(sapply(oof[oof%in%letters[1:26]], function(j) digout[which(letters[1:26]==j)] ))

Carl Witthoft

Posted 2014-02-18T23:51:28.397

Reputation: 133

2

k [32 Chars]

{(.Q.a!|,/(4 3 4,5#3)#'|$2+!8)x}

Usage

{(.Q.a!|,/(4 3 4,5#3)#'|$2+!8)x}"stack exchange"
"78225 39242643"

nyi

Posted 2014-02-18T23:51:28.397

Reputation: 448

2

JavaScript, 85

JavaScript is never going to win the golf wars, but I like it and I wanted to do something different than jump on the @ace bandwagon.

alert(prompt().replace(/[a-z]/g,function(a){for(i=7;a<"dgjmptw{"[i--];);return i+4}))

George Reith

Posted 2014-02-18T23:51:28.397

Reputation: 2 424

2

Python 2.7, 80

for c in raw_input():print'\b'+(`(ord(c)-97)/3+2-(c in('svyz'))`if c>'`'else c),

I am new to python, so I'm sure there must be a way to golf this even further, it's a different aproach, hope you guys like it, my god, is python pretty!

Run example:

  • input: 01-800-abcdefghijklmnopqrstuvwxyz
  • output: 01-800-22233344455566677778889999

jsedano

Posted 2014-02-18T23:51:28.397

Reputation: 1 607

2

T-SQL, 216 bytes

I spent quite some time over the past couple of nights painstakingly creating a mathematical sequence function that would round correctly to generate the proper ASCII codes for the numbers from the alphabetical ASCII codes. It had a ridiculous number of decimal places in the coefficients, but it worked.

However, mattnewport's rational approach works in SQL as well, at a much lower cost of bytes, so I am shamelessly scrapping my own math in favor of his. Go up-vote him, it's an elegant solution!

Here's mine:

DECLARE @p VARCHAR(MAX)='';WITH t AS(SELECT ASCII(LEFT(@s,1))c,2 i UNION ALL SELECT ASCII(SUBSTRING(@s,i,1)),i+1FROM t WHERE i<=LEN(@s))SELECT @p=@p+CHAR(CASE WHEN c>96THEN 20-c/122+5*c/16 ELSE c END)FROM t;SELECT @p

This uses a recursive CTE to make an impromptu stack of the characters in the phone number and translate the letters on the fly, then a bit of SQL trickery (SELECT @p=@p+columnValue) to recompose the string from the CTE without requiring another recursion construct.

Output:

DECLARE @s VARCHAR(MAX)='1-800-abcdefghijklmnopqrstuvwxyz'
--above code runs here
1-800-22233344455566677778889999

Jonathan Van Matre

Posted 2014-02-18T23:51:28.397

Reputation: 2 307

2

Python 2.7, 66 65


Anakata's Original

for c in raw_input():print'\b'+(`(ord(c)-97)/3+2-(c in('svyz'))`if c>'`'else c),


Further golfed

for c in input():print(ord(c)-91)/3-(c in('svyz'))if c>'`'else c,


I don't have enough reputation to comment on @anakata's answer, so I made a separate post here. I had the same idea (taking the ordinance modulus 3) but couldn't figure out how to print the right numbers for s - z.

Anyways, the golf improvements I made:

  • changed raw_input to input

  • removed the extraneous '\b' and parentheses and single quotes

  • removed the +2 offset and placed that in the original subtraction (97 - (3 * 2) = 91)

Tested with the Python 2.7.6 interpreter. Assumes, per the rules, a string input.

zheshishei

Posted 2014-02-18T23:51:28.397

Reputation: 21

you could also remove the space between the ) and the if – Willem – 2014-03-13T18:57:12.450

You're right. good catch willem – zheshishei – 2014-03-14T17:24:28.427

1

PHP, 87

echo str_ireplace(range('a','z'),str_split('22233344455566677778889999'),fgets(STDIN));

ub3rst4r

Posted 2014-02-18T23:51:28.397

Reputation: 282

1

q [38 Chars]

{(.Q.a!"22233344455566677778889999")x}

Inspired by @ace's solution

Example

{(.Q.a!"22233344455566677778889999")x}"stack exchange"
"78225 39242643"

nyi

Posted 2014-02-18T23:51:28.397

Reputation: 448

1

C# 140

using System.Linq;class P{static void Main(string[]a){System.Console.Write(string.Concat(a[0].Select(d=>(char)(d>96?20-d/122+5*d/16:d))));}}

RobIII

Posted 2014-02-18T23:51:28.397

Reputation: 397

1

XQuery, 71

BaseX was used as XQuery processor. $i is input.

translate($i,"abcdefghijklmnopqrstuvwxyz","22233344455566677778889999")

Not the shortest answer, but quite short and very readable.

Reinstate Monica - dirkk

Posted 2014-02-18T23:51:28.397

Reputation: 251

1

Python, very ungolfed

Since everyone is copying ace, i decided to post the code i made up before i submitted the question:

def phonekeypad(text):
    c = ['','','abc','def','ghi','jkl','mno','pqrs','tuv','wxyz']
    st = ""
    for i in list(text):
        a = False
        for t in range(len(c)):
            if i in c[t]:
                st += str(t)
                a=True
        if a == False:
            st += str(i)
    return st

TheDoctor

Posted 2014-02-18T23:51:28.397

Reputation: 7 793

1

EcmaScript 6 (103 bytes):

i.replace(/[a-z]/g,x=>keys(a='00abc0def0ghi0jkl0mno0pqrs0tuv0wxyz'.split(0)).find(X=>a[X].contains(x)))

Expects i to contain the string.

Try it in any recent version of Firefox. I've not tried Google Chrome.

Toothbrush

Posted 2014-02-18T23:51:28.397

Reputation: 3 197

1

Python 3, 121

print("".join((lambda x:"22233344455566677778889999"[ord(x)-97] if ord(x)>96 and ord(x)<123 else x)(i) for i in input()))

gcq

Posted 2014-02-18T23:51:28.397

Reputation: 251

1

Haskell, 93C

t[]_ a=a
t(b:c)(d:e)a
 |a==b=d
 |True=t c e a
y=map(t['a'..'z']"22233344455566677778889999")

Usage

y "1-800-program"

bazzargh

Posted 2014-02-18T23:51:28.397

Reputation: 2 476

0

05AB1E, 11 bytes

Aā6+žq÷T9:‡

Try it online!

A                 # alphabet: "abcdefghijklmnoqrstuvwxyz"
 ā                # length range: [1..26]
  6+              # add 6 to each: [7..32]
    žq÷           # divide by pi: [2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7,7,8,8,8,9,9,9,10]
       T9:        # replace 10 with 9
          ‡       # transliterate the input (a => 2, ..., z => 9)

Grimmy

Posted 2014-02-18T23:51:28.397

Reputation: 12 521

0

Python

import string          
trans = str.maketrans(string.ascii_lowercase,
                      '22233344455566677778889999')                                                                                         
print("1-800-ask-usps".translate(trans))

yegle

Posted 2014-02-18T23:51:28.397

Reputation: 219

0

ECMASCRIPT, 101 (with input)

"1-800-PROGRAM".replace(/./g,function(c){
return "22233344455566677778889999"[c.charCodeAt(0)-65]||c})

Newline added for clarity. 85 characters if the input is in a variable.

Malvolio

Posted 2014-02-18T23:51:28.397

Reputation: 141

0

Perl, 54

print map{/[a-y]/?int(5/16*ord)-28:/z/?9:$_}<>=~/./gs

Shoot, @RobHoare still beat me by 4 characters. :)

DLosc

Posted 2014-02-18T23:51:28.397

Reputation: 21 213

0

QBasic, 155

Ah, the memories...

INPUT n$
FOR i=1 TO LEN(n$)
c$=MID$(n$,i,1)
a=ASC(c$)
IF 97>a THEN
PRINT c$;
ELSE IF 122>a THEN
PRINT STR$(a\3.2-28);
ELSE
PRINT 9;
END IF
NEXT i

This should have been shorter, but I was testing with repl.it, which doesn't allow single-line IF statements and behaves strangely if you leave the variable off of NEXT i. It also doesn't recognize the ASC function, so to run the code you'll need to add this workaround at the beginning:

DECLARE FUNCTION ASC(s$)
FUNCTION ASC(s$)
FOR j=1 TO 255
IF CHR$(j)=LEFT$(s$,1) THEN
ASC=j
END IF
NEXT j
END FUNCTION

(The second time you run it, the interpreter will complain unless you remove the DECLARE FUNCTION line, go figure.)

DLosc

Posted 2014-02-18T23:51:28.397

Reputation: 21 213

0

R, 110

s=strsplit(scan(,""),"")[[1]];i=grep("[a-z]",s);s[i]=sort(c(1:24%%8+2,7,9))[match(s[i],letters)];cat(s,sep="")

Example:

> s=strsplit(scan(,""),"")[[1]];i=grep("[a-z]",s);s[i]=sort(c(1:24%%8+2,7,9))[match(s[i],letters)];cat(s,sep="")
1: 1-800-program
2: 
Read 1 item
1-800-7764726

Sven Hohenstein

Posted 2014-02-18T23:51:28.397

Reputation: 2 464

0

VB.net (61c)

Excludes the 45c min for a valid vb.net program.

Module P
Sub Main(A()As string)
For Each x In A(0).ToUpper 
Console.Write(If(x<"A"or x>"Z",x,Chr(Asc(x)\4+35)))
Next
End Sub
End Module

Adam Speight

Posted 2014-02-18T23:51:28.397

Reputation: 1 234