loading...

Daily Challenge #248 - Chinese Numerals

thepracticaldev profile image dev.to staff ・1 min read

Create a function that takes a Number as its argument and returns a Chinese numeral string. You don't need to validate the input argument, it will always be a Number in the range [0, 99999] with no decimals.

Simplified Chinese numerals have characters representing each number from 0 to 9 and additional numbers representing larger numbers like 10, 100, 1000, and 10000.

0 líng 零
1 yī 一
2 èr 二
3 sān 三
4 sì 四
5 wǔ 五
6 liù 六
7 qī 七
8 bā 八
9 jiǔ 九
10 shí 十
100 bǎi 百
1000 qiān 千
10000 wàn 万

Multiple-digit numbers are constructed by first the digit value (1 to 9) and then the place multiplier (such as 10, 100, 1000), starting with the most significant digit. A special case is made for 10 - 19 where the leading digit value (yī 一) is dropped. Note that this special case is only made for the actual values 10 - 19, not any larger values. Trailing zeros are omitted, but interior zeros are grouped together and indicated by a single 零 character without giving the place multiplier.

Examples

10 十
11 十一
18 十八
21 二十一
110 一百一十
123 一百二十三
24681 二万四千六百八十一

Tests

to_chinese_numeral(9)
to_chinese_numeral(10)
to_chinese_numeral(110)
to_chinese_numeral(111)
to_chinese_numeral(1000)
to_chinese_numeral(10000)

Good luck!


This challenge comes from ConstableBrew on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

markdown guide
 

I didn't quite get what the deal was with the zero, and since there were no examples to show how it worked, I decided to do Japanese numerals, which are basically the same but without the zero. Here's my JavaScript implementation. It could probably be a lot simpler, but oh well.

const numerals = {
    0: '0',
    1: '',
    2: '',
    3: '',
    4: '',
    5: '',
    6: '',
    7: '',
    8: '',
    9: '',
}

const units = {
    0: '',
    1: '',
    2: '',
    3: '',
    4: '',
}

const toJapaneseNumeral = n => n
    .toString()
    .split('')
    .reverse()
    .map((x, i) => numerals[x] + units[i])
    .reverse()
    .join('')
    .replace('一十', '')
    .replace(/0.?/g, '')
 

I didn't quite get what the deal was with the zero

Wikipedia has a decent explanation: "Interior zeroes before the unit position (as in 1002) must be spelt explicitly. The reason for this is that trailing zeroes (as in 1200) are often omitted as shorthand, so ambiguity occurs. One zero is sufficient to resolve the ambiguity. Where the zero is before a digit other than the units digit, the explicit zero is not ambiguous and is therefore optional, but preferred. "

There are a few examples too, but they use traditional instead of simplified characters.

 

Add more test cases.

// 28901 二万八千九百零一
// 28911 二万八千九百一十一
// 20911 二万零九百一十一
// 20901 二万零九百零一
// 20001 二万零一
// 29000 二万九

let numberArray = [
  '',
  '',
  '',
  '',
  '',
  '',
  '',
  '',
  '',
  '',
]

let unitArray = ['', '', '', '', '']

const toChineseNumeral = input => {
  return input.toString().split('').map(
    (number, idx) => {
      return numberArray[parseInt(number)]
    }
  ).reduce(
    (acc, current) => {
      let last = acc[acc.length - 1]
      if (last == '' && current == '')
        acc[acc.length - 1] = ' '
      if (acc.length == (input.length - 1) && current == '')
        current = ''
      acc.push(current)
      return acc
    },
    []
  ).map(
    (elem, idx) => {
      if (elem == ' ' || elem == '')
        return elem.trim()
      return elem + unitArray[input.length-1 - idx]
    }
  ).join('').replace(/^一十/, '')
}

console.log(toChineseNumeral(29389))
console.log(toChineseNumeral(29000))
console.log(toChineseNumeral(20001))
console.log(toChineseNumeral(20901))

console.log([
    10,
    11,
    18,
    21,
    110,
    123,
    24681
].map(toChineseNumeral))
 

First time doing one of these. Figured I'd throw out one of those weird languages (Raku):

use v6;

sub toChineseNumeral(Int $number = 0) {
    my $numerals = <         >;
    my $places = $number.polymod(10, 10, 10, 10).reverse;
    my Str $numbers = join "", do for ^4 {
        my Int $number = $places[$_];
        next if $number == 0;
        $numerals[$number] ~ <   >[$_];
    }

    given $number {
        when 0..9 { $numerals[$places.tail] }
        when 10 { $numbers.substr(1) }
        when 11..19 { $numbers.substr(1) ~ $numerals[$places.tail] }
        default { $numbers ~ $numerals[$places.tail] }
    }
}

sub MAIN() {
    for [9, 10, 11, 18, 21, 110, 111, 123, 1000, 10000, 24681] {
        say $_ ~ ": " ~ toChineseNumeral($_)
    }
}