Skip to content

Instantly share code, notes, and snippets.

@hackprime
Last active May 31, 2023 07:16
Show Gist options
  • Save hackprime/717b4b09a7cce7488e82 to your computer and use it in GitHub Desktop.
Save hackprime/717b4b09a7cce7488e82 to your computer and use it in GitHub Desktop.
Convert number from digits to words in Russian
var TRIPLET_NAMES = [
null,
['тысяча', 'тысячи', 'тысяч'],
['миллион', 'миллиона', 'миллионов'],
['миллиард', 'миллиарда', 'миллиардов'],
['триллион', 'триллиона', 'триллионов'],
['квадриллион', 'квадриллиона', 'квадриллионов'],
],
ZERO_NAME = 'нуль',
ONE_THOUSANT_NAME = 'одна',
TWO_THOUSANTS_NAME = 'две',
HUNDRED_NAMES = [
null,
'сто',
'двести',
'триста',
'четыреста',
'пятьсот',
'шестьсот',
'семьсот',
'восемьсот',
'девятьсот',
],
TEN_NAMES = [
null,
null,
'двадцать',
'тридцать',
'сорок',
'пятьдесят',
'шестьдесят',
'семьдесят',
'восемьдесят',
'девяносто',
],
UNIT_NAMES = [
ZERO_NAME,
'один',
'два',
'три',
'четыре',
'пять',
'шесть',
'семь',
'восемь',
'девять',
],
TEN_UNIT_NAMES = [
'десять',
'одиннадцать',
'двенадцать',
'тринадцать',
'четырнадцать',
'пятнадцать',
'шестнадцать',
'семнадцать',
'восемнадцать',
'девятнадцать',
],
INPUT_VALUE_LIMIT = Number.MAX_SAFE_INTEGER;
function pluralEnding(number, variants) {
var one = variants[0],
two = variants[1],
five = variants[2];
number = Math.abs(number);
number %= 100;
if (number >= 5 && number <= 20) {
return five;
}
number %= 10;
if (number === 1) {
return one;
} else if (number >= 2 && number <= 4) {
return two;
}
return five;
}
function numberInWords(number) {
var numberInWords = [],
tripletOfZerosMask = 0,
i,
pos,
length,
tripletNames,
tripletIndex,
digitPosition,
prevDigitValue;
if (typeof(number) !== `string`) {
number = number + '';
}
if (number > INPUT_VALUE_LIMIT) {
return undefined
}
length = number.length;
for (i = 0; i < length; i += 1) {
pos = length - 1 - i;
tripletIndex = Math.floor(pos / 3);
digitPosition = pos % 3;
digitValue = parseInt(number[i]);
if (digitPosition === 2) {
tripletOfZerosMask = 0
if (digitValue === 0) {
tripletOfZerosMask |= 0b100;
} else if (digitValue) {
numberInWords.push(HUNDRED_NAMES[digitValue]);
}
continue;
}
if (digitPosition === 1) {
if (digitValue === 0) {
tripletOfZerosMask |= 0b10;
} else if (digitValue === 1) {
numberInWords.push(TEN_UNIT_NAMES[parseInt(number[i + 1])])
} else if (digitValue) {
numberInWords.push(TEN_NAMES[digitValue])
}
continue
}
if (digitPosition === 0) {
prevDigitValue = i - 1 >= 0 ? parseInt(number[i - 1]) : null;
if (digitValue === 0) {
tripletOfZerosMask |= 0b1;
if (length === 1) {
numberInWords.push(ZERO_NAME);
}
} else if (
prevDigitValue && prevDigitValue !== 1 || !prevDigitValue
) {
if (tripletIndex === 1 && digitValue == 1) {
numberInWords.push(ONE_THOUSANT_NAME)
} else if (tripletIndex === 1 && digitValue == 2) {
numberInWords.push(TWO_THOUSANTS_NAME)
} else {
numberInWords.push(UNIT_NAMES[digitValue])
}
}
tripletNames = TRIPLET_NAMES[tripletIndex];
if (tripletNames && tripletOfZerosMask !== 0b111) {
if (prevDigitValue === 1) {
numberInWords.push(pluralEnding(10 + digitValue, tripletNames));
} else {
numberInWords.push(pluralEnding(digitValue, tripletNames));
}
}
continue;
}
}
return numberInWords.join(' ');
}
var assert = require('assert');
assert.equal(numberInWords(0), 'нуль');
assert.equal(numberInWords(5), 'пять');
assert.equal(numberInWords(10), 'десять');
assert.equal(numberInWords(16), 'шестнадцать');
assert.equal(numberInWords(30), 'тридцать');
assert.equal(numberInWords(53), 'пятьдесят три');
assert.equal(numberInWords(100), 'сто');
assert.equal(numberInWords(111), 'сто одиннадцать');
assert.equal(numberInWords(123), 'сто двадцать три');
assert.equal(numberInWords(204), 'двести четыре');
assert.equal(numberInWords(300), 'триста');
assert.equal(numberInWords(1000), 'одна тысяча');
assert.equal(numberInWords(1400), 'одна тысяча четыреста');
assert.equal(numberInWords(2018), 'две тысячи восемнадцать');
assert.equal(numberInWords(83756), 'восемьдесят три тысячи семьсот пятьдесят шесть');
assert.equal(numberInWords(293111), 'двести девяносто три тысячи сто одиннадцать');
assert.equal(numberInWords(5000000), 'пять миллионов');
assert.equal(numberInWords(32001950), 'тридцать два миллиона одна тысяча девятьсот пятьдесят');
assert.equal(numberInWords(5606031041), 'пять миллиардов шестьсот шесть миллионов тридцать одна тысяча сорок один');
assert.equal(numberInWords(9000000000006), 'девять триллионов шесть');
assert.equal(numberInWords(9007199254740991), 'девять квадриллионов семь триллионов сто девяносто девять миллиардов двести пятьдесят четыре миллиона семьсот сорок тысяч девятьсот девяносто один');
assert.equal(numberInWords(9007199254740992), undefined);
@Laser42
Copy link

