# How long does it take to type this?

## Introduction

I can type at a moderate pace, using the QWERTY keyboard layout. But if a word like yellowwooddoor has a ton of repeated letters, it takes a bit longer to type it. Even worse is when a word like "jump" has the same finger used for multiple different consecutive letters.

Here's how long it takes me to type letters on each finger (very unscientifically measured):

Columns are Finger name, keystrokes/second, seconds/keystroke, and the keys used by each finger

Typing same letter twice:
L Pinky 5.2 0.1923076923 1qaz
L Ring  5   0.2          2wsx
L Mid   5.3 0.1886792453 3edc
L Index 5.5 0.1818181818 4rfv5tgb
R Thumb 6.5 0.1538461538 [space]
R Index 6.9 0.1449275362 6yhn7ujm
R Mid   6.3 0.1587301587 8ik,
R Ring  6.2 0.1612903226 9ol.
R Pinky 6.1 0.1639344262 0p;'

Typing different letter on same finger:
L Pinky 4.6 0.2173913043
L Ring  4.6 0.2173913043
L Mid   4.5 0.2222222222
L Index 5.3 0.1886792453
R Index 5.4 0.1851851852
R Mid   5.1 0.1960784314
R Ring  5.2 0.1923076923
R Pinky 5.2 0.1923076923


Same data in CSV format.

It takes

.75 * (first_finger_same_letter_time + second_finger_same_letter_time) / 2


time to switch between two fingers.

## Challenge

Given a string as input, how long does it take to type it?

• The "timer" starts the moment the first key is pressed and ends when the last key is pressed. You are just counting the time between keypresses.
• This is . Shortest answer in bytes wins.
• Submission can be either a complete program or function.
• Input and output any way you want it, stdin/out, function params, file, doesn't matter.
• Output should be accurate to at least 3 decimal places (+/- 0.001 for rounding error is fine). Leading 0. for numbers under 1 and trailing newline optional.
• Input will be a string that contains (lowercase) a-z, 0-9, space, semicolon, comma, period, and apostrophe.
• I always type spaces with my right thumb.
• I use the normal touch typing fingers (you can also look at the above table for finger-key mappings).
• Reference code used to generate test cases

## Test cases

(empty string or any one-character string) - 0.000

aa - 0.192

fff - 0.364

fj - 0.123

the quick brown fox jumped over the lazy dog - 5.795

yellowwooddoor - 1.983

orangewooddoor - 1.841

jump on it, jump on it - 2.748

type on it, type on it - 2.549

abcdefghijklmnopqrstuvwxyz01234567890 ;,.' - 5.746

ok, this may not be the most accurate but it's in the ballpark, maybe within 30 percent or so. - 12.138

• Can we assume the input will be at least 2 character, or do we need to output 0 if the input is empty or a single character? Apr 8 '19 at 15:53
• There are already a few answers that do handle it, so not going to change the rules halfway through Apr 8 '19 at 16:01
• A new type of code golf: Instead of scoring answers based on byte count, the winner is whoever can type their program the fastest. Apr 8 '19 at 16:23

# JavaScript (Node.js), 180 bytes

s=>(B=Buffer)(s).map(p=c=>(b='23841410645532207643205431765001333746443'[c*45%91%73%41]*2,t+=1/p?p-b?3/8*(g(b)+g(p)):g(b|c!=s):0,p=b,s=c),t=0,g=x=>10/B('4.2.5-75E6?3>4=4AA')[x])&&t


Try it online!

## How?

### Storing delays

The helper function $$\g\$$ takes an integer $$\0\le x \le17\$$ and returns a delay in seconds.

g = x => 10 / Buffer('4.2.5-75E6?3>4=4AA')[x]


The input $$\x\$$ is expected to be either:

• twice the bin number to get the delay for the same letter
• twice the bin number + 1 to get the delay for different letters

What is actually stored in the string '4.2.5-75E6?3>4=4AA' is the number of keystrokes per second multiplied by $$\10\$$ and converted to ASCII. Conveniently, all resulting characters are printable.

For instance, $$\5.2\$$ is stored as chr(52) which is '4'.

### Converting a character to a key bin

We use the following hash function to convert an ASCII code $$\c\$$ to an index into a lookup table containing the bin numbers in $$\[0..8]\$$:

