Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 23.10.2011, 15:42
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 06.04.2009
Сообщений: 2,379

Скрипт для создания словесной формы
Набор классов для преобразования всяких циферок во что-то вроде:
Цитата:
> 56 миллиардов 402 миллиона 250 тысяч
> 500 рублей 51 копейка
> много денег
> 1 год 2 месяца 5 дней 20 часов 21 минута 44 секунды
> вчера
и т. д.

В ОО вид переделывал второпях, так что возможно, где то криво.
Сперва класс для наследования от него, сам по себе он бесполезен:
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));// пару секунд

Последний раз редактировалось Riim, 23.10.2011 в 16:55.
Ответить с цитированием
  #2 (permalink)  
Старый 24.10.2011, 15:55
Аватар для B~Vladi
Модератор Всея Форума
Отправить личное сообщение для B~Vladi Посмотреть профиль Найти все сообщения от B~Vladi
 
Регистрация: 14.05.2009
Сообщений: 4,021

В закладки, скоро пригодится. Самому лень писать
А для физических величин (1 кг, 200г) как будет выглядеть?
__________________
Болтовня ничего не стоит. Покажите мне код. — Linus Torvalds
влад.куркин.рф
Ответить с цитированием
  #3 (permalink)  
Старый 24.10.2011, 17:34
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 06.04.2009
Сообщений: 2,379

Сообщение от B~Vladi
А для физических величин (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 миллиграмм
Ответить с цитированием
  #4 (permalink)  
Старый 29.10.2011, 14:15
Аспирант
Отправить личное сообщение для stopkran Посмотреть профиль Найти все сообщения от stopkran
 
Регистрация: 12.12.2009
Сообщений: 54

Я так предпочитаю работать с цифрами, а не с числами :-). Как-то так:

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;
Ответить с цитированием
  #5 (permalink)  
Старый 30.10.2011, 04:04
Аватар для x-yuri
Отправить личное сообщение для x-yuri Посмотреть профиль Найти все сообщения от x-yuri
 
Регистрация: 27.12.2008
Сообщений: 4,201

Сообщение от stopkran
return mod100 >= 5 && mod100 <= 20 ? 2 : mod10 == 1 ? 0 : mod10 >= 2 && mod10 <= 4 ? 1 : 2;
вообще это очень напоминает вот это. Только не понятно, почему отличается. Это какие-то оптимизации, Riim?
Ответить с цитированием
  #6 (permalink)  
Старый 30.10.2011, 16:04
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 06.04.2009
Сообщений: 2,379

Сообщение от x-yuri
Это какие-то оптимизации, Riim?
я просто составил таблицу окончаний и записал условие по ней, так как мне было читабельней.

По таблице получается, что в Русском языке есть исключение в диапазоне 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;
Ответить с цитированием
  #7 (permalink)  
Старый 31.10.2011, 05:01
Профессор
Отправить личное сообщение для with-love-from-siberia Посмотреть профиль Найти все сообщения от with-love-from-siberia
 
Регистрация: 14.12.2009
Сообщений: 155

25 строка в первом сообщении
!--partsCount || !i ? 'round' : 'floor'


Может быть ее лучше заменить на обратную
--partsCount && i ? 'floor' : 'round'

для упрощения и улучшения читабельности?
Ответить с цитированием
  #8 (permalink)  
Старый 31.10.2011, 07:35
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 06.04.2009
Сообщений: 2,379

Сообщение от with-love-from-siberia
Может быть ее лучше заменить на обратную
да, так лучше.
Ответить с цитированием
  #9 (permalink)  
Старый 31.10.2011, 08:13
Профессор
Отправить личное сообщение для with-love-from-siberia Посмотреть профиль Найти все сообщения от with-love-from-siberia
 
Регистрация: 14.12.2009
Сообщений: 155

Хм...
Код:
var diff = new Diff(5.2);
// 5 единиц
Ответить с цитированием
  #10 (permalink)  
Старый 31.10.2011, 10:23
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 06.04.2009
Сообщений: 2,379

with-love-from-siberia, допили для не целых чисел .
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Подскажите скрипт для загрузки фото на сервер Mapshal Общие вопросы Javascript 0 13.08.2011 17:49
в модальном окне не работает скрипт проверки формы necroms jQuery 1 11.03.2011 15:14
Скрипт — заметки для сайтов https Работа 4 05.12.2010 12:34
Скрипт для плагина к Download Master ponand Общие вопросы Javascript 21 05.01.2009 22:12
Либа для создания окон Кирпич Библиотеки/Тулкиты/Фреймворки 8 25.06.2008 16:44