diff --git a/README-english.md b/README-english.md
index c181491..1e674b0 100644
--- a/README-english.md
+++ b/README-english.md
@@ -89,33 +89,31 @@ numberToWordsRu.convert('-4201512.21', {
## Methods
-- [convert(number[, options])](#methods-convert)
+- convert(number, [options])
------------------------
-**
`convert(number[, options])`
**
+```
+convert(number, [options])
+```
-*Convert number to words.*
+Convert number to words.
-Return value is *String*.
+#### **Arguments**
-
+`number (string|number)`: Number to convert to words.
-`number` {String | Number}
+>If typed as *number* max value is **9'007'199'254'740'991** (limit of Javascript).
-*Number to convert to words.*
+>If typed as *string* max value is 10305 (**306 digits**) before point and 10304 (**305 digits**) after point.
-If typed as *Number* max value is **9'007'199'254'740'991** (limit of Javascript).
+`[options] (Object)`: Convert options.
-If typed as *String* max value is **10305** (306 digits) before point and **10304** (305 digits) after point.
+#### **Returns**
-
+`(string)`: Returns converted to text number.
-`options` {Object}
-
-*Convert options.*
-
-**Default options**:
+**Default *options* object**:
```js
{
@@ -138,15 +136,21 @@ If typed as *String* max value is **10305** (306 digits) before point
```
------------------------
-### **Options object**
+### **Argument `options`**
+
+```
+currency: (string|Object)
+```
-`currency`: {String | Object}
+Currency of number.
-*Select currency.*
+#### **Default value**
-**Default**: `'rub'`
+'rub'
-String values:
+#### **Possible values**
+
+- String values:
| String value | Description | Example |
| ------------- | ------------- | ------------- |
@@ -157,7 +161,7 @@ String values:
-Own currency **object** example:
+- Own currency:
```js
{
@@ -179,21 +183,29 @@ Own currency **object** example:
}
```
+**Note**: If currency object will not be filled completely then missing data will be taken from default currency (`'rub'`).
+
-`roundNumber`: {Number}
+```
+roundNumber: (number)
+```
+
+Round number to specified precision.
-*Round number to specified precision.*
+#### **Default value**
-**Default**: `-1`
+-1
-`{Number}` - Precision.
+#### **Possible values**
-`-1` - Disable rounding.
+- `(number)` - Precision. Integer.
-If option `currency` is a common currency (`rub` / `usd` / `eur`) then after rounding it will be rounded again to 2 digits. Also in this case the result always will have 2 digits in fractional part (for example "00", "05").
+- `-1` - Disable rounding.
-Example:
+**Note**: If option `currency` is a common currency (`rub` / `usd` / `eur`) then after rounding it will be rounded again to 2 digits. Also in this case the result always will have 2 digits in fractional part (for example "00", "05").
+
+#### Example
```js
numberToWordsRu.convert('129.6789', {
@@ -215,32 +227,38 @@ numberToWordsRu.convert('129.6789', {
// Сто тридцать рублей 00 копеек
```
-If delimiter is slash ("`/`") then number will NOT be rounded in any case.
+**Note**: If delimiter is slash ("`/`") then number will NOT be rounded in any case.
-`convertMinusSignToWord`: {Boolean}
+```
+convertMinusSignToWord: (Boolean)
+```
+
+Convert minus sign to word ( '-' --> 'минус' ).
-*Convert minus sign to word ('-' => 'минус').*
+#### **Default value**
-**Default**: `true`
+true
-`showNumberParts`: {Object}
+```
+showNumberParts: (Object)
+```
-*Show parts of number.*
+Show parts of number.
-**Default object**:
+#### **Default value**
```js
-showNumberParts: {
+{
integer: true,
fractional: true
}
```
-Example:
+#### Example
```js
numberToWordsRu.convert('123.45', {
@@ -262,20 +280,22 @@ numberToWordsRu.convert('123.45', {
-`convertNumbertToWords`: {Object}
+```
+convertNumbertToWords: (Object)
+```
-*Convert to words parts of number.*
+Convert to words parts of number.
-**Default object**:
+#### **Default value**
```js
-convertNumbertToWords: {
+{
integer: true,
fractional: false
}
```
-Example:
+#### Example
```js
numberToWordsRu.convert('123.45', {
@@ -297,20 +317,22 @@ numberToWordsRu.convert('123.45', {
-`showCurrency`: {Object}
+```
+showCurrency: (Object)
+```
-*Show currency in parts of number.*
+Show currency in parts of number.
-**Default object**:
+#### **Default value**
```js
-showCurrency: {
+{
integer: true,
fractional: true
}
```
-Example:
+#### Example
```js
numberToWordsRu.convert('123.45', {
diff --git a/README.md b/README.md
index 9249363..e7d4d89 100644
--- a/README.md
+++ b/README.md
@@ -89,33 +89,31 @@ numberToWordsRu.convert('-4201512.21', {
## Методы
-- [convert(number[, options])](#methods-convert)
+- convert(number, [options])
------------------------
-**`convert(number[, options])`
**
+```
+convert(number, [options])
+```
-*Конвертировать число в слова.*
+Конвертировать число в слова.
-Тип возвращаемых данных: *String*.
+#### **Аргументы метода**
-
+`number (string|number)`: Число, которое нужно конвертировать.
-`number`: {String | Number}
+>Если введенное число типа *number*, то максимальное значение **9'007'199'254'740'991** (ограничение Javascript).
-*Число, которое нужно конвертировать.*
+>Если введенное число типа *string*, то максимальное значение 10305 (**306 цифр**) до запятой и 10304 (**305 цифр**) после запятой.
-Если введенное число типа *Number*, то максимальное значение **9'007'199'254'740'991** (ограничение Javascript).
+`[options] (Object)`: Опции конвертирования числа.
-Если введенное число типа *String*, то максимальное значение **10305** (306 цифр) до запятой и **10304** (305 цифр) после запятой.
+#### **Возвращаемое значение**
-
+`(string)`: Возвращает конвертированное в текст число.
-`options`: {Object}
-
-*Опции конвертирования числа.*
-
-**Опций по умолчанию**:
+**Объект *options* по умолчанию**:
```js
{
@@ -138,15 +136,21 @@ numberToWordsRu.convert('-4201512.21', {
```
------------------------
-### **Объект опций**
+### **Аргумент `options`**
+
+```
+currency: (string|Object)
+```
-`currency`: {String | Object}
+Валюта числа.
-*Валюта числа.*
+#### **Значение по умолчанию**
-**По умолчанию**: `'rub'`
+'rub'
-Строковые значения:
+#### **Возможные значения**
+
+- Строковые значения:
| Строковое значение | Описание | Пример |
| ------------- | ------------- | ------------- |
@@ -157,7 +161,7 @@ numberToWordsRu.convert('-4201512.21', {
-Можно задать свою валюту объектом:
+- Своя валюта:
```js
{
@@ -178,21 +182,30 @@ numberToWordsRu.convert('-4201512.21', {
}
}
```
+
+**Примечание**: Если объект валюты заполнить не полностью, то недостающие данные будут взяты из объекта валюты по умолчанию (`'rub'`).
+
-`roundNumber`: {Number}
+```
+roundNumber: (number)
+```
+
+Округлить число до заданной точности.
+
+#### **Значение по умолчанию**
-*Округлить число до заданной точности.*
+-1
-**По умолчанию**: `-1`
+#### **Возможные значения**
-`{Number}` - Количество знаков после запятой, до которой нужно округлить число.
+- `(number)` - Целое число. Количество знаков после запятой, до которой нужно округлить число.
-`-1` - Отключить округление.
+- `-1` - Отключить округление.
-Если опция `currency` является стандартной валютой (`rub` / `usd` / `eur`), то даже после округления число будет еще раз округлено до 2 знаков после запятой. Также в этом случае у результата в дробной части всегда будет 2 знака (например "00", "05").
+**Примечание**: Если опция `currency` является стандартной валютой (`'rub'` / `'usd'` / `'eur'`), то даже после округления число будет еще раз округлено до 2 знаков после запятой. Также в этом случае у результата в дробной части всегда будет 2 знака (например "00", "05").
-Например:
+#### Пример
```js
numberToWordsRu.convert('129.6789', {
@@ -214,32 +227,38 @@ numberToWordsRu.convert('129.6789', {
// Сто тридцать рублей 00 копеек
```
-Если разделитель числа является дробной чертой ("`/`"), то число НЕ будет округлено в любом случае.
+**Примечание**: Если разделитель числа является дробной чертой ("`/`"), то число НЕ будет округлено в любом случае.
-`convertMinusSignToWord`: {Boolean}
+```
+convertMinusSignToWord: (Boolean)
+```
-*Конвертировать знак минус в слово ('-' => 'минус').*
+Конвертировать знак минус в слово ( '-' --> 'минус' ).
-**По умолчанию**: `true`
+#### **Значение по умолчанию**
+
+true
-`showNumberParts`: {Object}
+```
+showNumberParts: (Object)
+```
-*Отображать часть числа.*
+Отображать указанные части числа.
-**Объект по умолчанию**:
+#### **Значение по умолчанию**
```js
-showNumberParts: {
+{
integer: true, // Целая часть числа
fractional: true // Дробная часть числа
}
```
-Например:
+#### Пример
```js
numberToWordsRu.convert('123.45', {
@@ -261,20 +280,22 @@ numberToWordsRu.convert('123.45', {
-`convertNumbertToWords`: {Object}
+```
+convertNumbertToWords: (Object)
+```
-*Конвертировать в слова указанные части числа .*
+Конвертировать в слова указанные части числа .
-**Объект по умолчанию**:
+#### **Значение по умолчанию**
```js
-convertNumbertToWords: {
+{
integer: true, // Целая часть числа
fractional: false // Дробная часть числа
}
```
-Например:
+#### Пример
```js
numberToWordsRu.convert('123.45', {
@@ -296,20 +317,22 @@ numberToWordsRu.convert('123.45', {
-`showCurrency`: {Object}
+```
+showCurrency: (Object)
+```
-*Отображать валюту в указанных частях числа.*
+Отображать валюту в указанных частях числа.
-**Объект по умолчанию**:
+#### **Значение по умолчанию**
```js
-showCurrency: {
+{
integer: true, // Целая часть числа
fractional: true // Дробная часть числа
}
```
-Например:
+#### Пример
```js
numberToWordsRu.convert('123.45', {
diff --git a/package.json b/package.json
index e1dd5de..1db92bb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "number-to-words-ru",
- "version": "2.1.1",
+ "version": "2.1.2",
"description": "Convert a number to words on russian language.",
"license": "MIT",
"repository": "Ant1mas/number-to-words-ru",
@@ -20,6 +20,7 @@
"eslint-config-google": "^0.14.0",
"html-webpack-plugin": "^4.3.0",
"jest": "^26.0.1",
+ "lodash": "^4.17.15",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.9.0",
diff --git a/src/combineResultData.js b/src/combineResultData.js
index 0819ee3..22041d1 100644
--- a/src/combineResultData.js
+++ b/src/combineResultData.js
@@ -104,6 +104,20 @@ const combineResultData = (numberArray, options) => {
currencyObject.currencyNounGender.fractionalPart,
).result;
}
+ } else {
+ // Если не нужно конвертировать в слова
+ // Если валюта "number"
+ if (useOptions.currency === 'number') {
+ // Если в дробной части есть цифры
+ if (convertedNumberArr[3].length > 0) {
+ // Удалить лишние нули перед числом
+ convertedNumberArr[3] = convertedNumberArr[3].replace(/^0+/, '');
+ // Если после удаления лишних нулей не осталось цифр, то добавить один "0"
+ if (convertedNumberArr[3] === '') {
+ convertedNumberArr[3] = '0';
+ }
+ }
+ }
}
// Если нужно отображать валюту числа
if (useOptions.showCurrency.fractional === true) {
diff --git a/src/defaultOptions.js b/src/defaultOptions.js
new file mode 100644
index 0000000..9ed473c
--- /dev/null
+++ b/src/defaultOptions.js
@@ -0,0 +1,22 @@
+const defaultOptions = {
+ /* currency - Название валюты ('rub', 'usd', 'eur')
+ или 'number'
+ или объект со своей валютой */
+ currency: 'rub',
+ roundNumber: -1,
+ convertMinusSignToWord: true,
+ showNumberParts: {
+ integer: true,
+ fractional: true,
+ },
+ convertNumbertToWords: {
+ integer: true,
+ fractional: false,
+ },
+ showCurrency: {
+ integer: true,
+ fractional: true,
+ },
+};
+
+export default defaultOptions;
diff --git a/src/getCurrencyObject.js b/src/getCurrencyObject.js
index e3a112a..1075dcd 100644
--- a/src/getCurrencyObject.js
+++ b/src/getCurrencyObject.js
@@ -1,4 +1,7 @@
import textValues from 'textValues';
+import defaultOptions from 'defaultOptions';
+import stringCurrencies from 'stringCurrencies';
+import updateObjectDeep from 'updateObjectDeep';
/**
* Получить объект с данными валюты.
@@ -31,19 +34,23 @@ const getCurrencyObject = (convertOptions) => {
}
// Если валюта описана объектом
} else if (typeof convertOptions.currency === 'object') {
+ // Объект валюты по умолчанию
+ const defaultCurrencyObject = stringCurrencies[defaultOptions['currency']];
+ // Обновить объект валюты новым объектом валюты
+ const updatedCurrencyObject = updateObjectDeep(defaultCurrencyObject, convertOptions.currency);
// Если объект оформлен правильно
if (
- typeof convertOptions.currency === 'object' &&
- Object.keys(convertOptions.currency).length === 3 &&
- convertOptions.currency.currencyNameCases.length === 3 &&
- convertOptions.currency.fractionalPartNameCases.length === 3 &&
- typeof convertOptions.currency.currencyNounGender === 'object' &&
- Object.keys(convertOptions.currency.currencyNounGender).length === 2 &&
- typeof convertOptions.currency.currencyNounGender.integer === 'number' &&
- typeof convertOptions.currency.currencyNounGender.fractionalPart === 'number'
+ typeof updatedCurrencyObject === 'object' &&
+ Object.keys(updatedCurrencyObject).length === 3 &&
+ updatedCurrencyObject.currencyNameCases.length === 3 &&
+ updatedCurrencyObject.fractionalPartNameCases.length === 3 &&
+ typeof updatedCurrencyObject.currencyNounGender === 'object' &&
+ Object.keys(updatedCurrencyObject.currencyNounGender).length === 2 &&
+ typeof updatedCurrencyObject.currencyNounGender.integer === 'number' &&
+ typeof updatedCurrencyObject.currencyNounGender.fractionalPart === 'number'
) {
// Принять валюту
- currencyObject = convertOptions.currency;
+ currencyObject = updatedCurrencyObject;
} else {
// Если объект оформлен неправильно
throw new Error(`Wrong currency object.`);
diff --git a/src/getOptions.js b/src/getOptions.js
index 4b4d26c..8b457e0 100644
--- a/src/getOptions.js
+++ b/src/getOptions.js
@@ -1,3 +1,6 @@
+import _ from 'lodash';
+import defaultOptions from 'defaultOptions';
+
/**
* Получить опции конверирования.
* @param {Object} options - Опции, выбранные пользователем.
@@ -5,26 +8,7 @@
*/
const getOptions = (options) => {
// Опции по умолчанию
- const resultOptions = {
- /* currency - Название валюты ('rub', 'usd', 'eur')
- или 'number'
- или объект со своей валютой */
- currency: 'rub',
- roundNumber: -1,
- convertMinusSignToWord: true,
- showNumberParts: {
- integer: true,
- fractional: true,
- },
- convertNumbertToWords: {
- integer: true,
- fractional: false,
- },
- showCurrency: {
- integer: true,
- fractional: true,
- },
- };
+ const resultOptions = _.cloneDeep(defaultOptions);
// Заменить опции по умолчанию выбранными опциями, если они правильно указаны
const updateOptions = (currentOptions, newOptions) => {
Object.keys(currentOptions).forEach((key) => {
diff --git a/src/index.js b/src/index.js
index 5b3a090..a40f0b1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,3 +1,4 @@
+import 'lodashMixins/index';
import methodConvert from 'methods/convert';
const numberToWordsRu = {
diff --git a/src/lodashMixins/deepMapValues.js b/src/lodashMixins/deepMapValues.js
new file mode 100644
index 0000000..220df25
--- /dev/null
+++ b/src/lodashMixins/deepMapValues.js
@@ -0,0 +1,18 @@
+const deepMapValues = {
+ 'deepMapValues': (object, iteratee) => {
+ const result = _.cloneDeep(object);
+ const iterateObject = (object, path = []) => {
+ _.forOwn(object, (value, key) => {
+ if (_.isPlainObject(value)) {
+ value = iterateObject(value, [...path, key]);
+ } else {
+ _.set(result, [...path, key], iteratee(path, key, value));
+ }
+ });
+ };
+ iterateObject(object);
+ return result;
+ },
+};
+
+export default deepMapValues;
diff --git a/src/lodashMixins/index.js b/src/lodashMixins/index.js
new file mode 100644
index 0000000..4cc80cf
--- /dev/null
+++ b/src/lodashMixins/index.js
@@ -0,0 +1,6 @@
+import _ from 'lodash';
+import deepMapValues from 'lodashMixins/deepMapValues';
+
+_.mixin({
+ ...deepMapValues,
+});
diff --git a/src/roundNumber.js b/src/roundNumber.js
index a8e9a3c..fc58837 100644
--- a/src/roundNumber.js
+++ b/src/roundNumber.js
@@ -61,6 +61,22 @@ const roundNumber = (numberArray, precision = 2) => {
const resultNumberArray = [...numberArray];
resultNumberArray[1] = numberPartToRound.slice(0, -1).split('.')[0];
resultNumberArray[3] = numberPartToRound.slice(0, -1).split('.')[1];
+ // Убрать лишние нули из дробной части справа
+ resultNumberArray[3] = resultNumberArray[3]
+ .split('')
+ .reverse()
+ .join('')
+ .replace(/^0+/, '')
+ .split('')
+ .reverse()
+ .join('');
+ // Если дробная часть пустая, то сделать равной 0
+ if (
+ resultNumberArray[3] === '' &&
+ precision > 0
+ ) {
+ resultNumberArray[3] = '0';
+ }
return resultNumberArray;
};
diff --git a/src/updateObjectDeep.js b/src/updateObjectDeep.js
new file mode 100644
index 0000000..0183817
--- /dev/null
+++ b/src/updateObjectDeep.js
@@ -0,0 +1,23 @@
+import _ from 'lodash';
+
+/**
+ * Рекурсивное обновление значений в объекте.
+ * Новые поля не добавляются. Значение обновляется, только если тип полей одинаковый.
+ * @param {Object} object - Исходный объект.
+ * @param {Object} newObject - Новый объект.
+ * @return {Object} Обновленный объект.
+ */
+const updateObjectDeep = (object, newObject) => {
+ return _.deepMapValues(object, (path, key, value) => {
+ // Если тип данных одинаковый
+ if (Object.prototype.toString.call(_.get(newObject, [...path, key])) === Object.prototype.toString.call(value)) {
+ // Заменить новым значением
+ return _.get(newObject, [...path, key]);
+ } else {
+ // Оставить старое значение
+ return value;
+ }
+ });
+};
+
+export default updateObjectDeep;
diff --git a/test/index.test.js b/test/index.test.js
index 4904fdf..9c2a86d 100644
--- a/test/index.test.js
+++ b/test/index.test.js
@@ -385,28 +385,43 @@ describe('Options', () => {
});
});
describe('object values', () => {
- test('common value', () => {
- expect(numberToWordsRu.convert('1234567.12345', {
- currency: {
- currencyNameCases: ['доллар', 'доллара', 'долларов'],
- fractionalPartNameCases: ['цент', 'цента', 'центов'],
- currencyNounGender: {
- integer: 0, // Мужской род
- fractionalPart: 1, // Женский род
+ describe('common values', () => {
+ test('full objects', () => {
+ expect(numberToWordsRu.convert('1234567.12345', {
+ currency: {
+ currencyNameCases: ['доллар', 'доллара', 'долларов'],
+ fractionalPartNameCases: ['цент', 'цента', 'центов'],
+ currencyNounGender: {
+ integer: 0, // Мужской род
+ fractionalPart: 1, // Женский род
+ },
},
- },
- })).toBe('Один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь долларов 12345 центов');
- expect(numberToWordsRu.convert('1234567.12345', {
- currency: {
- currencyNameCases: ['доллар', 'доллара', 'долларов'],
- fractionalPartNameCases: ['цент', 'цента', 'центов'],
- currencyNounGender: {
- integer: 0, // Мужской род
- fractionalPart: 1, // Женский род
+ })).toBe('Один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь долларов 12345 центов');
+ expect(numberToWordsRu.convert('1234567.12345', {
+ currency: {
+ currencyNameCases: ['доллар', 'доллара', 'долларов'],
+ fractionalPartNameCases: ['цент', 'цента', 'центов'],
+ currencyNounGender: {
+ integer: 0, // Мужской род
+ fractionalPart: 1, // Женский род
+ },
},
- },
- roundNumber: 2,
- })).toBe('Один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь долларов 12 центов');
+ roundNumber: 2,
+ })).toBe('Один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь долларов 12 центов');
+ });
+ test('not full objects', () => {
+ expect(numberToWordsRu.convert('1234561.12345', {
+ currency: {
+ currencyNameCases: ['сообщение', 'сообщения', 'сообщений'],
+ currencyNounGender: {
+ integer: 2, // Средний род
+ },
+ },
+ })).toBe('Один миллион двести тридцать четыре тысячи пятьсот шестьдесят одно сообщение 12345 копеек');
+ expect(numberToWordsRu.convert('1234567.12345', {
+ currency: {},
+ })).toBe('Один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь рублей 12345 копеек');
+ });
});
test('currencyNounGender == 2', () => {
expect(numberToWordsRu.convert('1231.52', {
@@ -528,6 +543,18 @@ describe('Options', () => {
currency: 'number',
roundNumber: -1,
})).toBe('Одна тысяча двести тридцать четыре целых 6789 десятитысячных');
+ expect(numberToWordsRu.convert('1.9999', {
+ currency: 'number',
+ roundNumber: 3,
+ })).toBe('Две целых 0 десятых');
+ expect(numberToWordsRu.convert('1.9999', {
+ currency: 'number',
+ roundNumber: 0,
+ })).toBe('Две целых');
+ expect(numberToWordsRu.convert('1.00089', {
+ currency: 'number',
+ roundNumber: 5,
+ })).toBe('Одна целая 89 стотысячных');
});
test('custom currency', () => {
expect(numberToWordsRu.convert('1234.6789', {