16
\$\begingroup\$

Introduction:

When saying numbers out loud, the order is slightly different in Dutch than in English. For a number like \$1234\$:

  • in English you'd say: one thousand, two hundred and thirty-four
  • in Dutch you'd say: duizend-tweehonderdenvierendertig, or more readable with spaces: duizend twee honderd en vier en dertig [literally translated to: thousand two hundred and four and thirty]

Note two things in this example:

  1. How in English we have 'thirty-four', yet the order in Dutch is 'four and thirty'.
  2. How in English we have 'one thousand', yet in Dutch the 'one' is omitted for 'one hundred', 'one thousand', 'one million', etc.

Challenge:

Given a positive integer, convert its digits and digit order to the correct Dutch one and return it (as string/digit-list/etc., due to a potential leading \$0\$).

So with the example in the introduction above, with an input of 1234, you'd return "0243" (where the leading 0 is for the omitted 1 of 'one thousand' to simply 'duizend').

General formula:

  1. First split on the thousands from the back - if applicable (e.g. 1234567890 to \$1\text{,}234\text{,}567\text{,}890\$, but inputs 1, 12, 123, etc. are already a single group).
  2. Then for every group of two or three digits, swap the last two, except for:
    • If the last two digits as number are in the range \$[00,19]\$
    • If the last digit is a \$0\$, aka the last two digits are one of: \$[20,30,40,50,60,70,80,90]\$
  3. If the very first digit of a group is a \$1\$, it'll sometimes becomes a \$0\$. This depends on whether it's:
    1. The only one in a group after splitting on the thousands (e.g. 1234, which is \$1\text{,}234\$ with thousand separators, becomes "0243", but 12345, which is \$12\text{,}345\$ with thousand separators becomes 12354).
    2. In the Dutch language, and therefore in extension this challenge, 100 is also a separated group (e.g. 100 becomes 000; 123456 (\$123\text{,}456\$) becomes "032465", etc.)
    3. (Note that 1 and 10 won't change, despite having a 1 as first digit in their own 'group'. This in extension also means 101 maps to "001", not "000" (also applies within groups - e.g. 101010 with thousand groups \$101\text{,}010\$ becomes "001010".)

(Optional 4. Note that leading 0s within a group are ignored for this sub-rule - with the exception of 'één'. E.g. 1001001 split on the thousands is \$1\text{,}001\text{,}001\$, which would result in "0000001". Again, this is optional, since I forgot about this edge case and it would break almost all existing answers.. So outputting "0001001" instead is ok.)

Challenge rules:

  • We only care about the digits/numbers, so the differences in and (e.g. 205 being 'two hundred and five' in English versus 'tweehonderdvijf' [two hundred five] in Dutch) is irrelevant for this challenge.
  • The numbers in the range \$[13,19]\$ (thirteen-nineteen in English or dertien-negentien in Dutch) are already incorrect in English (e.g. 19 = ninete(e)n instead of te(e)nnine), so those are kept as is when converting to the Dutch order as well. Keeping the order unchanged applies to range \$[1,12]\$ (one-twelve in English or één-twaalf in Dutch) as well.
  • I/O is flexible, so may be a string, digit-array/list/stream, etc. If you take the input as string, you may not take it formatted with thousand separators already in place.
  • There can be overlap in outputs for multiple inputs. E.g. 19 (negentien - nineteen) and 91 (éénennegentig - one and ninety) will both result in the output 19.
  • The input will always be positive, so no need to work for negative values as well (or 0).

General Rules:

  • This is , so the shortest answer in bytes wins.
    Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
  • Standard rules apply for your answer with default I/O rules, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters and return-type, full programs. Your call.
  • Default Loopholes are forbidden.
  • If possible, please add a link with a test for your code (e.g. TIO or ATO).
  • Also, adding an explanation for your answer is highly recommended.

Test Cases:

Input:          Output:

1               1            [één]
10              10           [tien]
19              19           [negentien]
91              19           [éénennegentig]
100             000          [honderd]
123             032          [honderdtweeëndertig]
1101            0001         [duizend-honderdéén]
1234            0243         [duizend-tweehonderdenvierendertig]
10101           10001        [tienduizend-honderdéén]
12678           12687        [twaalfduizend-zeshonderdzevenentachtig]
21876           12867        [éénentwintigduizend-achthonderdzesenzeventig]
101010          001010       [honderdéénduizend-tien]
123456          032465       [honderddrieëntwintig-vierhonderdzesenvijftig]
223344          232344       [tweehonderddrieëntwintigduizend-driehonderdvierenveertig]
1234567890      0243576890   [miljard-tweehonderddrieënveertigmiljoen-vijfhonderdenzevenenzestigduizend-achthonderdnegentig]

Here the first 100 values:

[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
 12,22,32,42,52,62,72,82,92,30,13,23,33,43,53,63,73,83,93,40,
 14,24,34,44,54,64,74,84,94,50,15,25,35,45,55,65,75,85,95,60,
 16,26,36,46,56,66,76,86,96,70,17,27,37,47,57,67,77,87,97,80,
 18,28,38,48,58,68,78,88,98,90,19,29,39,49,59,69,79,89,99,000]

Optional test case:

1001001         0000001      [miljoen-duizend-één]

(Outputting 0001001 is fine as well, since it's an edge case I forgot about when I made the challenge, and apparently almost every existing answer breaks for it..)

\$\endgroup\$
10
  • 1
    \$\begingroup\$ What are the correct answers for 101010 and 1001001? \$\endgroup\$ Commented 2 days ago
  • 1
    \$\begingroup\$ @neil in Dutch that'd be 'Hundred and one - thousand - (and) ten'. So if I read the challenge correctly, 001010? \$\endgroup\$ Commented 2 days ago
  • 1
    \$\begingroup\$ @Neil I've added both test cases. Excellor is indeed correct about the first one being 001010 (honderéénduizend-tien : hundred one thousand - ten). And the second one is 0000001 (miljoen-duizend-één : million - thousand - one). \$\endgroup\$ Commented 2 days ago
  • 1
    \$\begingroup\$ @Neil Since 1001001 wasn't covered by my general formula and breaks all existing answers (except for Arnauld's apparently), I've added it as an optional test case instead. Outputting 0001001, even though it's technically not correct, is fine for the challenge.. \$\endgroup\$ Commented 2 days ago
  • 1
    \$\begingroup\$ Indeed, @Arnauld's answer fails on 101010 instead, thus the question ;-) \$\endgroup\$ Commented 2 days ago

7 Answers 7

4
\$\begingroup\$

Google Sheets, 225 bytes

=let(x,split(text(A1,"#,000"),","),join(,map(sequence(count(x)),lambda(i,let(p,text(index(x,i),"000"),r,right(p),y,if(or(mod(p,100)<20,-r=0),p,left(p)&r&mid(p,2,1)),ifs(left(y)="1",0&right(y,2),i+y=2,n(A1=1),i=1,--y,1,y))))))

Simple implementation of the algorithm in the question. Expects a number and returns a string.

screenshot

Prettified:

=let(
  x, split(text(A1, "#,000"), ","),
  join(, map(sequence(count(x)), lambda(i, let(
    p, text(index(x, i), "000"),
    r, right(p),
    y, if(or(mod(p, 100) < 20, -r = 0), 
      p, 
      left(p) & r & mid(p, 2, 1)
    ),
    ifs(
      left(y) = "1", 0 & right(y, 2), 
      i + y = 2, n(A1 = 1),
      i = 1, --y,
      1, y
    )
  ))))
)
\$\endgroup\$
2
  • 2
    \$\begingroup\$ you can save 1 by using -right(p)=0 instead of right(p)="0" \$\endgroup\$ Commented yesterday
  • 2
    \$\begingroup\$ Heh, unary minus instead of doubleunary. Thanks @z.. \$\endgroup\$ Commented yesterday
2
\$\begingroup\$

Jelly, 30 bytes

Ṛs3UṚµṫ-t0Ḍ>19‘œ?LḂ_Ɗḣ1aƲ)ƲḊ¡F

