UNPKG

15.3 kBJavaScriptView Raw
1import { tableChips } from '@analys/table-chips';
2import { tableDivide } from '@analys/table-divide';
3import { tableFilter } from '@analys/table-filter';
4import { tableFind } from '@analys/table-find';
5import { tableFormula } from '@analys/table-formula';
6import { tableGroup } from '@analys/table-group';
7import { slice, shallow } from '@analys/table-init';
8import { tableJoin } from '@analys/table-join';
9import { lookupCached, lookup, lookupMany } from '@analys/table-lookup';
10import { tableAcquire, tableMerge } from '@analys/table-merge';
11import { tablePivot } from '@analys/table-pivot';
12import { tableToObject } from '@analys/table-select';
13import { inferTypes } from '@analys/table-types';
14import { selectTabularToSamples, tabularToSamples, selectTabular, sortTabularByKeys } from '@analys/tabular';
15import { NUM_ASC } from '@aryth/comparer';
16import { DistinctCount, Distinct } from '@aryth/distinct-column';
17import { distinctByColumn } from '@aryth/distinct-matrix';
18import { column } from '@vect/column-getter';
19import { mutate } from '@vect/column-mapper';
20import { push, unshift, pop, shift, splices as splices$1 } from '@vect/columns-update';
21import { size, transpose } from '@vect/matrix';
22import { mapper as mapper$1, mutate as mutate$2, selectMutate } from '@vect/matrix-mapper';
23import { wind } from '@vect/object-init';
24import { difference, intersect } from '@vect/vector-algebra';
25import { mapper, mutate as mutate$1, iterate } from '@vect/vector-mapper';
26import { splices } from '@vect/vector-update';
27
28class Table {
29 /** @type {*[]} */
30 head;
31 /** @type {*[][]} */
32
33 rows;
34 /** @type {string} */
35
36 title;
37 /** @type {string[]} */
38
39 types;
40 /**
41 * @param {*[]} [head]
42 * @param {*[][]} [rows]
43 * @param {string} [title]
44 * @param {string[]} [types]
45 */
46
47 constructor(head, rows, title, types) {
48 this.head = head || [];
49 this.rows = rows || [];
50 this.title = title || '';
51 this.types = types;
52 }
53
54 static from(o) {
55 return new Table(o.head || o.banner, o.rows || o.matrix, o.title, o.types);
56 }
57
58 toSamples(fields) {
59 return fields ? selectTabularToSamples.call(this, fields) : tabularToSamples.call(this);
60 }
61
62 toObject(mutate = false) {
63 var _this, _this2;
64
65 return mutate ? (_this = this, slice(_this)) : (_this2 = this, shallow(_this2));
66 }
67
68 setTitle(title) {
69 return this.title = title, this;
70 }
71
72 get size() {
73 return size(this.rows);
74 }
75
76 get ht() {
77 var _this$rows;
78
79 return (_this$rows = this.rows) === null || _this$rows === void 0 ? void 0 : _this$rows.length;
80 }
81
82 get wd() {
83 var _this$head;
84
85 return (_this$head = this.head) === null || _this$head === void 0 ? void 0 : _this$head.length;
86 }
87
88 get height() {
89 var _this$rows2;
90
91 return (_this$rows2 = this.rows) === null || _this$rows2 === void 0 ? void 0 : _this$rows2.length;
92 }
93
94 get width() {
95 var _this$head2;
96
97 return (_this$head2 = this.head) === null || _this$head2 === void 0 ? void 0 : _this$head2.length;
98 }
99
100 get columns() {
101 return transpose(this.rows);
102 }
103
104 cell(x, field) {
105 return x in this.rows ? this.rows[x][this.coin(field)] : undefined;
106 }
107
108 coin(field) {
109 return this.head.indexOf(field);
110 }
111
112 columnIndexes(fields) {
113 return fields.map(this.coin, this);
114 }
115
116 row(field, value, objectify) {
117 const vector = this.rows.find(row => row[this.coin(field)] === value);
118 return vector && objectify ? wind(this.head, vector) : vector;
119 }
120
121 column(field) {
122 return column(this.rows, this.coin(field), this.height);
123 }
124
125 setColumn(field, column) {
126 return mutate(this.rows, this.coin(field), (_, i) => column[i], this.height), this;
127 }
128
129 mutateColumn(field, fn) {
130 return mutate(this.rows, this.coin(field), (x, i) => fn(x, i), this.height), this;
131 }
132
133 pushRow(row) {
134 return this.rows.push(row), this;
135 }
136
137 unshiftRow(row) {
138 return this.rows.unshift(row), this;
139 }
140
141 pushColumn(label, column) {
142 return this.head.push(label), push(this.rows, column), this;
143 }
144
145 unshiftColumn(label, column) {
146 return this.head.unshift(label), unshift(this.rows, column), this;
147 }
148
149 popRow() {
150 return this.rows.pop();
151 }
152
153 shiftRow() {
154 return this.rows.shift();
155 }
156
157 popColumn() {
158 return [this.head.pop(), pop(this.rows)];
159 }
160
161 shiftColumn() {
162 return [this.head.shift(), shift(this.rows)];
163 }
164
165 renameColumn(field, newName) {
166 const ci = this.coin(field);
167 if (ci >= 0) this.head[ci] = newName;
168 return this;
169 }
170
171 mapHead(fn, {
172 mutate = true
173 } = {}) {
174 return this.boot({
175 head: mapper(this.head, fn)
176 }, mutate);
177 }
178
179 mutateHead(fn) {
180 return mutate$1(this.head, fn), this;
181 }
182
183 map(fn, {
184 mutate = true
185 } = {}) {
186 return this.boot({
187 rows: mapper$1(this.rows, fn, this.height, this.width)
188 }, mutate);
189 }
190
191 mutate(fn, {
192 fields,
193 exclusive
194 } = {}) {
195 if (!fields && !exclusive) return mutate$2(this.rows, fn, this.height, this.width), this;
196 fields = fields ?? this.head;
197 fields = exclusive ? difference(fields, exclusive) : fields;
198 return selectMutate(this.rows, this.columnIndexes(fields), fn, this.height), this;
199 }
200
201 lookupOne(valueToFind, key, field, cached = true) {
202 return (cached ? lookupCached : lookup).call(this, valueToFind, key, field);
203 }
204
205 lookupMany(valuesToFind, key, field) {
206 return lookupMany.call(this, valuesToFind, key, field);
207 }
208 /**
209 *
210 * @param {string} key
211 * @param {string|string[]|[string,string][]} [field]
212 * @param {boolean} [objectify=true]
213 * @return {Object|Array}
214 */
215
216
217 lookupTable(key, field, objectify = true) {
218 return tableToObject.call(this, key, field, objectify);
219 }
220 /**
221 *
222 * @param {*[]|[*,*][]} fields
223 * @param {boolean=true} [mutate]
224 * @returns {Table}
225 */
226
227
228 select(fields, {
229 mutate = false
230 } = {}) {
231 let o = mutate ? this : slice(this);
232 selectTabular.call(o, fields);
233 return mutate ? this : this.copy(o);
234 }
235 /**
236 *
237 * @param {*} field
238 * @param {Function} splitter
239 * @param {*} [fields] - new names of divided fields
240 * @param {boolean=true} [mutate]
241 * @returns {Table}
242 */
243
244
245 divideColumn(field, splitter, {
246 fields,
247 mutate = false
248 } = {}) {
249 var _this3;
250
251 const o = mutate ? this : (_this3 = this, shallow(_this3)),
252 y = this.coin(field);
253 o.head.splice(y, 1, ...(fields ?? splitter(field)));
254 iterate(o.rows, row => row.splice(y, 1, ...splitter(row[y])));
255 return mutate ? this : Table.from(o);
256 }
257 /**
258 *
259 * @param {{key,to,as}|{key,to,as}[]} fieldSpec
260 * @param {*} [nextTo] - the existing field after which the newField inserts
261 * @param {boolean=true} [mutate]
262 * @returns {Table}
263 */
264
265
266 proliferateColumn(fieldSpec, {
267 nextTo,
268 mutate = false
269 } = {}) {
270 var _this4, _fieldSpec;
271
272 if (!Array.isArray(fieldSpec)) fieldSpec = [fieldSpec];
273 const o = mutate ? this : (_this4 = this, shallow(_this4)),
274 {
275 head,
276 rows
277 } = o,
278 y = nextTo ? this.coin(nextTo) + 1 : 0;
279 fieldSpec.forEach(o => o.index = this.coin(o.key));
280
281 if (((_fieldSpec = fieldSpec) === null || _fieldSpec === void 0 ? void 0 : _fieldSpec.length) === 1) {
282 const [{
283 index,
284 to,
285 as
286 }] = fieldSpec;
287 head.splice(y, 0, as);
288 iterate(rows, row => row.splice(y, 0, to(row[index])));
289 } else {
290 head.splice(y, 0, ...fieldSpec.map(({
291 as
292 }) => as));
293 iterate(rows, row => row.splice(y, 0, ...fieldSpec.map(({
294 index,
295 to
296 }) => to(row[index]))));
297 }
298
299 return mutate ? this : Table.from(o);
300 }
301 /**
302 *
303 * @param {*|*[]} newField
304 * @param {*[]|*[][]} column
305 * @param {*} [nextTo] - the existing field after which the newField inserts
306 * @param {boolean=true} [mutate]
307 * @returns {Table}
308 */
309
310
311 insertColumn(newField, column, {
312 nextTo,
313 mutate = false
314 } = {}) {
315 var _this5;
316
317 const o = mutate ? this : (_this5 = this, shallow(_this5)),
318 y = nextTo ? this.coin(nextTo) + 1 : 0;
319
320 if (Array.isArray(newField)) {
321 o.head.splice(y, 0, ...newField);
322 iterate(o.rows, (row, i) => row.splice(y, 0, ...column[i]));
323 } else {
324 o.head.splice(y, 0, newField);
325 iterate(o.rows, (row, i) => row.splice(y, 0, column[i]));
326 }
327
328 return mutate ? this : Table.from(o);
329 }
330 /**
331 *
332 * @param {*|*[]} field
333 * @param {boolean=true} [mutate]
334 * @returns {Table}
335 */
336
337
338 deleteColumn(field, {
339 mutate = false
340 } = {}) {
341 var _this6;
342
343 const o = mutate ? this : (_this6 = this, shallow(_this6));
344 const {
345 head,
346 rows
347 } = o;
348
349 if (Array.isArray(field)) {
350 const indexes = this.columnIndexes(field).filter(i => i >= 0).sort(NUM_ASC);
351 splices(head, indexes);
352 splices$1(rows, indexes);
353 } else {
354 const index = this.coin(field);
355 head.splice(index, 1);
356 rows.forEach(row => row.splice(index, 1));
357 }
358
359 return mutate ? this : Table.from(o);
360 }
361
362 divide(fields, {
363 mutate = false
364 } = {}) {
365 var _this7;
366
367 const o = mutate ? this : (_this7 = this, shallow(_this7));
368 const {
369 pick,
370 rest
371 } = tableDivide.call(o, fields);
372 return {
373 pick: Table.from(pick),
374 rest: mutate ? this : Table.from(rest)
375 };
376 }
377 /**
378 *
379 * @param {Object|Filter[]|Filter} filterCollection
380 * @param {boolean} [mutate=true]
381 * @return {Table}
382 */
383
384
385 filter(filterCollection, {
386 mutate = true
387 } = {}) {
388 var _this8;
389
390 const o = mutate ? this : (_this8 = this, slice(_this8));
391 tableFilter.call(o, filterCollection);
392 return mutate ? this : this.copy(o);
393 }
394 /**
395 *
396 * @param {Object<*,function(*?):boolean>} filter
397 * @param {boolean} [mutate=true]
398 * @return {Table}
399 */
400
401
402 find(filter, {
403 mutate = true
404 } = {}) {
405 var _this9;
406
407 const o = mutate ? this : (_this9 = this, slice(_this9));
408 tableFind.call(o, filter);
409 return mutate ? this : this.copy(o);
410 } //TODO: supposedly returns rows, currently only returns specific column
411
412
413 distinct(fields, {
414 mutate = true
415 } = {}) {
416 var _this10;
417
418 const o = mutate ? this : (_this10 = this, slice(_this10));
419
420 for (let field of fields) o.rows = distinctByColumn.call(o.rows, this.coin(field));
421
422 return mutate ? this : this.copy(o);
423 }
424 /**
425 *
426 * @param {*} field
427 * @param {boolean} [count=false]
428 * @param {string|boolean} [sort=false] - When sort is function, sort must be a comparer between two point element.
429 * @returns {[any, any][]|[]|any[]|*}
430 */
431
432
433 distinctOnColumn(field, {
434 count = false,
435 sort = false
436 } = {}) {
437 return count ? DistinctCount(this.coin(field))(this.rows, {
438 l: this.height,
439 sort
440 }) : Distinct(this.coin(field))(this.rows, this.height);
441 }
442 /**
443 *
444 * @param field
445 * @param comparer
446 * @param mutate
447 * @returns {Table} - 'this' Table rows is mutated by sort function
448 */
449
450
451 sort(field, comparer, {
452 mutate = true
453 } = {}) {
454 var _this11;
455
456 const y = this.coin(field);
457
458 const rowComparer = (a, b) => comparer(a[y], b[y]);
459
460 const o = mutate ? this : (_this11 = this, slice(_this11));
461 o.rows.sort(rowComparer);
462 return mutate ? this : this.copy(o);
463 }
464 /**
465 *
466 * @param {function(*,*):number} comparer - Comparer of head elements
467 * @param {boolean} mutate
468 * @returns {Table|*}
469 */
470
471
472 sortLabel(comparer, {
473 mutate = true
474 } = {}) {
475 var _this12;
476
477 let o = mutate ? this : (_this12 = this, slice(_this12));
478 sortTabularByKeys.call(o, comparer);
479 return mutate ? this : this.copy(o);
480 }
481
482 join(another, fields, joinType, fillEmpty) {
483 return Table.from(tableJoin(this, another, fields, joinType, fillEmpty));
484 }
485 /**
486 *
487 * @param {Table} another
488 * @param {boolean} [mutate=true]
489 * @return {Table}
490 */
491
492
493 union(another, {
494 mutate = true
495 } = {}) {
496 const self = mutate ? this : this.copy();
497 const shared = intersect(self.head, another.head);
498
499 if (shared.length) {
500 for (let label of shared) self.setColumn(label, another.column(label));
501
502 another = another.deleteColumn(shared, {
503 mutate
504 });
505 }
506
507 return mutate ? tableAcquire(self, another) : this.copy(tableMerge(self, another));
508 }
509 /**
510 * @param {Object} options
511 * @param {*} options.key
512 * @param {*} [options.field]
513 * @param {number} [options.mode=ACCUM] - MERGE, ACCUM, INCRE, COUNT
514 * @param {boolean} [options.objectify=true]
515 * @return {[*,*][]|{}}
516 */
517
518
519 chips(options = {}) {
520 return tableChips.call(this, options);
521 }
522 /**
523 * @param {Object} options
524 * @param {*} options.key
525 * @param {*} [options.field]
526 * @param {Function} [options.filter]
527 * @param {Object|Array} [options.alias]
528 * @return {Table}
529 */
530
531
532 group(options = {}) {
533 return Table.from(tableGroup.call(this, options));
534 }
535 /**
536 * @param {Object} options
537 * @param {Object|Array} [formulae]
538 * @param {Function} [options.filter]
539 * @param {boolean} [options.append=true]
540 * @return {Table}
541 */
542
543
544 formula(formulae, options = {}) {
545 return Table.from(tableFormula.call(this, formulae, options));
546 }
547 /**
548 * @param {Object} options
549 * @param {str|str[]|Object<str,Function>|[string,Function][]} options.side
550 * @param {str|str[]|Object<str,Function>|[string,Function][]} options.banner
551 * @param {Object|*[]|string|number} [options.field]
552 * @param {Object<string|number,function(*?):boolean>} [options.filter]
553 * @param {function(...*):number} [options.formula] - formula is valid only when cell is CubeCell array.
554 * @returns {Crostab|CrostabObject}
555 */
556
557
558 crosTab(options = {}) {
559 const table = slice(this);
560
561 if (options.filter) {
562 tableFind.call(table, options.filter);
563 }
564
565 return tablePivot.call(options, table);
566 }
567
568 inferTypes({
569 inferType,
570 omitNull = true,
571 mutate = false
572 } = {}) {
573 const types = inferTypes.call(this, {
574 inferType,
575 omitNull
576 });
577 return mutate ? this.types = types : types;
578 }
579 /** @returns {Table} */
580
581
582 boot({
583 head,
584 rows,
585 types
586 } = {}, mutate = true) {
587 if (mutate) {
588 if (head) this.head = head;
589 if (rows) this.rows = rows;
590 if (types) this.types = types;
591 return this;
592 } else {
593 return this.copy({
594 head,
595 rows,
596 types
597 });
598 }
599 }
600 /** @returns {Table} */
601
602
603 copy({
604 head,
605 rows,
606 types
607 } = {}) {
608 var _this$types;
609
610 if (!head) head = this.head.slice();
611 if (!rows) rows = this.rows.map(row => row.slice());
612 if (!types) types = (_this$types = this.types) === null || _this$types === void 0 ? void 0 : _this$types.slice();
613 return new Table(head, rows, this.title, types);
614 }
615
616}
617/**
618 * @param {str|str[]|Object<str,Function>|[string,Function][]} side
619 * @param {str|str[]|Object<str,Function>|[string,Function][]} banner
620 * @param {Object|*[]|string|number} [field]
621 * @param {Object<string|number,function(*?):boolean>} [filter]
622 * @param {function(...*):number} [formula] - formula is valid only when cell is CubeCell array.
623 */
624
625export { Table };