Как сделать склонение числительных на javascript.
@volond
Без склонения числительных пользователь может увидеть «8 мортышка(ек)» или «4 трамваев» и т.д. Это не красиво и усложняет процесс восприятия интерфейса.
Поэтому для многих стало нормой делать системы, которые бы склоняли числительные, так, чтобы пользователю было удобно читать интерфейс, так, как будто интерфейс – живое существо.
Как сделать склонение числительных на javascript
Подход очень простой. В русском языке есть несколько падежей, в которых числительное может выступать в зависимости от числа, с которым они используются.
Ниже приведена функция, которая выполняет склонение числительных на javascript:

function declOfNum(number, words) {
return words[(number % 100 > 4 && number % 100 < 20) ?
2 : [2, 0, 1, 1, 1, 2][(number % 10 < 5) ?
number % 10 : 5
]];
}
В эту функцию передается число (параметр number), и массив заголовков (параметр titles), среди которых будет выбрана правильная форма, подходящая к числу.
Первый элемент title — для единиц, второй — для пар, третия — для других чисел.
Например:
Нужно вывести фразу N арбузов, вызовем функцию:
title=declOfNum(N,['арбуз','арбуза','арбузов']);
В результате склонение числительных на javascript сработает, и мы получим (в зависимости от N):
1 арбуз
2 арбуза
12 арбузов
Всё просто, и удобно.
Еще одно применение пропишем правильно дату:

Но воспользуемся функцией каррирования
function curry(fn) {
const arity = fn.length;
return function $curry(...args) {
if (args.length < arity) {
return $curry.bind(null, ...args);
}
return fn.call(null, ...args);
};
};
и перепишем функцию declOfNum
const curryDeclOfNum = curry((wordsMap, number, key) => {
return wordsMap.get(key)[(number % 100 > 4 && number % 100 < 20) ?
2 : [2, 0, 1, 1, 1, 2][(number % 10 < 5) ?
number % 10 : 5
]];
})
теперь предыдущий пример выглядит так:

var wordsMap = new Map([
[0, ['год', 'года', 'лет']],
[1, ['день', 'дня', 'дней']],
[2, ['час', 'часа', 'часов']],
[3, ['минута', 'минуты', 'минут']],
[4, ['секунда', 'секунды', 'секунд']]
])
var timeDeclOfNum = curryDeclOfNum(wordsMap)
var timeReducer = (t, n, i) => { return !n || [...t, `${n} ${timeDeclOfNum(n, i)}`] }
console.log([10, 45, 13, 1].reduce(timeReducer, []).join(' '))

или единицы измерения:
var wordsMapEd = new Map([
['шт.', ['штука', 'штуки', 'штук']],
['кг.', ['килограмм', 'килограмма', 'килограммов']],
])
var edDeclOfNum = curryDeclOfNum(wordsMapEd)
var edReducer = (t, word) => {
var [n, key] = word.split(' ')
return !n || [...t, `${n} ${edDeclOfNum(+n, key)}`]
}
console.log(['10 шт.', "45 кг.", "13 шт.", "1 кг."].reduce(edReducer, []).join(' '))