A monadic Link that accepts the number as a list of integers - its base ten digits - and yields a list of integers.

Try it online! Or see the test-suite.

How?

Ṛs3UṚµṫ-t0Ḍ>19‘œ?LḂ_Ɗḣ1aƲ)ƲḊ¡F - Link: list of integers, N
Ṛ                              - reverse {N}
                            ¡  - if...
                           Ḋ   - ...condition: dequeue (i.e. if length(N) > 1)
                          Ʋ    - ...then: last four links as a monad - f(reversed N):
 s3                            -   split into chunks of three, or fewer
   U                           -   reverse each
    Ṛ                          -   reverse - e.g. N = [1,2,3,4,5] -> Parts = [[1,2],[3,4,5]]
     µ                   )     -   for each Part:
      ṫ-                       -     tail from 1-index -1
                                       -> last, up to two digits of the part
        to                     -     trim zeros from each side
          Ḍ>19                 -     is that, converted from base ten, greater than 19?
              ‘                -     increment -> P = 2 if so, otherwise P = 1
                        Ʋ      -     last four links as a monad - f(Part):
                    Ɗ          -       last three links as a monad - f(Part):
                 L             -         length {Part}
                  Ḃ            -         is odd?
                   _           -         subtract {Part} (vectorsises)
                     ḣ         -         head to 1-index...
                      1        -         ...one -> [length is odd - first digit of Part]
                       a       -         logical AND {Part}
                                           Note: [x] AND [a, b, c] -> [(x AND a), b, c]
                                            and: all numbers are truthy except zero
               œ?              -     get the {P}th permutation of {that altered Part}
                                       (swap the last two digits if P = 2)
                          F    - flatten
