How many times should I press this?

24

7

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.

Mr. Xcoder

Posted 2017-06-28T11:01:52.023

Reputation: 39 774

Related, Inspired by this – Mr. Xcoder – 2017-06-28T11:03:18.353

Related. Related. – Shaggy – 2017-06-28T11:17:32.903

2I 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. – Yakk – 2017-06-28T16:22:01.183

@Yakk That would be far too complicated – Mr. Xcoder – 2017-06-28T18:15:13.267

@Mr.Xcoder Are you sure though? I have seen the code-wizards here do impossible stuff in less space than a tweet. – J_F_B_M – 2017-06-28T22:54:06.857

@J_F_B_M That wouldn't be very hard, but it is too complicated for the purpose of this challenge, at least. – Mr. Xcoder – 2017-06-29T06:53:07.967

Thrice, word is thrice. Once, twice, thrice. As a non-English speaker I saw our common struggle there :) – Ege Bayrak – 2017-06-29T07:46:16.770

@EgeBayrak Happy now? – Mr. Xcoder – 2017-06-29T07:47:22.767

Can we take all uppercase if that makes the code shorter? – Adám – 2017-07-04T08:24:23.730

@Adám No, sorry. I don't want to create confusion with old answers that might become shorter too. – Mr. Xcoder – 2017-07-04T08:26:15.520

Answers

11

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

Rick Hitchcock

Posted 2017-06-28T11:01:52.023

Reputation: 2 461

(s,t=0)=>[...s].map(l=>t+=(1+'behknquxcfilorvysz'.indexOf(l)/8|0)+1)&&t for 71 bytes – Johan Karlsson – 2017-06-28T11:39:53.463

Thanks, @JohanKarlsson, I figured the same thing while in the shower! Found another optimization to shave off 5 more bytes. – Rick Hitchcock – 2017-06-28T11:54:06.440

6I 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. – Neil – 2017-06-28T13:38:22.623

1@Neil, while that may not be shorter, it is certainly cleverer. – Rick Hitchcock – 2017-06-28T13:40:27.920

1@Neil You should post it. – Mr. Xcoder – 2017-06-28T13:43:31.627

s=>[...s].map(l=>s=~~s+2+'behknquxcfilorvysz'.search(l)/8)|s should save 4 bytes, decimal parts being truncated by ~~s on each iteration and ultimately by |s. – Arnauld – 2017-06-28T22:10:35.347

Sweet, I'll study that and update later, thanks! – Rick Hitchcock – 2017-06-28T22:20:17.157

7

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

Emigna

Posted 2017-06-28T11:01:52.023

Reputation: 50 798

Sorry, but for an empty input this gives 10. It is fine elsewhere – Mr. Xcoder – 2017-06-28T12:01:38.773

@Mr.Xcoder: The empty string gives no output, but it's still wrong. Thanks for notifying, I'll fix it. – Emigna – 2017-06-28T12:06:10.253

2It gives 10 on TIO. – Mr. Xcoder – 2017-06-28T12:06:56.090

@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 :) – Emigna – 2017-06-28T12:09:03.537

@Mr.Xcoder: Empty string input is given like this

– Emigna – 2017-06-28T12:09:42.497

@Emigna Won't just \n suffice? – Erik the Outgolfer – 2017-06-28T12:10:17.187

@EriktheOutgolfer: A literal line break would work as well yes :) – Emigna – 2017-06-28T12:11:01.697

Ooh, didn't know £ would behave like that...it's supposed to do a[:b] right? – Erik the Outgolfer – 2017-06-28T12:50:17.600

@EriktheOutgolfer: Yep. But if b is a list instead of an int it repeatedly does a[:b] on the remainder of a for each element in b basically. – Emigna – 2017-06-28T12:52:02.710

ƵÜbS3+ pushes [3,3,3,3,3,4,3,4] but reversed. – Magic Octopus Urn – 2017-06-28T13:49:25.167

@MagicOctopusUrn: I also had ƵxbRS3+ as well as •M†–•5в, but unfortunately they are all the same size :( – Emigna – 2017-06-28T14:19:58.820

•Cç•3вÌ also works. – Emigna – 2017-06-28T14:26:32.757

-1 by using ðм instead of ðδK. Probably wasn't available yet at time of posting (and currently it also doesn't work in the TIO since the items are wrapped in inner lists, so a third O would be necessary..) – Kevin Cruijssen – 2019-08-01T08:17:15.213

7

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!

ovs

Posted 2017-06-28T11:01:52.023

Reputation: 21 408

Interesting solution. How does it work for spaces, I don't get it >.<? – Mr. Xcoder – 2017-06-28T13:55:53.690

@Mr.Xcoder for anything not in the string '...'.find(c) returns -1. By adding 2 we get one keypress. – ovs – 2017-06-28T13:59:17.403

I knew it returned -1, but didn't realize you have a +2 after the boilerplate... Anyway, the shortest Python solution by far. – Mr. Xcoder – 2017-06-28T14:00:09.000

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 :) – Mario Ishac – 2017-06-28T18:14:44.640

5

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.

Uriel

Posted 2017-06-28T11:01:52.023

Reputation: 11 708

You can use 'adgjmptw ' 'behknqux' 'cfilorvy' 'sz' to save some bytes – user41805 – 2017-06-28T12:04:07.080

49 bytes – Leaky Nun – 2017-06-28T12:26:28.243

@LeakyNun onice – Uriel – 2017-06-28T12:34:06.543

You can drop the space in the string – user41805 – 2017-06-28T12:35:30.150

