15
\$\begingroup\$

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 |
\$\endgroup\$
  • 1
    \$\begingroup\$ Can we take input as a string or an array of digits? \$\endgroup\$ – Dennis Dec 14 '18 at 15:20

13 Answers 13

11
\$\begingroup\$

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!

\$\endgroup\$
  • \$\begingroup\$ 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. \$\endgroup\$ – xnor Dec 14 '18 at 20:27
  • \$\begingroup\$ Here we go: lambda n:4*(n>99)+`n`.count('7')+cmp(n/10%10,1)-n%~9/9-min(n%100,13)%12/~9 \$\endgroup\$ – xnor Dec 14 '18 at 20:58
  • \$\begingroup\$ @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. \$\endgroup\$ – Dennis Dec 14 '18 at 23:11
  • \$\begingroup\$ Actually, just shoving things into a hardcoded constant turns out shorter. \$\endgroup\$ – xnor Dec 14 '18 at 23:48
  • \$\begingroup\$ @xnor Thanks again! \$\endgroup\$ – Dennis Dec 14 '18 at 23:57
8
\$\begingroup\$

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 $_
\$\endgroup\$
7
\$\begingroup\$

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!

\$\endgroup\$
7
\$\begingroup\$

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

\$\endgroup\$
  • 2
    \$\begingroup\$ Also, your [2]*7 part will fail for 17, since that should be 3 instead of 2 (sev-en-teen). \$\endgroup\$ – Kevin Cruijssen Dec 14 '18 at 14:24
  • 2
    \$\begingroup\$ -4 bytes, including a fix for 17. \$\endgroup\$ – Shaggy Dec 14 '18 at 17:28
  • \$\begingroup\$ @Shaggy Thanks :) \$\endgroup\$ – TFeld Dec 17 '18 at 7:40
  • \$\begingroup\$ @KevinCruijssen Fixed now (thanks to Shaggy) \$\endgroup\$ – TFeld Dec 17 '18 at 7:40
6
\$\begingroup\$

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

\$\endgroup\$
6
\$\begingroup\$

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.

\$\endgroup\$
6
\$\begingroup\$

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)
\$\endgroup\$
  • 1
    \$\begingroup\$ 102 bytes by changing .split("7",-1) to .split("7",9), and -6+(n>99?4:0) to -(n>99?2:6). \$\endgroup\$ – Olivier Grégoire Dec 19 '18 at 10:09
  • 1
    \$\begingroup\$ @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! \$\endgroup\$ – Kevin Cruijssen Dec 19 '18 at 11:22
5
\$\begingroup\$

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
\$\endgroup\$
  • \$\begingroup\$ This fails the 700 test case. 'Seven Hundred' has 4 syllables, this returns 5. \$\endgroup\$ – AJFaraday Dec 14 '18 at 13:40
  • \$\begingroup\$ @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. \$\endgroup\$ – Kevin Cruijssen Dec 14 '18 at 13:43
  • \$\begingroup\$ I'm so sorry, it returns 0 for 'one hundred' \$\endgroup\$ – AJFaraday Dec 14 '18 at 13:44
  • \$\begingroup\$ @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.. \$\endgroup\$ – Kevin Cruijssen Dec 14 '18 at 13:46
  • 4
    \$\begingroup\$ By the way, loving the top hat on a rubix cube! \$\endgroup\$ – AJFaraday Dec 14 '18 at 14:02
5
\$\begingroup\$

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.

\$\endgroup\$
4
\$\begingroup\$

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.
\$\endgroup\$
3
\$\begingroup\$

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!

\$\endgroup\$
  • \$\begingroup\$ So many savings to be made here! Here's just a few very quick ones \$\endgroup\$ – Shaggy Dec 15 '18 at 0:50
  • 1
    \$\begingroup\$ 145 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.) \$\endgroup\$ – Shaggy Dec 15 '18 at 8:45
  • 1
    \$\begingroup\$ @Shaggy 2 more bytes can be changed when using >99 and >19 instead of >=100 and >=20. \$\endgroup\$ – Kevin Cruijssen Dec 15 '18 at 9:59
  • 1
    \$\begingroup\$ @KevinCruijssen actually that saves 3 bytes because it it going from 100 to 99 :) \$\endgroup\$ – NK1406 Dec 15 '18 at 15:01
  • \$\begingroup\$ I also managed to save another byte by placing the variable at the start of the echo. \$\endgroup\$ – NK1406 Dec 15 '18 at 15:02
2
\$\begingroup\$

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
\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.