\$\endgroup\$
0
2
\$\begingroup\$

Charcoal, 35 bytes

⮌⭆⪪⮌S³⭆ι∧∧∨¬κ⊖⮌ι⁻⊗λμ⎇⬤…ι²›Iνξ§ι⁻¹μλ

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

    S                               First input as a string
   ⮌                                Reversed
  ⪪                                 Split into groups of length
     ³                              Literal integer `3`
 ⭆                                  Map over groups and join
       ι                            Current group
      ⭆                             Map over digits and join
            κ                       Group index
           ¬                        Is zero
          ∨                         Logical Or
               ι                    Current group
              ⮌                     Unreversed
             ⊖                      Is not one
         ∧                          Logical And
                  λ                 Current digit
                 ⊗                  Doubled
                ⁻                   Does not equal
                   μ                Digit index
        ∧                           Logical And
                       ι            Current group
                      …             Truncated to length
                        ²           Literal integer `2`
                     ⬤              All digits satisfy
                           ν        Innermost digit
                          I         Cast to integer
                         ›          Is greater than
                            ξ       Innermost index
                    ⎇               If true then
                              ι     Current group
                             §      Cyclically indexed by
                                ¹   Literal integer `1`
                               ⁻    Subtract
                                 μ  Digit index
                                  λ Else current digit
⮌                                  Unreversed
                                   Implicitly print

Some byte savings were obtained:

  • by zeroing out all of the digits of the groups 1 and 001, not just the last
  • by zeroing out the last digit of groups ending in 0
  • by reversing groups that are single digits 2 to 9 (they get multiplied by 11, reversed, then divided by 11 again).
\$\endgroup\$
2
\$\begingroup\$

R, 99 98 94 bytes

\(n,`[`=gsub)"([^01])([^0])(?=(...)*$)"["\\2\\1","(^1(?=.)|1(..))(?=(...)*$)"["0\\2",n,,T],,T]

Attempt This Online!

-4 bytes by pajonk.


R, 258 bytes
\(n,`?`=\(x)paste(x,collapse=""),`~`=strsplit,`-`=nchar){r=?rev(el(n~""))
L=-r
o={}
for(i in seq(1,L,3)){g=substring(r,i,e<-min(i+2,L))
s=el(g~"")
n=-g
if(n<2&s[1]<2&L>1|n>2&s[3]<2)s[max(1,n)]="0"
if(n>1&!grepl("^.[01]|^0",g))s[1:2]=s[2:1]
o=c(o,s)}
?rev(o)}

