24
\$\begingroup\$

We are all used to the old-school telephone keypad, right? For reference, here is what it looks like:

Telephone Keybad


Given a String consisting only of lowercase ASCII letters and single spaces, your task is to return the number of taps one should make in order to type down the full String with a telephone keypad as the one above.

For those who are unfamiliar with this, here's how it works:

  • The key with the digit 2, for example, also has the string abc written down on it. In order to type a, you must press this key once, for b you must press twice and for c you must press thrice.

  • For consecutive letters that are on the same key, you must wait 1 second before pressing again. So, if you want to type cb, you must press 3 times for c, wait a second and then press twice for b, so still 5 taps.

  • The same applies for all other keys, except for a single space, which only requires 1 press. Also note that the keys 7 and 9 have four letters on them. The same algorithm is applied, the only difference being the number of letters. The strings corresponding to each key can be found in the image above (but lowercase), or in the following list, that contains all the characters you might receive:

    "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz", " "
    

Test Cases

Input  ->  Output  (Explanation)

"" -> 0                   (nothing should be tapped)
"water" -> 8              ("w,a,t" each require 1 tap (on keys 9, 2 and 8), "e" requires 2 taps (on key 3), "r" requires 3 taps (on key 7), 1+1+1+2+3 = 8)
"soap"  -> 9              (4+3+1+1)
"candela" -> 13           (3+1+2+1+2+3+1)
"code golf" -> 20         (3+3+1+2+1(for the space)+1+3+3+3)
"king of the hill" -> 33  (2+3+2+1+1+3+3+1+1+2+2+1+2+3+3+3)

Specs

  • Standard I/O rules and Default Loopholes apply.

  • You may only take input in your language's native String type. Output can either be an integer or a string representation of that integer.

  • This is , the shortest answer in every language wins.

\$\endgroup\$
10
  • \$\begingroup\$ Related, Inspired by this \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 11:03
  • \$\begingroup\$ Related. Related. \$\endgroup\$
    – Shaggy
    Jun 28, 2017 at 11:17
  • 2
    \$\begingroup\$ I think this would be a more interesting question if you did 1 tap per second, and had to wait 1 second, and counted seconds instead of taps. \$\endgroup\$
    – Yakk
    Jun 28, 2017 at 16:22
  • \$\begingroup\$ @Yakk That would be far too complicated \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 18:15
  • \$\begingroup\$ @Mr.Xcoder Are you sure though? I have seen the code-wizards here do impossible stuff in less space than a tweet. \$\endgroup\$
    – JFBM
    Jun 28, 2017 at 22:54

21 Answers 21

11
\$\begingroup\$

JavaScript (ES6) 77 66 64 60 bytes

(Saved some bytes thanks to @Johan Karlsson and @Arnauld).

s=>[...s].map(l=>s=~~s+2+'behknquxcfilorvysz'.search(l)/8)|s

f=

s=>[...s].map(l=>s=~~s+2+'behknquxcfilorvysz'.search(l)/8)|s

console.log(f(''));                 //0
console.log(f('water'));            //8
console.log(f('soap'));             //9
console.log(f('candela'));          //13
console.log(f('code golf'));        //20
console.log(f('king of the hill')); //33

\$\endgroup\$
7
  • \$\begingroup\$ (s,t=0)=>[...s].map(l=>t+=(1+'behknquxcfilorvysz'.indexOf(l)/8|0)+1)&&t for 71 bytes \$\endgroup\$ Jun 28, 2017 at 11:39
  • \$\begingroup\$ Thanks, @JohanKarlsson, I figured the same thing while in the shower! Found another optimization to shave off 5 more bytes. \$\endgroup\$ Jun 28, 2017 at 11:54
  • 6
    \$\begingroup\$ I found a purely arithmetical solution for 71 bytes: f=s=>[...s].map(c=>t+=((c=parseInt(0+c,36))>23?c+3:c&&~-c%3)%7%4+1,t=0)|t. \$\endgroup\$
    – Neil
    Jun 28, 2017 at 13:38
  • 1
    \$\begingroup\$ @Neil, while that may not be shorter, it is certainly cleverer. \$\endgroup\$ Jun 28, 2017 at 13:40
  • 1
    \$\begingroup\$ @Neil You should post it. \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 13:43
