How many syllables in that number?

15

1

I'd love to take a number and know how many syllables are in it, when spoken in English.

Let's limit this to positive integers which are less than one thousand.

I'm British, so we're going to follow the hundreds column with an 'and' when there are any non-zero digits after it.

The Challenge

  • Write some code which will accept a positive integer lower than 1000 and output the number of syllables in the words which represent that number in British English.
  • It DOES NOT need to generate the words to represent the numbers, only the number of syllables they contain.
  • It's code golf, attempt to achieve this in the fewest bytes.
  • Use any language you like.
  • The standard loopholes are forbidden.

Test Cases

|  N  | In words                             | Syllables |
|   1 | one                                  |         1 |
|   2 | two                                  |         1 |
|   3 | three                                |         1 |
|   4 | four                                 |         1 |
|   5 | five                                 |         1 |
|   6 | six                                  |         1 |
|   7 | sev-en                               |         2 |
|   8 | eight                                |         1 |
|   9 | nine                                 |         1 |
|  10 | ten                                  |         1 |
|  11 | el-ev-en                             |         3 |
|  12 | twelve                               |         1 |
|  13 | thir-teen                            |         2 |
|  14 | four-teen                            |         2 |
|  17 | se-ven-teen                          |         3 |
|  20 | twen-ty                              |         2 |
|  21 | twen-ty one                          |         3 |
|  42 | four-ty two                          |         3 |
|  73 | sev-en-ty three                      |         4 |
|  77 | sev-en-ty sev-en                     |         5 |
| 100 | one hund-red                         |         3 |
| 110 | one hund-red and ten                 |         5 |
| 111 | one hund-red and el-ev-en            |         7 |
| 555 | five hund-red and fif-ty five        |         7 |
| 700 | sev-en hund-red                      |         4 |
| 770 | sev-en hund-red and sev-en-ty        |         8 |
| 777 | sev-en hund-red and sev-en-ty sev-en |        10 |
| 999 | nine hund-red and nine-ty nine       |         7 |

AJFaraday

Posted 2018-12-14T12:54:03.247

Reputation: 10 466

1Can we take input as a string or an array of digits? – Dennis – 2018-12-14T15:20:49.980

Answers

11

Python 2, 84 83 74 67 bytes

lambda n:4*(n>99)+2-n%~9/9-0x55561aaaab/4**(n%100)%4+`n`.count('7')

Thanks to @xnor for golfing off 9 16 bytes!

Try it online!


Python 2, 79 bytes

lambda n:4*(n>99)+([-1]+10*[1]+[3,1]+7*[2]+8*([2]+9*[3]))[n%100]+`n`.count('7')

Straightforward, but longer.

Try it online!

Dennis

Posted 2018-12-14T12:54:03.247

Reputation: 196 637

For your 83-byte solution, you can cut 3 bytes by changing -10 to ~9 and switching around the last bit to +(0<n%100!=12)-(n%100!=11), but that's still longer than your new solution. – xnor – 2018-12-14T20:27:55.313

@xnor That's really clever! min(n%100,13)%12/~9 might actually help with an approach I was trying for my Jelly answer as well. – Dennis – 2018-12-14T23:11:46.380

Actually, just shoving things into a hardcoded constant turns out shorter.

– xnor – 2018-12-14T23:48:38.577

@xnor Thanks again! – Dennis – 2018-12-14T23:57:49.640

8

Perl 5 -p, 53 bytes

$_=4*/.../+2*/[^0].$/+!/0$/+y/7//-/1[^1]$/-/12$/-/00/

Try it online!

How

-p commandline flag reads input into $_

$_=4*/.../     # Hundreds place has minimum of 4 sylables (__ HUN-DRED AND),
               # match fails on number <100, and would add 0 here
  +2*/[^0].$/  # Tens place has two syllables if not 0 (__-TY or __TEEN),
               # match fails on numbers <10, and would add 0
  +!/0$/       # Ones place has one syllable if not 0 (__)
               # -- Now adjust for special cases --
  +y/7//       # add a syllable for every 7 present
  -/1[^1]$/    # remove a syllable for 10-19, except 11
  -/12$/       # remove another syllable for 12
  -/00/        # remove the syllable for AND if it's an even hundred

-p commandline flag outputs contents of $_

Xcali

Posted 2018-12-14T12:54:03.247

Reputation: 7 671

7

Python 2, 112 108 bytes