Attempt This Online!

\$\endgroup\$
2
  • \$\begingroup\$ replace both pe= with , for -4:ato.pxeger.com/… \$\endgroup\$ Commented 2 days ago
  • \$\begingroup\$ In the second solution: paste(x,collapse="") -> Reduce(paste0,x) for -4 bytes \$\endgroup\$ Commented 2 days ago
2
\$\begingroup\$

Perl 5 -p, 73 69 bytes

s/((^1\B)|(1(..)))(?=(...)*$)/0$4/g;s/([^01])([^0])(?=(...)*$)/$2$1/g

Try it online!

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

JavaScript (ES6), 74 bytes

Expects a string. Returns another string.

s=>s.replace(/1(?=..(...)*$)|(^1\B|[2-9][^0])(?=(...)*$)/g,s=>s[1]+s[0]|0)

Try it online!

Try the first 100 values

Method

We look for:

  • 1(?=..(...)*$) → a 1 followed by two digits, followed by any number of 3-digit groups until the end of the string
  • ^1\B(?=(...)*$) → a 1 at the beginning of the string, not immediately followed by the end of the string, followed by any number of 3-digit groups until the end of the string (1)
  • [2-9][^0](?=(...)*$) → a digit in the range 2-9, followed by a digit in the range 1-9, followed by any number of 3-digit groups until the end of the string

We replace each match s with s[1] + s[0] | 0, which results in:

  • 0 if the match is 1 (because this yields "undefined1" | 0, which evaluates to 0)
  • otherwise, the reversed two-digit number (which is guaranteed to be coerced back into a two-digit string, since it cannot start with a zero)

(1) This could obviously be more simply described as ^1(?=(...)+$), but then the pattern (?=(...)*$) could not be factorized with the next case in the final regular expression.

Worked example

.-----------> ^1(...)(...)(...)$ replaced with 0
|.----------> 1(..)(...)(...)$ replaced with 0
||   .------> 34(...)$ replaced with 43
||   |  .---> 45$ replaced with 54
||   |_ |_
1100234345 -> 0000243354
\$\endgroup\$
3
  • 1
    \$\begingroup\$ 72 \$\endgroup\$ Commented 2 days ago
  • 1
    \$\begingroup\$ Sorry for the possibly confusing rules.. I should have kept this in the Sandbox for way longer.. 😔But 101010 should be 001010 (honderdéénduizend - tien : hundred one thousand - ten). (PS: 1001001 can be either 0001001 or 0000001 - 0000001 would be the correct answer, but I forgot about this edge case when I made the challenge, and I feel like it's a bit too late to change it now, hence why either is an option.) \$\endgroup\$ Commented 2 days ago
  • 1
    \$\begingroup\$ @KevinCruijssen Thanks for the notification. Hopefully fixed now (but 1001001 gives 0001001). \$\endgroup\$ Commented 2 days ago
1
\$\begingroup\$

Retina, 45 bytes

r`..?.?
$&,
V`[2-9][1-9]\b
\b1(?=..,|,.)
0
,

Try it online!

  • r`..?.?: Match chunks of 1 to 3 digits, using r to enable right-to-left mode.
    $&,: Replace each match with itself followed by a comma.
  • V`[2-9][1-9]\b: Match a ≥2 digit followed by a ≥1 digit ending a chunk (using a word boundary: digits are word characters and commas are non-word characters). Stage type V reverses each match.
  • \b1(?=..,|,.): Match a 1 at the start of a chunk if it is followed by something matching .., (making it a 3-digit chunk) or matching ,. (making it a 1-digit chunk that is not the last chunk). Replace it with 0.
  • Finally, match , and replace it with an empty string.

Retina, 53 bytes, follows rule 4

r`..?.?
$&,
V`[2-9][1-9]\b
(?<=00|\b)1(?=..,|,.)
0
,

Try it online!

Mostly the same as before; the third regex uses a lookbehind to also match the 1 in 001.

\$\endgroup\$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.