7
\$\begingroup\$

05AB1E, 29 26 25 bytes

ð¢svA•22ā₂•S£ð«øðδKy.åƶOO

Try it online!

Explanation

ð¢                         # count spaces in input
  sv                       # for each char y in input
    A                      # push the lowercase alphabet
     •22ā₂•S               # push the base-10 digit list [3,3,3,3,3,4,3,4]
            £              # split the alphabet into pieces of these sizes
             ð«            # append a space to each
               ø           # transpose
                ðδK        # deep remove spaces
                   y.å     # check y for membership of each
                      ƶ    # lift each by their index in the list
                       O   # sum the list
                        O  # sum the stack
\$\endgroup\$
13
  • \$\begingroup\$ Sorry, but for an empty input this gives 10. It is fine elsewhere \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 12:01
  • \$\begingroup\$ @Mr.Xcoder: The empty string gives no output, but it's still wrong. Thanks for notifying, I'll fix it. \$\endgroup\$
    – Emigna
    Jun 28, 2017 at 12:06
  • 2
    \$\begingroup\$ It gives 10 on TIO. \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 12:06
  • \$\begingroup\$ @Mr.Xcoder: Yeah you have to explicitly give the empty string. No input is not the same as an empty string. It is a bit confusing I know. Fixed now though :) \$\endgroup\$
    – Emigna
    Jun 28, 2017 at 12:09
  • \$\begingroup\$ @Mr.Xcoder: Empty string input is given like this \$\endgroup\$
    – Emigna
    Jun 28, 2017 at 12:09
7
\$\begingroup\$

Python 2, 56 bytes

Uses the same algorithm as @RickHitchcock's Javascript solution

lambda x:sum('behknquxcfilorvysz'.find(c)/8+2for c in x)

Try it online!

\$\endgroup\$
4
  • \$\begingroup\$ Interesting solution. How does it work for spaces, I don't get it >.<? \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 13:55
  • \$\begingroup\$ @Mr.Xcoder for anything not in the string '...'.find(c) returns -1. By adding 2 we get one keypress. \$\endgroup\$
    – ovs
    Jun 28, 2017 at 13:59
  • \$\begingroup\$ I knew it returned -1, but didn't realize you have a +2 after the boilerplate... Anyway, the shortest Python solution by far. \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 14:00
  • \$\begingroup\$ Oml, I happened to make the exact same solution after slowly golfing my program down, until I realized you posted it :( Nice job for finding this solution as well :) \$\endgroup\$ Jun 28, 2017 at 18:14
5
\$\begingroup\$

Python 3, 69 67 65 64 bytes

1 byte thanks to Mr. Xcoder.

1 byte thanks to Felipe Nardi Batista.

lambda x:sum((ord(i)+~(i>"s"))%3+3*(i in"sz")+(i>" ")for i in x)

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ Save one byte by replacing i==" " with i<"a", because you only receive letters and spaces \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 12:04
  • 4
    \$\begingroup\$ 61 minutes...too late! \$\endgroup\$ Jun 28, 2017 at 12:05
5
\$\begingroup\$

Dyalog APL, 37 bytes

+/⌈9÷⍨'adgjmptw behknqux~cfilorvy~'⍳⍞

Try it online!

How?

Get the ndex of every char of the input in the string 'adgjmptw behknqux~cfilorvy~' (s and z will default to 28), divide by 9, round up and sum.

\$\endgroup\$
8
  • \$\begingroup\$ You can use 'adgjmptw ' 'behknqux' 'cfilorvy' 'sz' to save some bytes \$\endgroup\$
    – user41805
    Jun 28, 2017 at 12:04
  • \$\begingroup\$ 49 bytes \$\endgroup\$
    – Leaky Nun
    Jun 28, 2017 at 12:26
  • \$\begingroup\$ @LeakyNun onice \$\endgroup\$
    – Uriel
    Jun 28, 2017 at 12:34
  • \$\begingroup\$ You can drop the space in the string \$\endgroup\$
    – user41805
    Jun 28, 2017 at 12:35
  • \$\begingroup\$ @Uriel wait, you don't need to count f← so it is 47 bytes \$\endgroup\$
    – Leaky Nun
    Jun 28, 2017 at 12:36