@Uriel wait, you don't need to count f← so it is 47 bytes – Leaky Nun – 2017-06-28T12:36:39.880

@LeakyNun yup. and 46 – Uriel – 2017-06-28T12:38:12.793

Save a byte by converting to tradfn.

– Adám – 2017-07-04T08:22:48.960

@Adám but trains looks so much better! ;) – Uriel – 2017-07-04T15:55:36.827

5

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!

Leaky Nun

Posted 2017-06-28T11:01:52.023

Reputation: 45 011

Save one byte by replacing i==" " with i<"a", because you only receive letters and spaces – Mr. Xcoder – 2017-06-28T12:04:26.683

461 minutes...too late! – Erik the Outgolfer – 2017-06-28T12:05:51.797

4

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.

Neil

Posted 2017-06-28T11:01:52.023

Reputation: 95 035

Pure arithmetic :) – Mr. Xcoder – 2017-06-28T14:01:01.723

What does s=>[...s] do why not just s=>s.map()... – Evan Carroll – 2017-06-28T16:34:28.500

1@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. – Neil – 2017-06-28T16:47:48.003

4

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;
}

dbuchmann

Posted 2017-06-28T11:01:52.023

Reputation: 41

*(k+i) can be k[i]. – CalculatorFeline – 2017-06-28T23:36:11.430

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 – Tas – 2017-06-29T01:05:25.753

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. – dbuchmann – 2017-06-29T06:59:24.090

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. – dj0wns – 2017-07-05T11:16:47.833

4

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!

Henry

Posted 2017-06-28T11:01:52.023

Reputation: 461

What is the purpose of the duplicate letters? – Mr. Xcoder – 2017-06-28T18:19:12.113

@Mr.Xcoder It's used to count the taps, I will add an explanation. – Henry – 2017-06-28T18:21:03.080

You can save one byte by rewriting f to f=length.(=<<)(\x->x:[y|y<-l,y==x]), where (=<<) is concatMap here. – siracusa – 2017-06-28T20:47:17.070

And another one with going back to filter: f=length.(=<<)(\x->x:filter(==x)l) – siracusa – 2017-06-28T20:51:28.473

1As you use l only once, it can be inlined. – Laikoni – 2017-06-28T22:07:26.517

Use a single sum instead of sum of lengths: sum.(>>= \x->1:[1|y<-l,y==x]). Also, as Laikoni said, inline l. – Zgarb – 2017-06-29T07:51:18.423

Haven't gotten to the chapters on Monoids/Functors/Applicatives, so I am afraid I have never used =<<. I tried to do as you suggested, but was unable to get it working. @siracusa – Henry – 2017-06-29T13:24:41.547

Oh, what was the problem? – siracusa – 2017-06-29T17:32:34.907

@siracusa Oh! I thought you were giving me partial code, so I tried to add stuff to what I thought would work. Now that I tried what you posted, I was able to get it to work fine. Thanks! – Henry – 2017-06-29T17:56:32.863

3

Jelly, 25 bytes

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

Try it online!

Erik the Outgolfer

Posted 2017-06-28T11:01:52.023

Reputation: 38 134

3

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 :)

NikoNyrh

Posted 2017-06-28T11:01:52.023

Reputation: 2 361

2

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!

officialaimm

Posted 2017-06-28T11:01:52.023

Reputation: 2 739

2

Retina, 46 36 bytes

Thanks to CalculatorFeline for saving 6 bytes.

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

Try it online!

Martin Ender

Posted 2017-06-28T11:01:52.023

Reputation: 184 808

2

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.

Adam Mendenhall

Posted 2017-06-28T11:01:52.023

Reputation: 131

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());}} – Adam Mendenhall – 2017-06-28T21:19:22.820

1

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.

– Kevin Cruijssen – 2017-06-29T07:56:33.810

2

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.

– Kevin Cruijssen – 2017-06-29T08:23:49.453

2

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!

Arnauld

Posted 2017-06-28T11:01:52.023

Reputation: 111 334

1

Mathematica, 83 bytes

c=Characters;Tr[Tr@Mod[c@"bc1def1ghi1jkl1mno1pqrstuv1wxyz "~Position~#,4]+1&/@c@#]&

J42161217

Posted 2017-06-28T11:01:52.023

Reputation: 15 931

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..."?) – Greg Martin – 2017-06-28T18:43:29.630

this is code golf.this gives the right result without a."Tr " does the job – J42161217 – 2017-06-28T18:49:21.457

1

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

steenbergh

Posted 2017-06-28T11:01:52.023

Reputation: 7 772

1

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.

marcosm

Posted 2017-06-28T11:01:52.023

Reputation: 986

on your machine you may need bc <(fold -1|tr "\n "adgjmptwbehknquxcfilorvysz +[1*9][2*8][3*8]44;echo 0) – marcosm – 2017-06-28T18:36:46.553

1

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;}

Ray

Posted 2017-06-28T11:01:52.023

Reputation: 1 488

you may use s=n to replace return n, and combine s++; with c=*s. It could be 9 bytes shorter. – Keyu Gan – 2017-06-29T03:34:23.853

@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. – Ray – 2017-07-03T21:50:33.807

1

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!

Keyu Gan

Posted 2017-06-28T11:01:52.023

Reputation: 2 028

@ceilingcat thanks! – Keyu Gan – 2019-08-01T07:24:54.970

1

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

Adám

Posted 2017-06-28T11:01:52.023

Reputation: 37 779

1

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!

Kenneth Taylor

Posted 2017-06-28T11:01:52.023

Reputation: 183