name:
src/Utilities/MoreUtils.ts
-rw-r--r--
8616
1/**
2 * If the value is UNDEFINED, return true.
3 * @param value - Value to check if undefined.
4 * @returns {boolean}
5 */
6function isUndefined(value : any) : boolean {
7 return value === undefined;
8}
9
10/**
11 * If the value is DEFINED, return true.
12 * @param value - Value to check if is defined.
13 * @returns {boolean}
14 */
15function isDefined(value : any) : boolean {
16 return value !== undefined;
17}
18
19/**
20 * Returns true if value is an instance of a Array.
21 * @param value
22 * @returns {boolean}
23 */
24function isArray(value) : boolean {
25 return value instanceof Array;
26}
27
28/**
29 * Alphabetical character to number.
30 * @param chr
31 * @returns {number}
32 */
33function characterToNumber(chr) {
34 chr = chr.replace(/\$/g, '');
35 let base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', i, j, result = 0;
36
37 for (i = 0, j = chr.length - 1; i < chr.length; i += 1, j -= 1) {
38 result += Math.pow(base.length, j) * (base.indexOf(chr[i]) + 1);
39 }
40
41 if (result) {
42 --result;
43 }
44
45 return result;
46}
47
48/**
49 * Converts a number to an alphabetical character.
50 * @param num
51 * @returns {string}
52 */
53function numberToCharacter(num) {
54 let s = '';
55
56 while (num >= 0) {
57 s = String.fromCharCode(num % 26 + 97) + s;
58 num = Math.floor(num / 26) - 1;
59 }
60
61 return s.toUpperCase();
62}
63
64/**
65 * Converts a quoted string to an un-quoted string: `"hey"` to `hey`
66 * @param str
67 * @returns {string}
68 */
69function string(str){
70 return str.substring(1, str.length - 1);
71}
72
73
74/**
75 * Converts XY coordinates (eg {0, 0}) to A1 coordinates (eg {A1}).
76 * @param x
77 * @param y
78 * @returns {string}
79 */
80function convertXYtoA1Coordinates(x, y) {
81 function numberToLetters(num) {
82 let mod = num % 26,
83 pow = num / 26 | 0,
84 out = mod ? String.fromCharCode(64 + mod) : (--pow, 'Z');
85 return pow ? numberToLetters(pow) + out : out;
86 }
87 return numberToLetters(x+1) + (y+1).toString();
88}
89
90/**
91 * Returns RowCol coordinates of an A1 cellId
92 * @param cellId
93 * @returns {Object}
94 */
95function A1toRowColCoordinates(cellId) {
96 let num = cellId.match(/\d+$/),
97 alpha = cellId.replace(num, '');
98
99 return {
100 row: parseInt(num[0], 10) - 1,
101 col: characterToNumber(alpha)
102 };
103}
104
105/**
106 * Class for building formatted strings with commas, forced number of leading and trailing zeros, and arbitrary leading
107 * and trailing strings.
108 */
109class NumberStringBuilder {
110 private n : number;
111 private shouldUseComma : boolean = false;
112 private integerZeroCount : number = 1; // e.g. default to "0.1"
113 private decimalZeroCount : number = 0; // e.g. default to "1"
114 private maxDecimalPlaces : number;
115 private headString : string = "";
116 private tailString : string = "";
117
118 /**
119 * Static builder, easier than `new`.
120 * @returns {NumberStringBuilder}
121 */
122 static start() : NumberStringBuilder {
123 return new NumberStringBuilder();
124 }
125
126 /**
127 * Pads a given string with "0" on the right or left side until it is a certain width.
128 * @param {string} str - String to pad.
129 * @param {number} width - Width to pad to. If this is less than the strings length, will do nothing.
130 * @param {string} type - "right" or "left" side to append zeroes.
131 * @returns {string}
132 */
133 private static pad(str : string, width : number, type : string) : string {
134 let z = '0';
135 str = str + '';
136 if (type === "left") {
137 return str.length >= width ? str : new Array(width - str.length + 1).join(z) + str;
138 } else {
139 return str.length >= width ? str : str + (new Array(width - str.length + 1).join(z));
140 }
141 }
142
143 /**
144 * Rounds a number n to a certain number of digits.
145 * @param n - Number to round.
146 * @param digits - Digits to round to.
147 * @returns {number}
148 */
149 private static round(n, digits) {
150 return Math.round(n * Math.pow(10, digits)) / Math.pow(10, digits);
151 }
152
153 /**
154 * Set the number that we'll be formatting.
155 * @param {number} n - Number.
156 * @returns {NumberStringBuilder}
157 */
158 public number(n : number) : NumberStringBuilder {
159 this.n = n;
160 return this;
161 }
162
163 /**
164 * The number of zeros to force on the left side of the decimal.
165 * @param {number} zeros
166 * @returns {NumberStringBuilder}
167 */
168 public integerZeros(zeros : number) : NumberStringBuilder {
169 this.integerZeroCount = zeros;
170 return this;
171 }
172
173 /**
174 * The number of zeros to force on the right side of the decimal.
175 * @param {number} zeros
176 * @returns {NumberStringBuilder}
177 */
178 public decimalZeros(zeros : number) : NumberStringBuilder {
179 this.decimalZeroCount = zeros;
180 return this;
181 }
182
183 /**
184 * If you would like to force the maximum number of decimal places, without padding with zeros, set this.
185 * WARNING: Should not be used in conjunction with decimalZeros().
186 * @param {number} maxDecimalPlaces
187 * @returns {NumberStringBuilder}
188 */
189 public maximumDecimalPlaces(maxDecimalPlaces: number) : NumberStringBuilder {
190 this.maxDecimalPlaces = maxDecimalPlaces;
191 return this;
192 }
193
194 /**
195 * Should digits to the left side of the decimal use comma-notation?
196 * @param {boolean} shouldUseComma
197 * @returns {NumberStringBuilder}
198 */
199 public commafy(shouldUseComma : boolean) : NumberStringBuilder {
200 this.shouldUseComma = shouldUseComma;
201 return this;
202 }
203
204 /**
205 * String to append to the beginning of the final formatted number.
206 * @param {string} head
207 * @returns {NumberStringBuilder}
208 */
209 public head(head : string) : NumberStringBuilder {
210 this.headString = head;
211 return this;
212 }
213
214 /**
215 * String to append to the end of the final formatted number.
216 * @param {string} tail
217 * @returns {NumberStringBuilder}
218 */
219 public tail(tail : string) : NumberStringBuilder {
220 this.tailString = tail;
221 return this;
222 }
223
224 /**
225 * Building the string using the rules set in this builder.
226 * @returns {string}
227 */
228 public build() : string {
229 let nStr = this.n.toString();
230 let isInt = this.n % 1 === 0;
231 let integerPart = isInt ? nStr : nStr.split(".")[0];
232 integerPart = integerPart.replace("-", "");
233 let decimalPart = isInt ? "" : nStr.split(".")[1];
234
235 // Building integer part
236 if (this.integerZeroCount > 1) {
237 integerPart = NumberStringBuilder.pad(integerPart, this.integerZeroCount, "left");
238 }
239
240 // Building decimal part
241 // If the decimal part is greater than the number of zeros we allow, then we have to round the number.
242 if (isDefined(this.maxDecimalPlaces)) {
243 let decimalAsFloat = NumberStringBuilder.round(parseFloat("0."+decimalPart), this.maxDecimalPlaces);
244 if (decimalAsFloat % 1 === 0) {
245 integerPart = Math.floor((parseInt(integerPart) + decimalAsFloat)).toString();
246 integerPart = NumberStringBuilder.pad(integerPart, this.integerZeroCount, "left");
247 decimalPart = "";
248 } else {
249 decimalPart = decimalAsFloat.toString().split(".")[1];
250 }
251 } else {
252 if (decimalPart.length > this.decimalZeroCount) {
253 let decimalAsFloat = NumberStringBuilder.round(parseFloat("0."+decimalPart), this.decimalZeroCount);
254 let roundedDecimalPart;
255
256 if (decimalAsFloat % 1 === 0) {
257 integerPart = Math.floor((parseInt(integerPart) + decimalAsFloat)).toString();
258 integerPart = NumberStringBuilder.pad(integerPart, this.integerZeroCount, "left");
259 roundedDecimalPart = "";
260 } else {
261 roundedDecimalPart = decimalAsFloat.toString().split(".")[1];
262 }
263 decimalPart = NumberStringBuilder.pad(roundedDecimalPart, this.decimalZeroCount, "right");
264 } else {
265 decimalPart = NumberStringBuilder.pad(decimalPart, this.decimalZeroCount, "right");
266 }
267 }
268
269 // Inserting commas if necessary.
270 if (this.shouldUseComma) {
271 integerPart = integerPart.split("").reverse().map(function (digit, index) {
272 if (index % 3 === 0 && index !== 0) {
273 return digit + ",";
274 }
275 return digit;
276 }).reverse().join("");
277 }
278
279 if (this.integerZeroCount === 0 && integerPart === "0") {
280 integerPart = "";
281 }
282
283 if (this.n === 0) {
284 return this.headString + "." + this.tailString;
285 }
286 let trueSign = this.n < 0 ? "-" : "";
287
288 if ((this.decimalZeroCount === 0 && isUndefined(this.maxDecimalPlaces)) || isDefined(this.maxDecimalPlaces) && decimalPart === "") {
289 return trueSign + this.headString + integerPart + this.tailString;
290 }
291 return trueSign + this.headString + integerPart + "." + decimalPart + this.tailString;
292 }
293}
294
295export {
296 isDefined,
297 isUndefined,
298 isArray,
299 string,
300 numberToCharacter,
301 convertXYtoA1Coordinates,
302 A1toRowColCoordinates,
303 characterToNumber,
304 NumberStringBuilder
305}