4
\$\begingroup\$

JavaScript (ES6), 71 bytes

f=
s=>[...s].map(c=>t+=((c=parseInt(0+c,36))>23?c+3:c&&~-c%3)%7%4+1,t=0)|t
<input oninput=o.textContent=f(this.value)><pre id=o>

Look no letter tables! I didn't quite understand @LeakyNun's formula so I came up with my own.

\$\endgroup\$
3
  • \$\begingroup\$ Pure arithmetic :) \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 14:01
  • \$\begingroup\$ What does s=>[...s] do why not just s=>s.map()... \$\endgroup\$ Jun 28, 2017 at 16:34
  • 1
    \$\begingroup\$ @EvanCarroll s is a string, so you can't map it directly. ...s iterates over s, while [...s] converts the iteration into an array, effectively splitting s into an array of characters. \$\endgroup\$
    – Neil
    Jun 28, 2017 at 16:47
4
\$\begingroup\$

C, 211 196 bytes

First submission here...looks quite lengthy and I see that this is not an efficient approach, but at least it works :)

f(char*n){char*k=" abcdefghijklmnopqrstuvwxyz";int t[]={0,3,3,3,3,3,4,3,4};int l=0,s,j,i;while(*n){i=0;while(k[i]){if(k[i]==*n){s=0;for(j=0;s<i-t[j];s+=t[j++]);*n++;l+=(!i?1:i-s);}i++;}}return l;}

Ungolfed version:

int f(char *n){
  char *k=" abcdefghijklmnopqrstuvwxyz";
  int t[]={0,3,3,3,3,3,4,3,4};
  int l=0,s,j,i;
  while(*n){                          // loop through input characters
    i=0;
    while(k[i]){
      if(k[i]==*n){                   // find matching char in k
        s=0;
        for(j=0;s<i-t[j];s+=t[j++]);  // sum up the "key sizes" up to the key found
        *n++;
        l+=(!i?1:i-s);                // key presses are i-s except for space (1)
      }
      i++;
    }
  }
  return l;
}
\$\endgroup\$
4
  • \$\begingroup\$ *(k+i) can be k[i]. \$\endgroup\$ Jun 28, 2017 at 23:36
  • \$\begingroup\$ You can cut the space after an * (e.g. char*n), and add your declarations to your empty for statement (instead of int s=0,j=0;(for(; you'd have for(int s=0,k=0;) and instead of i==0 use !i \$\endgroup\$
    – Tas
    Jun 29, 2017 at 1:05
  • \$\begingroup\$ Thank you for those hints. I couldn't put s into the for loop because I use it later on, but I put the int declarations together and used assignemts where I needed them. \$\endgroup\$
    – dbuchmann
    Jun 29, 2017 at 6:59
  • \$\begingroup\$ Yay a fellow C golfer! Anyways, some pointers: for loops are strictly better than while loops in almost all situation - take advantage of the free semicolons, particularly in the iteration expression. Use commas instead of semicolons in most places, this allows you to get away with not having curly braces in most places. There are other optimizations but they are more reliant on which version of C you compile to. \$\endgroup\$
    – dj0wns
    Jul 5, 2017 at 11:16
4
\$\begingroup\$

Haskell - 74 71 62 bytes

Edit: removed 3 bytes by using a list comprehension instead of filter

Edit: Save 9 bytes thanks to Siracusa, Laikoni and Zgarb!

f=sum.(>>= \x->1:[1|y<-"bcceffhiikllnooqrrsssuvvxyyzzz",y==x])

Usage

λ> f "candela"
13
λ>

Try it online!

\$\endgroup\$
9
  • \$\begingroup\$ What is the purpose of the duplicate letters? \$\endgroup\$
    – Mr. Xcoder
    Jun 28, 2017 at 18:19
  • \$\begingroup\$ @Mr.Xcoder It's used to count the taps, I will add an explanation. \$\endgroup\$
    – Henry
    Jun 28, 2017 at 18:21
  • \$\begingroup\$ You can save one byte by rewriting f to f=length.(=<<)(\x->x:[y|y<-l,y==x]), where (=<<) is concatMap here. \$\endgroup\$
    – siracusa
    Jun 28, 2017 at 20:47
  • \$\begingroup\$ And another one with going back to filter: f=length.(=<<)(\x->x:filter(==x)l) \$\endgroup\$
    – siracusa
    Jun 28, 2017 at 20:51
  • 1
    \$\begingroup\$ As you use l only once, it can be inlined. \$\endgroup\$
    – Laikoni
    Jun 28, 2017 at 22:07
3
\$\begingroup\$

Jelly, 25 bytes

Øaḟ⁾sz©;⁶s3Z®ṭċ@€€⁸×"J$ẎS

Try it online!

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

Clojure, 82 76 bytes

#(apply +(for[c %](+(count(filter #{c}"bcceffhiikllnooqrrsssuvvxyyzzz"))1)))

Oh it is simpler to just filter and count than use frequencies. Original:

#(apply +(count %)(for[c %](get(frequencies"bcceffhiikllnooqrrsssuvvxyyzzz")c 0)))

The string encodes how many times more than just once you need to press the key for a given character :)

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

Python 3, 91 bytes

lambda x:sum(j.find(i)+1for j in' !abc!def!ghi!jkl!mno!pqrs!tuv!wxyz'.split('!')for i in x)

Try it online!

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

Python 3, 60 bytes

Probably sub-optimal, as this is my first golf ever in Python.

lambda x:sum((ord(i)-8)%3.15//1+3*(i>'y')+(i>' ')for i in x)

Try it online!

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

Retina, 46 36 bytes

Thanks to CalculatorFeline for saving 6 bytes.

s|z
,c
[cfilorvy]
,b
[behknqux]
,,
.

Try it online!

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

Java, 95 73 bytes

a->a.chars().map(b->1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112)).sum()

Thanks to Kevin Cruijssen for making the function a lambda expression (where a is of type String). 95 bytes became 73 bytes!

A lambda expression sums up the press count of each character using map(). map() converts each character (ASCII in lower case range is 97-122) in the stream to the appropriate value (looks like simple saw wave, but taking into account both 4 cycles is annoying) using this math: 1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112). Here's a desmos graph of that model.

\$\endgroup\$
3
  • \$\begingroup\$ The loophole list says not to post code snippets even though it looks like everyone so far has done that. Either way, my full program is 130 bytes. Here it is: interface Z{static void main(String a){System.out.print(a.chars().map(b->1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112)).sum());}} \$\endgroup\$ Jun 28, 2017 at 21:19
  • 1
    \$\begingroup\$ Welcome to PPCG! You're indeed right that snippets aren't allowed, but the default is program or function. And with Java 8 you can use lambdas. So in this case a->{return a.chars().map(b->1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112)).sum();} is allowed. And since it's a single return statement, a->a.chars().map(b->1+(b<64?0:b+(Math.abs(b-115)<4?4:5))%(3+b/112)).sum() (73 bytes) would be your answer. Also, here is a TryItOnline-link of your answer you might want to add to your answer. Again: welcome, and nice answer. +1 from me. \$\endgroup\$ Jun 29, 2017 at 7:56
  • 2
    \$\begingroup\$ Some things to note about lambdas. You don't have to count f= nor the leading semi-colon ;. And you also don't have to add the type of the parameter as long as you mention what the type is (so instead of (String a)-> you can use a-> and mention that input a is a String in your answer). Oh, and Tips for golfing in Java and Tips for golfing in <all languages> might be interesting to read, in case you haven't yet. \$\endgroup\$ Jun 29, 2017 at 8:23
2
\$\begingroup\$

Pip, 100 90 bytes

a:qb:["abc""def""ghi""jkl""mno""pqrs""tuv""wxyz"s]Fc,#a{Fd,#b{e:((bd)@?(ac))e<4?i+:e+1x}}i

Check each character of the input for a match in each element of b. The index of that match plus 1 gets added to the total.

Try it online!

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

Mathematica, 83 bytes

