Skip to content

Commit d3b9892

Browse files
committed
Tidy up functionality
1 parent 38dce7b commit d3b9892

File tree

2 files changed

+282
-274
lines changed

2 files changed

+282
-274
lines changed

index.js

Lines changed: 173 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
root.numbered = factory();
1111
}
1212
})(this, function () {
13-
var numbers = {
13+
var NUMBER_MAP = {
1414
'.': 'point',
1515
'-': 'negative',
1616
0: 'zero',
@@ -44,216 +44,224 @@
4444
};
4545

4646
// http://en.wikipedia.org/wiki/English_numerals#Cardinal_numbers
47-
var helpers = {};
48-
// Store the helpers in the power of tens
49-
helpers[2] = 'hundred';
50-
helpers[3] = 'thousand';
51-
helpers[6] = 'million';
52-
helpers[9] = 'billion';
53-
helpers[12] = 'trillion';
54-
helpers[15] = 'quadrillion';
55-
helpers[18] = 'quintillion';
56-
helpers[21] = 'sextillion';
57-
helpers[24] = 'septillion';
58-
helpers[27] = 'octillion';
59-
helpers[30] = 'nonillion';
60-
helpers[33] = 'decillion';
61-
helpers[36] = 'undecillion';
62-
helpers[39] = 'duodecillion';
63-
helpers[42] = 'tredecillion';
64-
helpers[45] = 'quattuordecillion';
65-
helpers[48] = 'quindecillion';
66-
helpers[51] = 'sexdecillion';
67-
helpers[54] = 'septendecillion';
68-
helpers[57] = 'octodecillion';
69-
helpers[60] = 'novemdecillion';
70-
helpers[63] = 'vigintillion';
71-
helpers[100] = 'googol';
72-
helpers[303] = 'centillion';
73-
74-
// Make a hash of the numbers and helper numbers reversed
75-
// E.g. The key as the word and value as the number
76-
var numbersMap = {};
77-
numbersMap.nil = 0;
78-
numbersMap.naught = 0;
79-
numbersMap.period = '.';
80-
numbersMap.decimal = '.';
81-
82-
Object.keys(numbers).forEach(function (num) {
83-
numbersMap[numbers[num]] = isNaN(+num) ? num : +num;
47+
var CARDINAL_MAP = {
48+
2: 'hundred',
49+
3: 'thousand',
50+
6: 'million',
51+
9: 'billion',
52+
12: 'trillion',
53+
15: 'quadrillion',
54+
18: 'quintillion',
55+
21: 'sextillion',
56+
24: 'septillion',
57+
27: 'octillion',
58+
30: 'nonillion',
59+
33: 'decillion',
60+
36: 'undecillion',
61+
39: 'duodecillion',
62+
42: 'tredecillion',
63+
45: 'quattuordecillion',
64+
48: 'quindecillion',
65+
51: 'sexdecillion',
66+
54: 'septendecillion',
67+
57: 'octodecillion',
68+
60: 'novemdecillion',
69+
63: 'vigintillion',
70+
100: 'googol',
71+
303: 'centillion'
72+
};
73+
74+
// Make a hash of words back to their numeric value.
75+
var WORD_MAP = {
76+
nil: 0,
77+
naught: 0,
78+
period: '.',
79+
decimal: '.'
80+
};
81+
82+
Object.keys(NUMBER_MAP).forEach(function (num) {
83+
WORD_MAP[NUMBER_MAP[num]] = isNaN(+num) ? num : +num;
8484
});
8585

86-
Object.keys(helpers).forEach(function (num) {
87-
numbersMap[helpers[num]] = isNaN(+num) ? num : Math.pow(10, +num);
86+
Object.keys(CARDINAL_MAP).forEach(function (num) {
87+
WORD_MAP[CARDINAL_MAP[num]] = isNaN(+num) ? num : Math.pow(10, +num);
8888
});
8989

9090
/**
91-
* Returns the number of significant figures for the number
91+
* Returns the number of significant figures for the number.
92+
*
9293
* @param {number} num
9394
* @return {number}
9495
*/
95-
var intervals = function (num) {
96-
var match;
97-
if ((match = ('' + num).match(/e\+(\d+)/))) {
98-
return match[1];
99-
}
96+
function intervals (num) {
97+
var match = String(num).match(/e\+(\d+)/);
10098

101-
return ('' + num).length - 1;
102-
};
99+
if (match) return match[1];
100+
101+
return String(num).length - 1;
102+
}
103+
104+
/**
105+
* Calculate the value of the current stack.
106+
*
107+
* @param {Array} stack
108+
* @param {number} largest
109+
*/
110+
function totalStack (stack, largest) {
111+
var total = stack.reduceRight(function (prev, num, index) {
112+
if (num > stack[index + 1]) {
113+
return prev * num;
114+
}
115+
116+
return prev + num;
117+
}, 0);
118+
119+
return total * largest;
120+
}
103121

104122
/**
105-
* Accepts both a string and number type - and return the opposite
123+
* Accepts both a string and number type, and return the opposite.
124+
*
106125
* @param {string|number} num
107126
* @return {string|number}
108127
*/
109-
var numberWords = function (num) {
110-
if (typeof num === 'string') {
111-
return numberWords.parse(num);
112-
}
113-
if (typeof num === 'number') {
114-
return numberWords.stringify(num);
115-
}
116-
throw new Error('Number words can handle handle numbers and/or strings');
117-
};
128+
function numbered (num) {
129+
if (typeof num === 'string') return numbered.parse(num);
130+
if (typeof num === 'number') return numbered.stringify(num);
131+
132+
throw new Error('Numbered can only parse strings or stringify numbers');
133+
}
118134

119135
/**
120-
* Turn a number into a string representation
136+
* Turn a number into a string representation.
137+
*
121138
* @param {number} num
122139
* @return {string}
123140
*/
124-
numberWords.stringify = function (num) {
125-
var word = [],
126-
interval,
127-
remaining;
128-
129-
num = isNaN(+num) ? num : +num;
130-
131-
// Numbers are super buggy in JS over 10^20
132-
if (typeof num !== 'number') { return false; }
133-
// If the number is in the numbers object, we can quickly return
134-
if (numbers[num]) { return numbers[num]; }
135-
// If the number is a negative value
136-
if (num < 0) {
137-
return numbers['-'] + ' ' + numberWords.stringify(num * -1);
138-
}
141+
numbered.stringify = function (value) {
142+
var num = Number(value);
143+
var floor = Math.floor(num);
144+
145+
// If the number is in the numbers object, we quickly return.
146+
if (NUMBER_MAP[num]) return NUMBER_MAP[num];
147+
148+
// If the number is a negative value.
149+
if (num < 0) return NUMBER_MAP['-'] + ' ' + numbered.stringify(-num);
150+
151+
// Check if we have decimals.
152+
if (floor !== num) {
153+
var words = [numbered.stringify(floor), NUMBER_MAP['.']];
154+
var chars = String(num).split('.').pop();
155+
156+
for (var i = 0; i < chars.length; i++) {
157+
words.push(numbered.stringify(+chars[i]));
158+
}
139159

140-
// Check if we have decimals
141-
if (num % 1) {
142-
word.push(numberWords.stringify(Math.floor(num)));
143-
word.push(numbers['.']);
144-
word = word.concat(('' + num).split('.')[1].split('').map(numberWords.stringify));
145-
return word.join(' ');
160+
return words.join(' ');
146161
}
147162

148-
interval = intervals(num);
149-
// It's below one hundred, but greater than nine
163+
var interval = intervals(num);
164+
165+
// It's below one hundred, but greater than nine.
150166
if (interval === 1) {
151-
word.push(numbers[Math.floor(num / 10) * 10] + '-' + numberWords.stringify(Math.floor(num % 10)));
152-
}
153-
// Simple check to find the closest full number helper
154-
while (interval > 3 && !helpers[interval]) {
155-
interval -= 1;
167+
return NUMBER_MAP[Math.floor(num / 10) * 10] + '-' + numbered.stringify(Math.floor(num % 10));
156168
}
157169

158-
if (helpers[interval]) {
159-
remaining = Math.floor(num % Math.pow(10, interval));
160-
word.push(numberWords.stringify(Math.floor(num / Math.pow(10, interval))));
161-
word.push(helpers[interval] + (remaining > 99 ? ',' : ''));
170+
var sentence = [];
171+
172+
// Simple check to find the closest full number helper.
173+
while (!CARDINAL_MAP[interval]) interval -= 1;
174+
175+
if (CARDINAL_MAP[interval]) {
176+
var remaining = Math.floor(num % Math.pow(10, interval));
177+
178+
sentence.push(numbered.stringify(Math.floor(num / Math.pow(10, interval))));
179+
sentence.push(CARDINAL_MAP[interval] + (remaining > 99 ? ',' : ''));
180+
162181
if (remaining) {
163-
if (remaining < 100) { word.push('and'); }
164-
word.push(numberWords.stringify(remaining));
182+
if (remaining < 100) sentence.push('and');
183+
184+
sentence.push(numbered.stringify(remaining));
165185
}
166186
}
167187

168-
return word.join(' ');
188+
return sentence.join(' ');
169189
};
170190

171191
/**
172192
* Turns a string representation of a number into a number type
173193
* @param {string} num
174194
* @return {number}
175195
*/
176-
numberWords.parse = function (num) {
177-
if (typeof num !== 'string') { return false; }
178-
179-
var modifier = 1,
180-
largest = 0,
181-
largestInterval = 0,
182-
zeros = 0, // Keep track of the number of leading zeros in the decimal
183-
stack = [];
184-
185-
var totalStack = function () {
186-
var total = stack.reduceRight(function (memo, num, index, array) {
187-
if (num > array[index + 1]) {
188-
return memo * num;
196+
numbered.parse = function (num) {
197+
var modifier = 1;
198+
var largest = 0;
199+
var largestInterval = 0;
200+
var zeros = 0; // Track leading zeros in a decimal.
201+
var stack = [];
202+
203+
var total = num.split(/\W+/g)
204+
.map(function (word) {
205+
var num = word.toLowerCase();
206+
207+
return WORD_MAP[num] !== undefined ? WORD_MAP[num] : num;
208+
})
209+
.filter(function (num) {
210+
if (num === '-') modifier = -1;
211+
if (num === '.') return true; // Decimal points are a special case.
212+
213+
return typeof num === 'number';
214+
})
215+
.reduceRight(function (memo, num) {
216+
var interval = intervals(num);
217+
218+
// Check the interval is smaller than the largest one, then create a stack.
219+
if (typeof num === 'number' && interval < largestInterval) {
220+
stack.push(num);
221+
if (stack.length === 1) return memo - largest;
222+
return memo;
189223
}
190-
return memo + num;
191-
}, 0);
192224

193-
return total * largest;
194-
};
225+
memo += totalStack(stack, largest);
226+
stack = []; // Reset the stack for more computations.
195227

196-
var total = num.split(/\W+/g).map(function (num) {
197-
num = num.toLowerCase(); // Make life easier
198-
return numbersMap[num] != null ? numbersMap[num] : num;
199-
}).filter(function (num) {
200-
if (num === '-') {
201-
modifier = -1;
202-
}
203-
if (num === '.') {
204-
return true; // Decimal points are a special case
205-
}
206-
return isFinite(num); // Remove numbers we don't understand
207-
}).reduceRight(function (memo, num) {
208-
var interval = intervals(num),
209-
decimals,
210-
output;
211-
212-
// Check the interval is smaller than the largest one, then create a stack
213-
if (typeof num === 'number' && interval < largestInterval) {
214-
if (!stack.length) { memo = memo - largest; }
215-
stack.push(num);
216-
return memo;
217-
}
228+
// If the number is a decimal, transform everything we have worked with.
229+
if (num === '.') {
230+
var decimals = zeros + String(memo).length;
218231

219-
memo = memo + totalStack();
220-
stack = []; // Reset the stack for more computations
221-
222-
// If the number is a decimal, transform everything we were just working with
223-
if (num === '.') {
224-
decimals = zeros + ('' + memo).length;
225-
zeros = 0;
226-
// Reset the largest intervals and stuff
227-
largest = 0;
228-
largestInterval = 0;
229-
return memo * Math.pow(10, decimals * -1);
230-
}
232+
zeros = 0;
233+
largest = 0;
234+
largestInterval = 0;
231235

232-
// Keep a count of zeros we encountered
233-
if (num === 0) {
234-
zeros += 1;
235-
return memo;
236-
}
236+
return memo * Math.pow(10, -decimals);
237+
}
237238

238-
// Shove the number on the front if the intervals match and the number is a whole
239-
if (memo >= 1 && interval === largestInterval) {
240-
output = '' + memo;
241-
// Decrement the zeros count while adding zeros to the front of the number
242-
while (zeros && zeros--) {
243-
output = '0' + output;
239+
// Buffer encountered zeros.
240+
if (num === 0) {
241+
zeros += 1;
242+
return memo;
244243
}
245-
return +(num + output);
246-
}
247244

248-
// Store the largest number for future use
249-
largest = num;
250-
largestInterval = intervals(largest);
245+
// Shove the number on the front if the intervals match and the number whole.
246+
if (memo >= 1 && interval === largestInterval) {
247+
var output = '';
251248

252-
return (memo + num) * Math.pow(10, zeros);
253-
}, 0);
249+
while (zeros > 0) {
250+
zeros -= 1;
251+
output += '0';
252+
}
253+
254+
return Number(String(num) + output + String(memo));
255+
}
256+
257+
largest = num;
258+
largestInterval = intervals(largest);
259+
260+
return (memo + num) * Math.pow(10, zeros);
261+
}, 0);
254262

255-
return modifier * (total + totalStack());
263+
return modifier * (total + totalStack(stack, largest));
256264
};
257265

258-
return numberWords;
266+
return numbered;
259267
});

0 commit comments

Comments
 (0)