$$i = (((c\times 45) \bmod 91)\bmod 73)\bmod 41$$

### Main loop

The total time $$\t\$$ is updated with:

t +=                        // add to t:
1 / p ?                   //   if p is numeric:
p - b ?                 //     if p is not equal to b:
3 / 8 * (g(b) + g(p)) //       0.75 * (g(b) + g(p)) / 2
:                       //     else:
g(b | c != s)         //       g(b) if c == s or g(b + 1) otherwise
:                         //   else (first iteration):
0                       //     leave t unchanged


where $$\p\$$ is the previous bin and $$\s\$$ is the previous character.

# Jelly, 78 bytes

“bk¶ŀqṣṁq*E’b25+45s2
Øq;"““;“,.'”Zṙ-ØD;"s2ẎW$€3,4¦ẎœiⱮQḢ€ị¢QƑịZƊQ3.75⁵Ḋ?÷$SµƝS


Try it online!

### How?

“...’b25+45s2 - Link 1, keystrokes per 10 seconds: no arguments
“...’         - base 250 integer = 379310849477441257135820
b25      - to base 25 = [16,7,7,1,5,1,8,0,10,8,24,9,18,6,17,7,20]
+45   - add 45 = [61,52,52,46,50,46,53,45,55,53,69,54,63,51,62,52,65]
s2 - split into twos
- = [[61,52],[52,46],[50,46],[53,45],[55,53],[69,54],[63,51],[62,52],[65]]
- For: 0...    1...    2...    3...    4...    6...    8...    9...    space

Øq;"““;“,.'”Zṙ-ØD;"s2ẎW$€3,4¦ẎœiⱮQḢ€ị¢QƑịZƊQ3.75⁵Ḋ?÷$SµƝS - Main Link: list of characters
µƝ  - for each neighbouring pair:
Øq                                                        -   qwerty = ["qwertyuiop","asdfghjkl","zxcvbnm"]
““;“,.'”                                              -   list of lists = ["",";",",.'"]
"                                                      -   zip with:
;                                                       -     concatenate = ["qwertyuiop","asdfghjkl;","zxcvbnm,.'"]
Z                                             -   transpose = ["qaz","wsx","edc","rfv","tgb","yhn","ujm","ik,","ol.","p;'"]
ṙ-                                           -   rotate left -1 = ["p;'","qaz","wsx","edc","rfv","tgb","yhn","ujm","ik,","ol."]
ØD                                         -   digits = "0123456789"
"                                       -   zip with:
;                                        -     concatenate = ["0p;'","1qaz","2wsx","3edc","4rfv","5tgb","6yhn","7ujm","8ik,","9ol."]
s2                                     -   split into twos = [["0p;'","1qaz"],["2wsx","3edc"],["4rfv","5tgb"],["6yhn","7ujm"],["8ik,","9ol."]]
¦                             -   sparse application...
3,4                              -   ...to indices: [3,4]
$€ - ...do: last two links as a monad for each: Ẏ - tighten W - wrap in a list = [["0p;'","1qaz"],["2wsx","3edc"],["4rfv5tgb"],["6yhn7ujm"],["8ik,","9ol."]] Ẏ - tighten = ["0p;'","1qaz","2wsx","3edc","4rfv5tgb","6yhn7ujm","8ik,","9ol."] Q - de-duplicate (the neighbouring letters) Ɱ - map with: œi - multi-dimensional index-into e.g. "fj" -> [[5,3],[6,7]] - (note <space> is not there so yields an empty list) Ḣ€ - head of each -> [5,6] - (...and the head of an empty list is 0) ¢ - call the last Link (1) as a nilad ị - index-into -> [[55,53],[69,54]] - (...and 0 indexes into the rightmost entry) Ɗ - last three links as a monad: Ƒ - invariant under?: Q - de-duplicate (1 if so, else 0) Z - transpose -> [[55,69],[53,54]] ị - index-into -> [55,69] Q - de-duplicate -> [55,69]$     -   last two links as a monad:
?       -     if...
Ḋ        -     ...condition: dequeue
3.75          -     ...then: 3.75
⁵         -     ...else: 10                               -> 3.75
÷      -     divide                                    -> [0.06818181818181818,0.05434782608695652]
S    -   sum                                         -> 0.12252964426877469
S - sum


