В предыдущем уроке мы создали скрипт на php который переводить число в сумму прописью, но бывают ситуации когда нужно преобразовать в клиентской части, а не обращаться к серверу за результатом. В данной статье я приведу пример функции которая преобразовывает число в сумму прописью.
HTML
Для начала создадим наше поле в которое будем вводить число:
<input type="text" class="num" />
И область для вывода результата
<span class="result"></span>
Javascript
Также нам понадобится функция которая будет преобразовывать строку в массив, аналог функции в PHP str_split:
/** * Преобразует строку в массив */ function str_split(string, length) { var chunks, len, pos; string = (string == null) ? "" : string; length = (length == null) ? 1 : length; var chunks = []; var pos = 0; var len = string.length; while (pos < len) { chunks.push(string.slice(pos, pos += length)); } return chunks; };
И функцию для склонения словоформ:
/** * Склоняем словоформу */ function morph(number, titles) { var cases = [2, 0, 1, 1, 1, 2]; return titles[ (number0>4 && number0<20)? 2 : cases[Math.min(number, 5)] ]; };
И теперь функция для преобразования числа в сумму прописью
/** * Возвращает сумму прописью */ function number_to_string (num) { var def_translite = { null: 'ноль', a1: ['один','два','три','четыре','пять','шесть','семь','восемь','девять'], a2: ['одна','две','три','четыре','пять','шесть','семь','восемь','девять'], a10: ['десять','одиннадцать','двенадцать','тринадцать','четырнадцать','пятнадцать','шестнадцать','семнадцать','восемнадцать','девятнадцать'], a20: ['двадцать','тридцать','сорок','пятьдесят','шестьдесят','семьдесят','восемьдесят','девяносто'], a100: ['сто','двести','триста','четыреста','пятьсот','шестьсот','семьсот','восемьсот','девятьсот'], uc: ['копейка', 'копейки', 'копеек'], ur: ['рубль', 'рубля', 'рублей'], u3: ['тысяча', 'тысячи', 'тысяч'], u2: ['миллион', 'миллиона', 'миллионов'], u1: ['миллиард', 'миллиарда', 'миллиардов'], } var i1, i2, i3, kop, out, rub, v, zeros, _ref, _ref1, _ref2, ax; _ref = parseFloat(num).toFixed(2).split('.'), rub = _ref[0], kop = _ref[1]; var leading_zeros = 12 - rub.length; if (leading_zeros < 0) { return false; } var zeros = []; while (leading_zeros--) { zeros.push('0'); } rub = zeros.join('') + rub; var out = []; if (rub > 0) { // Разбиваем число по три символа _ref1 = str_split(rub, 3); for (var i = -1; i < _ref1.length;i++) { v = _ref1[i]; if (!(v > 0)) continue; _ref2 = str_split(v, 1), i1 = parseInt(_ref2[0]), i2 = parseInt(_ref2[1]), i3 = parseInt(_ref2[2]); out.push(def_translite.a100[i1-1]); // 1xx-9xx ax = (i+1 == 3) ? 'a2' : 'a1'; if (i2 > 1) { out.push(def_translite.a20[i2-2] + (i3 > 0 ? ' ' + def_translite[ax][i3-1] : '')); // 20-99 } else { out.push(i2 > 0 ? def_translite.a10[i3] : def_translite[ax][i3-1]); // 10-19 | 1-9 } if (_ref1.length > i+1){ var name = def_translite['u'+(i+1)]; out.push(morph(v,name)); } } } else { out.push(def_translite.null); } // Дописываем название "рубли" out.push(morph(rub, def_translite.ur)); // Дописываем название "копейка" out.push(kop + ' ' + morph(kop, def_translite.uc)); // Объединяем маcсив в строку, удаляем лишние пробелы и возвращаем результат return out.join(' ').replace(RegExp(' {2,}', 'g'), ' ').trimLeft(); };
Пример использования:
$(document).ready(function(){ $(".num").change(function(){ var value = $(this).val(); if(value){ $(".result").html(number_to_string(value)); } }); });
На этом все.
Украинский вариант до миллиона на typescript
type Values = [number, number, string] | [number, number, string, string];
interface MapNumbers {
[k: string]: Values
}
const mapNumbers: MapNumbers = {
0: [2, 1, "нуль"],
1: [0, 2, "один", "одна"],
2: [1, 2, "два", "дві"],
3: [1, 1, "три"],
4: [1, 1, "чотири"],
5: [2, 1, "п'ять"],
6: [2, 1, "шість"],
7: [2, 1, "сім"],
8: [2, 1, "вісім"],
9: [2, 1, "дев'ять"],
10: [2, 1, "десять"],
11: [2, 1, "одинадцять"],
12: [2, 1, "дванадцять"],
13: [2, 1, "тринадцять"],
14: [2, 1, "чотирнадцять"],
15: [2, 1, "п'ятнадцять"],
16: [2, 1, "шістнадцять"],
17: [2, 1, "сімнадцять"],
18: [2, 1, "вісімнадцять"],
19: [2, 1, "дев'ятнадцять"],
20: [2, 1, "двадцять"],
30: [2, 1, "тридцять"],
40: [2, 1, "сорок"],
50: [2, 1, "п'ятдесят"],
60: [2, 1, "шістдесят"],
70: [2, 1, "сімдесят"],
80: [2, 1, "вісімдесят"],
90: [2, 1, "дев'яносто"],
100: [2, 1, "сто"],
200: [2, 1, "двісті"],
300: [2, 1, "триста"],
400: [2, 1, "чотириста"],
500: [2, 1, "п'ятсот"],
600: [2, 1, "шістсот"],
700: [2, 1, "сімсот"],
800: [2, 1, "вісімсот"],
900: [2, 1, "дев'ятсот"]
};
type PluralAmount = [string, string, string];
const mapOrders: PluralAmount[] = [
["гривня", "гривні", "гривень"],
["тисяча", "тисячі", "тисяч"],
];
const objKop: PluralAmount = ["копійка", "копійки", "копійок"];
function Value(dVal: number): string | undefined {
const xVal: Values = mapNumbers[dVal];
if (xVal[1] === 1) {
return xVal[2];
} else {
return xVal[3];
}
}
function From0To999(fValue: number, oObjDesc: PluralAmount, fnAddDesc: (kop: string) => void, fnAddNum?: (val: any) => void) {
let nCurrState: number = 2;
if (Math.floor(fValue / 100) > 0) {
const fCurr: number = Math.floor(fValue / 100) * 100;
fnAddNum && fnAddNum(Value(fCurr));
nCurrState = mapNumbers[fCurr][0];
fValue -= fCurr;
}
if (fValue < 20) {
if (Math.floor(fValue) > 0) {
fnAddNum && fnAddNum(Value(fValue));
nCurrState = mapNumbers[fValue][0];
}
} else {
const fCurr: number = Math.floor(fValue / 10) * 10;
fnAddNum && fnAddNum(Value(fCurr));
nCurrState = mapNumbers[fCurr][0];
fValue -= fCurr;
if (Math.floor(fValue) > 0) {
fnAddNum && fnAddNum(Value(fValue));
nCurrState = mapNumbers[fValue][0];
}
}
fnAddDesc(oObjDesc[nCurrState]);
}
export function amountToWords(fAmount: number): string {
let fInt: number = Math.floor(fAmount + 0.005);
const fDec: number = Math.floor(((fAmount - fInt) * 100) + 0.5);
const arrRet: string[] = [];
const arrThousands: number[] = [];
for (; fInt > 0.9999; fInt /= 1000) {
arrThousands.push(Math.floor(fInt % 1000));
}
if (arrThousands.length == 0) {
arrThousands.push(0);
}
function PushToRes(strVal: any) {
arrRet.push(strVal);
}
for (let iSouth = arrThousands.length - 1; iSouth >= 0; --iSouth) {
if (arrThousands[iSouth] == 0) {
continue;
}
From0To999(arrThousands[iSouth], mapOrders[iSouth], PushToRes, PushToRes);
}
if (arrThousands[0] == 0) {
// Handle zero amount
if (arrThousands.length == 1) {
PushToRes(Value(0));
}
const nCurrState = 2;
PushToRes(mapOrders[0][nCurrState]);
}
if (arrRet.length > 0) {
// Capitalize first letter
arrRet[0] = arrRet[0][0].toUpperCase() + arrRet[0].substr(1);
}
arrRet.push(fDec.toString().padStart(2, "0"));
From0To999(fDec, objKop, PushToRes);
return arrRet.join(" ");
}
за javascript = Большое СПАСИБО Вам !
Подскажите, пожалуйста, как реализовать тоже самое, но цифру брать, например, из тега <span class="num">456</span> (<span class="result"></span>)Т.е. Значение в спан с классом num вставляется из базы, и рядом выводится прописью.
За ранее спасибо.
что значат эти строки
string = (string == null) ? "" : string;
length = (length == null) ? 1 : length;
Так, с keyup разобались.
Проблема: скрипт выводит undefined при 20000, 30000 и так далее:
двадцать undefined тысяч сто один рубль 00 копеек
Поправил ошибку, смотрите обновленный пример
Только что написал сам то же самое, хотел отписать сюда ))
(i3 > 0 ? def_translite[ax][i3-1] : '')); // 20-99
Теперь код могу признать рабочим и готовым к внедрению
Как сделать, чтобы функция работала на событии keyup, не когда фокус убран с поля?
Самый короткий скрипт всех времен и народов для суммы прописью на русском:
С комментариями!
function sprop(res)
{
// На входе в переменной res должна находиться строка в формате "12345.67" (пример: 18102412990.42)
var h000=[" миллиард", "", "а", "ов", " миллион", "", "а", "ов", " тысяч", "а", "и", "", " рубл", "ь", "я", "ей"];
// Первым идет корень слова, затем окончание для цифры 1, затем для цифер 2-4, затем для всего больше 4 и нуля
var h100=["", "сто", "двести", "триста", "четыреста", "пятьсот", "шестьсот", "семьсот", "восемьсот", "девятьсот"];
var h010=["", "", "двадцать", "тридцать", "сорок", "пятьдесят", "шестьдесят", "семьдесят", "восемьдесят", "девяносто"];
var h011=["", "один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять", "десять", "одиннадцать", "двенадцать", "тринадцать", "четырнадцать", "пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать"];
var pattern="000000000000";
var result=" "+res.substr(res.length-2)+" коп.";// Копируем хвост с копейками в результат "как есть"
res=res.slice(0,res.length-3); // Выкидываем дробную часть с точкой из исходника
res=pattern.substr(res.length-12)+res; // Добавляем в исходник лидирующие нули из pattern
raw = res.split(''); // Разбиваем строку на подстроки, в каждой из которых пока по одной цифре
var p=0, i=0, k=0, t=0; // Переменные
for (var group=0; group<4; group++) { // Цикл по группам из трех цифр - триадам (12 знаков pattern = 4 триады)
p=group*3; // Индекс нулевого элемента группы относительно начала массива RAW
t=+raw[p+2]; // В raw[p] у нас сотни, p+1 - десятки, p+2 - единицы очередной нашей группы
if(+raw[p+1]==1){t=0} // Высчитываем окончание подписи (-а, -ов, -ей...), если десятки=1 то на единицы даже не смотрим
if(t>1&&t<5){t=2} // Если единицы = 2,3,4, то добавка к индексу базы подписей +2 (м..ОВ, рублЕЙ)
if(t>4||t==0){t=3} // Если 0 или 5+ то добавка 3 (единица так и остается добавкой 1)
i=+raw[p]; // Собственно сама цифра (начнем с разряда сотен)
raw[p]=h100[i]; // Заменили сотни (разряд сотен - тупо подстановка)
i=+raw[p+1]; // Разряд десятков: если >=20 - тоже заменяем
if (i>1){raw[p+1]=h010[i]; i=0} // Заменили десятки (двадцать и больше)
k=i*10+(+raw[p+2]); // Считаем индекс K (для массива h011)
if(!isNaN(raw[p+1])){raw[p+1]=""} // Пустоту в десятки если прошлой операцией не заменили на строку
raw[p+2]=h011[k]; // Заменили 0, 1... 9, 10, 11, 12 ... 19
if(group==2&&k==1){raw[p+2]="одна"} // В разряде тысяч один->одна)
if(group==2&&k==2){raw[p+2]="две"} // В разряде тысяч два->две)
if(group==3||(raw[p]+raw[p+1]+raw[p+2]).length>0){ // Если подпись вообще нужна, то... (нужна если "рублей" или непустые триады)
raw[p+2]+=h000[group*4]; // Добавляем подпись к единицам (корень)
raw[p+2]+=h000[group*4+t];} // Добавляем подпись к единицам (окончание)
}
res = raw.join(' ')+result; // Склеиваем строку обратно через пробел и пришиваем обратно дробную часть
res = res.replace(/ +/g," ").trim(); // Убираем двойные пробелы, если вдруг появились, плюс лидирующие и замыкающие
res = res.charAt(0).toUpperCase()+res.slice(1); // Первую букву заглавной
return res; // Для нашего примера: Восемнадцать миллиардов сто два миллиона четыреста двенадцать тысяч девятьсот девяносто рублей 42 коп.
}
Спасибо огромное!!!
Но:
var result=" "+res.substr(res.length-2)+" коп.";// Копируем хвост с копейками в результат "как есть"
Пишет ошибку:
Uncaught TypeError: Cannot read property 'substr' of undefined