f=lambda n:n>99and f(n/100)+3+f(n%100)-(n%100<1)or n>19and f(n/10)-~f(n%10)or int("01111112111312222322"[n])

Try it online!

-4 bytes, thanks to Shaggy

TFeld

Posted 2018-12-14T12:54:03.247

Reputation: 19 246

2Also, your [2]*7 part will fail for 17, since that should be 3 instead of 2 (sev-en-teen). – Kevin Cruijssen – 2018-12-14T14:24:43.120

2-4 bytes, including a fix for 17. – Shaggy – 2018-12-14T17:28:36.243

@Shaggy Thanks :) – TFeld – 2018-12-17T07:40:28.803

@KevinCruijssen Fixed now (thanks to Shaggy) – TFeld – 2018-12-17T07:40:44.140

7

JavaScript (ES6), 89 bytes

n=>(s='01111112111312222322',n>99&&+s[n/100|0]+3-!(n%=100))+~~(s[n]||+s[n/10|0]-~s[n%10])

Try it online!

Arnauld

Posted 2018-12-14T12:54:03.247

Reputation: 111 334

6

JavaScript, 86 84 bytes

A tweaked port of TFeld's Python solution.

f=n=>n>99?f(n/100)+3+f(n%=100)-!n:n>19?f(n/10)-~f(n%10):+"01111112111312222322"[n|0]

Try it online

2 bytes saved thanks to Arnauld

Shaggy

Posted 2018-12-14T12:54:03.247

Reputation: 24 623

6

Java 11, 105 102 bytes

n->(""+"".repeat(8)).charAt(n%100)+(n+"").split("7",9).length-(n>99?2:6)

Contains loads of unprintable characters.

-3 bytes thanks @OlivierGrégoire.

Try it online.

Explanation:


n->               // Method with integer as both parameter and return-type
  (""
                  //  Push string with ASCII-value digits 46666666666867777777
 +"".repeat(8))
                  //  Appended with 8 times a string with ASCII-value digits 7888888888
   .charAt(n%100) //  Take the (input modulo-100)'th character of this string (as integer)
  +(n+"").split("7",9).length
                  //  Count the amount of 7s in the input + 1
  -(n>99?         //  And if the input is larger than 99:
     2            //   Subtract 2 (-1 for the 7s+1 count; -5 to map the ASCII-digits to:
                  //               4 → -1; 6 → 1; 7 → 2; 8 → 3;
                  //               and +4 for the inputs above 99)
    :             //  Else:
     6)           //   Subtract 6 (-1 for the 7s+1 count and -5 to map the ASCII-digits to:
                  //               4 → -1; 6 → 1; 7 → 2; 8 → 3)

Kevin Cruijssen

Posted 2018-12-14T12:54:03.247

Reputation: 67 575

1102 bytes by changing .split("7",-1) to .split("7",9), and -6+(n>99?4:0) to -(n>99?2:6). – Olivier Grégoire – 2018-12-19T10:09:29.637

1@OlivierGrégoire Thanks. Completely missed -(n>99?2:6), but it's so obvious now that you've pointed it out. And -1 to 9 due to the limited input-size I wouldn't have thought of, so thanks! – Kevin Cruijssen – 2018-12-19T11:22:10.307

6

Wolfram Language 101 115 Bytes