# 05AB1E, 92 86 bytes

Îü)v•δ'ā∍ë*8U¾Ã•₂в45+2ô9ÝÀžV€Sζ‚ø˜ð",.;'"S.;ykD4/ïD3›-D4›-‚©θè®€ËOUεXè}T/zX_iO3*8/ëθ]O


Explanation:

Î                     # Push 0 and the input-string
ü)                   # Create all pairs of the (implicit) input-string
# (which will be [] if the input-string is of length 0 or 1)
#  i.e. "ab d" → ["a","b"],["b"," "],[" ","d"]]
v                  # Loop over these pairs y:
•δ'ā∍ë*8U¾Ã•     '#  Push compressed integer 307264255556527588774514
₂в              #  Converted to Base-26 as list: [7,1,5,1,8,0,10,8,24,9,18,6,17,7,16,7,20]
45+           #  Add 45 to each: [52,46,50,46,53,45,55,53,69,54,63,51,62,52,61,52,65]
2ô         #  Split into parts of size 2: [[52,46],[50,46],[53,45],[55,53],[69,54],[63,51],[62,52],[61,52],[65]]
9Ý                #  Push list [0,1,2,3,4,5,6,7,8,9]
À               #  Rotate it once to [1,2,3,4,5,6,7,8,9,0]
žV             #  Push builtin ["qwertyuiop","asdfghjkl","zxcvbnm"]
€S           #  Convert each to a list of characters
ζ          #  Zip/transpose; swapping rows/columns, with space as default filler:
#   [["q","a","z"],["w","s","x"],["e","d","c"],["r","f","v"],["t","g","b"],["y","h","n"],["u","j","m"],["i","k"," "],["o","l"," "],["p"," "," "]]
‚ø        #  Pair it with the digit list, and zip/transpose again
˜       #  Then flatten this entire list:
#   ["1","q","a","z","2","w","s","x","3","e","d","c","4","r","f","v","5","t","g","b","6","y","h","n","7","u","j","m","8","i","k"," ","9","o","l"," ","0","p"," "," "]
ð",.;'"S.;
#  Replace the four spaces with [",", ".", ";", "'"] in order
yk               #  Get the indices of the characters in the pair y in this list
#   i.e. ["b"," "] → [19,-1]
4/            #  Divide both by 4
#   i.e. [19,-1] → [4.75,-0.25]
ï           #  Floor the decimals to integers
#   i.e. [4.75,-0.25] → [4,-1]
D3›-       #  If an index is larger than 3: decrease it by 1
#   i.e. [4,-1] → [3,-1]
D4›-   #  If an index is now larger than 4: decrease it by 1 again
D           ‚  #  Pair it with the original index
#   i.e. [[19,-1],[3,-1]]
© #  And save it in the register (without popping)
θè               #  Then use the last of the two to index into the list of pairs
#   i.e. [3,-1] → [[55,53],[65]]
®€Ë            #  Check for each pair in the register if they're equal
#   i.e. [[19,-1],[3,-1]] → [0,0]
O           #  Take the sum of that
U          #  And pop and store it in variable X
ε  }      #  Map the pairs in the list to:
Xè       #   The X'th value in the pair
#    i.e. [[55,53],[65]] and X=0 → [55,65]
T/               #  Divide each by 10
#   i.e. [55,65] → [5.5,6.5]
z              #  And take 1/value for each
#  i.e. [5.5,6.5] → [0.181...,0.153...]
X_i           #  If variable X was 0:
O          #   Take the sum of these decimal values
#    i.e. [0.181...,0.153...] → 0.335...
3*8/      #   Multiply it by 3, and then divide it by 8
#    i.e. 0.335... → 0.125...
ë           #  Else:
θ          #   Pop the pair of decimal values, and only leave the last one
]                  # Close both the if-else statement and the loop
O                 # And take the sum of the stack
# (which is output implicitly as result)


See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •δ'ā∍ë*8U¾Ã• is 307264255556527588774514 and •δ'ā∍ë*8U¾Ã•₂в is [7,1,5,1,8,0,10,8,24,9,18,6,17,7,16,7,20].