c=Characters;Tr[Tr@Mod[c@"bc1def1ghi1jkl1mno1pqrstuv1wxyz "~Position~#,4]+1&/@c@#]&
\$\endgroup\$
2
  • \$\begingroup\$ It seems to be generally accepted that Mathematica answers are allowed to use lists of characters for string variables, such as the input to this function. (Also is there an a missing at the start of "bc1..."?) \$\endgroup\$ Jun 28, 2017 at 18:43
  • \$\begingroup\$ this is code golf.this gives the right result without a."Tr " does the job \$\endgroup\$
    – ZaMoC
    Jun 28, 2017 at 18:49
1
\$\begingroup\$

QBIC, 94 bytes

[_l;||_SA,a,1|p=p-(instr(@sz`,B)>0)-(instr(@cfilorvy`+C,B)>0)-(instr(@behknqux`+C+D,B)>0)+1}?p

Explanation

[    |      FOR a = 1 TO
 _l |         the length of
   ;            the input string (A$)
_SA,a,1|    Take the a'th char of A$ and assign it to B$
p=p         p is our tap-counter, and in each iteration it gets increased by the code below
            which consist of this pattern:
                instr(@xyz`,B)>0    where 
                - instr tests if arg 2 is in arg 1 (it either returns 0 or X where X is the index of a2 in a1)
                - @...` defines the letters we want to test as arg1
                - B is the current letter to count the taps for
            Each of these blocks adds 1 tap to the counter, and each block has the letters of its level
            (4-taps, 3-taps or 2-taps) and the level 'above' it.
    -(instr(@sz`,B)>0)              <-- letters that require 4 taps
    -(instr(@cfilorvy`+C,B)>0)      <-- 3 or 4 taps
    -(instr(@behknqux`+C+D,B)>0)    <-- 2, 3,or 4 taps
    +1                              <-- and always a 1-tap
}           NEXT
?p          PRINT the number of taps
\$\endgroup\$
1
\$\begingroup\$

Bash, 69 68 bytes

bc<<<`fold -1|tr "\n "adgjmptwbehknquxcfilorvysz +[1*9][2*8][3*8]44`

Try it online!

Folds one char per line, transliterates each newline with +, each space with 1 and each letter with the corresponding number of pushes. bc does the sum.

\$\endgroup\$
1
  • \$\begingroup\$ on your machine you may need bc <(fold -1|tr "\n "adgjmptwbehknquxcfilorvysz +[1*9][2*8][3*8]44;echo 0) \$\endgroup\$
    – marcosm
    Jun 28, 2017 at 18:36
1
\$\begingroup\$

C, 92 88 bytes

c,n;f(char*s){n=0;while(c=*s++)n+=(c=='s')+3*(c>'y')+1+(c+1+(c<'s'))%3-(c<33);return n;}
\$\endgroup\$
2
  • \$\begingroup\$ you may use s=n to replace return n, and combine s++; with c=*s. It could be 9 bytes shorter. \$\endgroup\$
    – Keyu Gan
    Jun 29, 2017 at 3:34
  • \$\begingroup\$ @KeyuGan s=n wouldn't work, since s is a local. And *s=n wouldn't work since there are only CHAR_BIT bits in *s, which wouldn't be enough for some messages. But you're right about the s++. Thanks. \$\endgroup\$
    – Ray
    Jul 3, 2017 at 21:50
1
\$\begingroup\$

APL (Dyalog), 36 bytes

{+/(3×⍵∊'sz'),1+3|¯1+⍵⍳⍨819⌶⎕A~'SZ'}

Try it online!

Finds the mod-3 indices in the alphabet without S and Z. Since space, S, and Z are not found, they "have" index 25 (one more than the max index), which is good for space. Then we just need to add 3 for each S or Z.

{ anonymous function where the argument is represented by :

⎕A~'SZ' the uppercase Alphabet, except for S and Z

819⌶ lowercase

⍵⍳⍨ the ɩndices of the argument in that

¯1+ add negative one

3| mod-3

1+ add one (this converts all 0-mods to 3)

(), prepend:

  ⍵∊'sz' Boolean where the argument is either s or z

   multiply by 3

+/ sum

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

C (gcc), 75 77 bytes

n,b;f(char*a){for(n=0;b=*a++;)n+=b<'s'?--b%3+(b>31):""[b-'s'];a=n;}

The unprintable string "" is a table of 04 01 02 03 01 02 03 04.

Try it online!

\$\endgroup\$
0

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.