s=StringSplit;Length[Join@@(WordData[#,"Hyphenation"]&/@Join@@s/@
s[IntegerName@#,"-"])]+Boole[#>100&&#~Mod~100!=0]&

Explanation

(substituting StringSplit for s)

Length[Join@@(WordData[#,"Hyphenation"]&/@Join@@
StringSplit/@ StringSplit[IntegerName@#,"-"])]+Boole[#>100&&#~Mod~100!=0]&

IntegerName renders the number in American English (i.e. without "and" included in numbers greater than 100.) E.g. 777-> "seven hundred seventy-seven.

StringSplit[IntegerName@#,"-"] removes any hyphens in the rendering.

StringSplit/@ splits the rendering into words.

Join@@ leaves a simple list of words, without embedded list (in the case that a hyphen appeared).

WordData[#,"Hyphenation"] breaks up a single word into its syllables.

Join@@ leaves a simple list of syllables in all of the words.

Length counts the syllables

+Boole[#>100&&#~Mod~100!=0] adds 1 to the syllable count for those numbers greater than 100 (because of the additional "and" employed in the British English rendering), excluding integral multiples of 100.

DavidC

Posted 2018-12-14T12:54:03.247

Reputation: 24 524

5

05AB1E, 34 31 bytes

т%U7¢I€Ā`Iт@3*X_(X20@X12Q(X11QO

Try it online or verify all [1,999] test cases.

Explanation:

With all checks mentioned it will result in 1 for truthy and 0 for falsey.

т%         # Take modulo-100 of the (implicit) input
           #  i.e. 710 → 10
  U        # Pop and store it in variable `X`
7¢         # Count the amount of 7s in the (implicit) input
           #  i.e. 710 → 1
I€Ā        # Trutify each digit in the input (0 if 0; 1 otherwise)
   `       # And push all of the mapped values to the stack
           #  i.e. 710 → [1,1,0]
Iт@        # Check if the input is larger than or equal to 100
           #  i.e. 710 → 1 (truthy)
   3*      # Multiply that result by 3 (for 'hund-red and')
           #  i.e. 1 → 3
X_         # Check if variable `X` is 0
           #  i.e. 10 → 0 (falsey)
  (        # And negate that (to remove 'and' when #00)
           #  i.e. 0 → 0
X20@       # Check if variable `X` is larger than or equal to 20 (for '-ty')
           #  i.e. 10 → 0 (falsey)
X12Q       # Check if variable `X` is exactly 12
           #  i.e. 10 → 0 (falsey)
    (      # And negate that (to remove 'teen')
           #  i.e. 0 → 0
X11Q       # Check if variable `X` is exactly 11 (for 'el-ev-en' minus 'one one')
           #  i.e. 10 → 0 (falsey)
O          # Sum everything on the stack (and output implicitly)
           #  i.e. [1,1,1,0,3,0,0,0,0] → 6

Kevin Cruijssen

Posted 2018-12-14T12:54:03.247

Reputation: 67 575

This fails the 700 test case. 'Seven Hundred' has 4 syllables, this returns 5. – AJFaraday – 2018-12-14T13:40:10.360

@AJFaraday Should be fixed now. Accidentally had I (input) instead of X (input mod 100) when checking if it's larger than 20 for the +1 of ty. – Kevin Cruijssen – 2018-12-14T13:43:16.263

I'm so sorry, it returns 0 for 'one hundred' – AJFaraday – 2018-12-14T13:44:36.330

@AJFaraday Fixed again.. > (check if input is larger than 100) has been replaced with @ (check if input is larger than or equal to 100). Maybe I should have checked some more test cases myself more carefully before posting.. Sorry about that.. – Kevin Cruijssen – 2018-12-14T13:46:46.947

That works, Sorry. I feel so unkind finding those issues. – AJFaraday – 2018-12-14T13:48:21.067

@AJFaraday No no, I'm glad you found them. I would feel bad for posting a solution which contained errors and someone finds out a couple of weeks later.. I'm glad you're testing everything so carefully! Should have done so myself some more.. Thanks! Will add an explanation soon. – Kevin Cruijssen – 2018-12-14T13:49:23.327

4By the way, loving the top hat on a rubix cube! – AJFaraday – 2018-12-14T14:02:59.630

5

Charcoal, 39 31 bytes

I⁻⁺↨E謬Iι²№θ7I§⁺”)∨∧⌈a¡↶”×0⁸⁰N

Try it online! Link is to verbose version of code. Explanation:

I⁻⁺

Calculate adjustments to the number of syllables and output the result as a string.

↨E謬Iι²

Start by changing each non-zero digit to 1 and then decoding as base 2. This gives the correct answer for most inputs.

№θ7

Add 1 for each 7.

I§⁺”)∨∧⌈a¡↶”×0⁸⁰N

Take the literal string 10000000001021111111 and append 80 zeros, then cyclically index by the input, and subtract that digit.

Neil

Posted 2018-12-14T12:54:03.247

Reputation: 95 035

4

Jelly, 28 25 23 bytes

9ḊŻ;2+⁵Żċ%ȷ2$ạDṠḄƊ+Dċ7Ɗ

Try it online!

How it works

9ḊŻ;2+⁵Żċ%ȷ2$ạDṠḄƊ+Dċ7Ɗ  Main link. Argument: n (integer in [1, ..., 999])

9                        Set the return value to 9.
 Ḋ                       Dequeue; yield [2, 3, 4, 5, 6, 7, 8, 9].
  Ż                      Zero; yield [0, 2, 3, 4, 5, 6, 7, 8, 9].
   ;2                    Concat 2, yield [0, 2, 3, 4, 5, 6, 7, 8, 9, 2].
     +⁵                  Add 10; yield [10, 12, 13, 14, 15, 16, 17, 18, 19, 12].
       Ż                 Zero; yield [0, 10, 12, 13, 14, 15, 16, 17, 18, 19, 12].
         %ȷ2$            Yield n % 1e2.
        ċ                Count the occurrences of the modulus in the array.
                 Ɗ       Combine the three links to the left into a monadic chain.
              D            Decimal; convert n to its array of digits in base 10.
               Ṡ             Take the sign of each decimal digit (0 or 1).
                Ḅ            Convert the array of signs from base 2 to integer.
             ạ           Compute the abs. difference of the results to both sides.
                      Ɗ  Combine the three links to the left into a monadic chain.
                   D       Decimal; convert n to its array of digits in base 10.
                    ċ7     Count the number of 7's.

Dennis

Posted 2018-12-14T12:54:03.247

Reputation: 196 637

3

PHP, 190 158 145 141 137 bytes

<?for($j=$p=0;$p<strlen($i=$argv[1]);)$j+=str_split($i)[$p++]>0;echo$j+substr_count($i,7)+3*($i>99)-!($i%=100)+($i>19)-($i==12)+($i==11);

Try it online!

A port of Kevin Cruijssen's solution (unfortunately it doesn't have the same brevity in PHP :) )

-32 45 thanks to Shaggy!

-3 thanks to Kevin Crujissen!

NK1406

Posted 2018-12-14T12:54:03.247

Reputation: 739

So many savings to be made here! Here's just a few very quick ones – Shaggy – 2018-12-15T00:50:48.540

1145 bytes. You can save a few more bytes using short tags but I can't remember how to use them on TIO. (Note: I'm on my phone so haven't tested all inputs.) – Shaggy – 2018-12-15T08:45:41.803

1@Shaggy 2 more bytes can be changed when using >99 and >19 instead of >=100 and >=20. – Kevin Cruijssen – 2018-12-15T09:59:10.993

1@KevinCruijssen actually that saves 3 bytes because it it going from 100 to 99 :) – NK1406 – 2018-12-15T15:01:00.523

I also managed to save another byte by placing the variable at the start of the echo. – NK1406 – 2018-12-15T15:02:12.233

I found that to use short tags add the command line option -d short_open_tag=On for -4 bytes – NK1406 – 2018-12-15T15:13:12.157

2

05AB1E, 24 bytes

Port of Dennis' jelly answer

8L>Ć¾šT+¾šsт%¢sSĀJCαs7¢+

Try it online! or as a Test suite

Explanation

8L>                       # push range [2 ... 9]
   Ć                      # enclose, append head
    ¾š                    # prepend 0
      T+                  # add 10 to each
        ¾š                # prepend 0
          sт%¢            # count occurrences of input % 100 in this list
              sS          # push input split into a list of digits
                Ā         # truthify, check each if greater than 0
                 JC       # convert from base-2 to base-10
                   α      # absolute difference
                    s7¢+  # add the amount of 7's in the input

Emigna

Posted 2018-12-14T12:54:03.247

Reputation: 50 798

1

05AB1E, 26 bytes

€ĀJCI7¢•Ž¢Γ}Þ±6u•¾80׫Iè(O

Port of @Neil's Charcoal answer, so make sure to upvote him as well if you like this answer!

Try it online or verify all test cases.

Compressed integer •Ž¢Γ}Þ±6u• can alternatively be •8JA•b2TÌǝ for the same byte-count.

Explanation:

€Ā                   # Trutify every digit in the (implicit) input
                     # (0 remains 0; everything else becomes 1)
  J                  # Join it together to a single string
   C                 # Convert from binary to integer
I7¢                  # Count the amount of 7s in the input
•Ž¢Γ}Þ±6u•           # Push compressed integer 10000000001021111111
          ¾80׫      # Append 80 "0"s
               Iè    # Index the integer (with automatic wraparound) into it
                 (   # Negate the result
O                    # Sum all values on the stack (and output implicitly)

See this 05AB1E answer of mine (section How to compress large integers?) to understand why •Ž¢Γ}Þ±6u• is 10000000001021111111.

Kevin Cruijssen

Posted 2018-12-14T12:54:03.247

Reputation: 67 575