Скрипт для создания словесной формы
Набор классов для преобразования всяких циферок во что-то вроде:
Цитата:
В ОО вид переделывал второпях, так что возможно, где то криво. Сперва класс для наследования от него, сам по себе он бесполезен: function AbstractInterval() { throw new TypeError(); } AbstractInterval.prototype = { initialize: function(start, end) { this.interval = arguments.length > 1 ? Math.abs(end - start) : +start; if (!(this instanceof AbstractInterval) || isNaN(this.interval)) { throw new TypeError(); } }, formatPart: function(num, index) { return num + ' ' + this.verbalStages[index][AbstractInterval.pluralIndex(num)]; }, toVerbal: function(partsCount) { var interval = this.interval, stages = this.stages, verbalStages = this.verbalStages; if (isNaN(partsCount)) { partsCount = stages.length; } var parts = [], i = stages.length; while (i) { if (interval >= stages[--i]) { var num = Math[!--partsCount || !i ? 'round' : 'floor'](interval / stages[i]); parts.push(this.formatPart(num, i)); if (!partsCount || !(interval -= stages[i] * num)) { break; } } } return parts; }, toVerbalString: function(partsCount, partsDelim) { return this.toVerbal(partsCount).join(partsDelim == null ? ' ' : partsDelim); } }; AbstractInterval.pluralIndex = function(num) { var mod10 = num % 10, mod100 = num % 100; return mod100 >= 5 && mod100 <= 20 ? 2 : mod10 == 1 ? 0 : mod10 >= 2 && mod10 <= 4 ? 1 : 2; }; // для старых браузеров if (!('create' in Object)) { Object.create = function(obj) { function F() {} F.prototype = obj; return new F(); }; } Теперь создаем всякие полезные классы. Для просто чисел: function Diff() { this.initialize.apply(this, arguments); } // наследуем от AbstractInterval Diff.prototype = Object.create(AbstractInterval.prototype); Diff.prototype.stages = [1, 10, 100, 1000, 1e6, 1e9]; Diff.prototype.verbalStages = [ ['единица', 'единицы', 'единиц'], ['десяток', 'десятка', 'десятков'], ['сотня', 'сотни', 'сотен'], ['тысяча', 'тысячи', 'тысяч'], ['миллион', 'миллиона', 'миллионов'], ['миллиард', 'миллиарда', 'миллиардов'] ]; var diff = new Diff(56402250000); console.log(diff.toVerbalString());// 56 миллиардов 402 миллиона 250 тысяч Для дат: function DateDiff() { this.initialize.apply(this, arguments); } DateDiff.prototype = Object.create(AbstractInterval.prototype); DateDiff.prototype.stages = [1, 1000, 60000, 36e5, 864e5, 6048e5, 2592e6, 31536e6]; DateDiff.prototype.verbalStages = [ ['миллисекунда', 'миллисекунды', 'миллисекунд'], ['секунда', 'секунды', 'секунд'], ['минута', 'минуты', 'минут'], ['час', 'часа', 'часов'], ['день', 'дня', 'дней'], ['неделя', 'недели', 'недель'], ['месяц', 'месяца', 'месяцев'], ['год', 'года', 'лет'] ]; // посмотрим сколько осталось до конца света :) var dd = new DateDiff(new Date(), new Date('2012/12/21')); console.log('Мы все умрем через ' + dd.toVerbalString());// Мы все умрем через 1 год 1 месяц 4 недели 1 день 4 часа 22 минуты 26 секунд 450 миллисекунд Можно указать сколько максимум частей выводить: var dd = new DateDiff(new Date(), new Date('2012/12/21')); console.log(dd.toVerbalString(2));// 1 год 2 месяца 1 месяц превратился в 2, потому что скрипт не просто отрезает лишние части, а смотрит, что в них, и правильно округляет. Можно указать свой разделитель частей (через запятую): var dd = new DateDiff(new Date(), new Date('2012/12/21')); console.log(dd.toVerbalString(5, ', '));// 1 год, 1 месяц, 4 недели, 1 день, 4 часа Можно изменять обработку частей: DateDiff.prototype.formatPart = function(num, index) { if (num == 2) { return 'пару ' + this.verbalStages[index][2]; } return num + ' ' + this.verbalStages[index][AbstractInterval.pluralIndex(num)]; }; var dd = new DateDiff(2000); console.log(dd.toVerbalString(1));// пару секунд |
В закладки, скоро пригодится. Самому лень писать :)
А для физических величин (1 кг, 200г) как будет выглядеть? |
Цитата:
function MassDiff() { this.initialize.apply(this, arguments); } MassDiff.prototype = Object.create(AbstractInterval.prototype); MassDiff.prototype.stages = [1, 1000, 1e6, 1e8, 1e9]; MassDiff.prototype.verbalStages = [ ['миллиграмм', 'миллиграмма', 'миллиграмм'], ['грамм', 'грамма', 'грамм'], ['килограмм', 'килограмма', 'килограмм'], ['центнер', 'центнера', 'центнеров'], ['тонна', 'тонны', 'тонн'] ]; var md = new MassDiff(55670034040/* в миллиграммах */); console.log(md.toVerbalString());// 55 тонн 6 центнеров 70 килограмм 34 грамма 40 миллиграмм |
Я так предпочитаю работать с цифрами, а не с числами :-). Как-то так:
function digit_form (num, words) { num = (num < 10 ? '0' : '') + num; var forms = [2, 0, 1, 1, 1, 2, 2, 2, 2, 2], dig = num.split(''); dig.reverse(); return num + ' ' + (1 == dig[1] ? words[2] : words[forms[Number(dig[0])]]); } alert(digit_form(Math.round(10 / Math.random()), ['минута', 'минуты', 'минут'])); Так, может, медленнее и неправильнее, но зато код проще. А то голову сломал, разбирая: return mod100 >= 5 && mod100 <= 20 ? 2 : mod10 == 1 ? 0 : mod10 >= 2 && mod10 <= 4 ? 1 : 2; |
Цитата:
|
Цитата:
По таблице получается, что в Русском языке есть исключение в диапазоне 11-14 включительно. Если не считать этого исключения, то запись такая: mod10 == 1 ? 0 : mod10 >= 2 && mod10 <= 4 ? 1 : 2; в ней два условия, в том варианте, что по ссылке, исключение учитывается в обоих условиях, я же просто вынес его в отдельное условие и захватил в нем ближайшие цифры (какие можно было). Вот так, наверное, проще сравнивать будет: // без исключения mod10 == 1 ? 0 : mod10 >= 2 && mod10 <= 4 ? 1 : 2; // мой вариант mod100 >= 5 && mod100 <= 20 ? 2 : mod10 == 1 ? 0 : mod10 >= 2 && mod10 <= 4 ? 1 : 2; // вариант по ссылке mod10 == 1 && mod100 != 11 ? 0 : mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20) ? 1 : 2; |
25 строка в первом сообщении
!--partsCount || !i ? 'round' : 'floor' Может быть ее лучше заменить на обратную --partsCount && i ? 'floor' : 'round' для упрощения и улучшения читабельности? |
Цитата:
|
Хм...
Код:
var diff = new Diff(5.2); |
with-love-from-siberia, допили для не целых чисел ;) .
|
Часовой пояс GMT +3, время: 07:37. |