Laser42 commented Apr 9, 2019

After row 10, add the following:
TWO_THOUSANTS_NAME = 'две',
In rows 95-99 , change code

            } else if (prevDigitValue && prevDigitValue !== 1
                    || !prevDigitValue) {
                numberInWords.push(tripletIndex === 1 && digitValue == 1
                    ? ONE_THOUSANT_NAME
                    : UNIT_NAMES[digitValue])
            }

to

            } else if (prevDigitValue && prevDigitValue !== 1
                    || !prevDigitValue) {
                numberInWords.push(tripletIndex === 1 && digitValue == 1
                    ? ONE_THOUSANT_NAME
                    : (tripletIndex === 1 && digitValue == 2 
                        ? TWO_THOUSANTS_NAME 
                        :UNIT_NAMES[digitValue])
                )
            }

@Demuzx
Copy link

Demuzx commented Jul 15, 2019

numberInWords(5000000) - пять миллионов тысяч

@hackprime
Copy link
Author

@precious @Laser42 @Demuzx Thank you for comments and sorry for the late reply. I have updated the gist.

  • Now it doesn't push a triplet name if all digits are zero;
  • and it uses the correct word for "two thousand".

@iglv
Copy link

iglv commented May 13, 2021

'INPUT_VALUE_LIMIT' is assigned a value but never used

@hackprime
Copy link
Author

@iglv Thanks for your comment! I have updated the code, added the upper limit for the input value of the function.

@latdev
Copy link

latdev commented Oct 19, 2022

«Нуль» или «ноль», как пишется правильно?
«Ноль» и «нуль» правильно пишутся с буквой «о» и «у» как варианты одного и того же слова.

Отметим, что если разговор идет о величине чего-либо исчисляемого, то чаще употребляется слово «ноль»:

ноль рублей в кошельке;
ноль градусов;
счёт ноль-ноль;
ноль часов тридцать минут.

@Bekjon22
Copy link

it working well. thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment