@devmm/puredocs-excel-formula 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +169 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +169 -22
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -152,8 +152,11 @@ declare const enum TokenType {
|
|
|
152
152
|
SpillReference = 24,
|
|
153
153
|
Exclamation = 25,
|
|
154
154
|
AtSign = 26,
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
LeftBrace = 27,
|
|
156
|
+
RightBrace = 28,
|
|
157
|
+
Semicolon = 29,
|
|
158
|
+
ErrorLiteral = 30,
|
|
159
|
+
EOF = 31
|
|
157
160
|
}
|
|
158
161
|
interface Token {
|
|
159
162
|
readonly type: TokenType;
|
|
@@ -170,7 +173,8 @@ interface Token {
|
|
|
170
173
|
* - Error literals (#N/A, #REF!, #VALUE!, etc.)
|
|
171
174
|
* - Quoted sheet references ('My Sheet'!A1)
|
|
172
175
|
* - Unquoted sheet references (Sheet1!A1)
|
|
173
|
-
* - _xlfn. prefix stripping
|
|
176
|
+
* - _xlfn. / _xlws. / _xlpm. prefix stripping
|
|
177
|
+
* - Array constants ({1,2;3,4})
|
|
174
178
|
* - Cell references ($A$1, A1, AA123)
|
|
175
179
|
* - Named ranges
|
|
176
180
|
* - @ implicit intersection operator
|
package/dist/index.d.ts
CHANGED
|
@@ -152,8 +152,11 @@ declare const enum TokenType {
|
|
|
152
152
|
SpillReference = 24,
|
|
153
153
|
Exclamation = 25,
|
|
154
154
|
AtSign = 26,
|
|
155
|
-
|
|
156
|
-
|
|
155
|
+
LeftBrace = 27,
|
|
156
|
+
RightBrace = 28,
|
|
157
|
+
Semicolon = 29,
|
|
158
|
+
ErrorLiteral = 30,
|
|
159
|
+
EOF = 31
|
|
157
160
|
}
|
|
158
161
|
interface Token {
|
|
159
162
|
readonly type: TokenType;
|
|
@@ -170,7 +173,8 @@ interface Token {
|
|
|
170
173
|
* - Error literals (#N/A, #REF!, #VALUE!, etc.)
|
|
171
174
|
* - Quoted sheet references ('My Sheet'!A1)
|
|
172
175
|
* - Unquoted sheet references (Sheet1!A1)
|
|
173
|
-
* - _xlfn. prefix stripping
|
|
176
|
+
* - _xlfn. / _xlws. / _xlpm. prefix stripping
|
|
177
|
+
* - Array constants ({1,2;3,4})
|
|
174
178
|
* - Cell references ($A$1, A1, AA123)
|
|
175
179
|
* - Named ranges
|
|
176
180
|
* - @ implicit intersection operator
|
package/dist/index.js
CHANGED
|
@@ -446,8 +446,11 @@ var TokenType = /* @__PURE__ */ ((TokenType2) => {
|
|
|
446
446
|
TokenType2[TokenType2["SpillReference"] = 24] = "SpillReference";
|
|
447
447
|
TokenType2[TokenType2["Exclamation"] = 25] = "Exclamation";
|
|
448
448
|
TokenType2[TokenType2["AtSign"] = 26] = "AtSign";
|
|
449
|
-
TokenType2[TokenType2["
|
|
450
|
-
TokenType2[TokenType2["
|
|
449
|
+
TokenType2[TokenType2["LeftBrace"] = 27] = "LeftBrace";
|
|
450
|
+
TokenType2[TokenType2["RightBrace"] = 28] = "RightBrace";
|
|
451
|
+
TokenType2[TokenType2["Semicolon"] = 29] = "Semicolon";
|
|
452
|
+
TokenType2[TokenType2["ErrorLiteral"] = 30] = "ErrorLiteral";
|
|
453
|
+
TokenType2[TokenType2["EOF"] = 31] = "EOF";
|
|
451
454
|
return TokenType2;
|
|
452
455
|
})(TokenType || {});
|
|
453
456
|
function makeToken(type, value2, position) {
|
|
@@ -461,6 +464,8 @@ var FormulaException = class extends Error {
|
|
|
461
464
|
this.name = "FormulaException";
|
|
462
465
|
}
|
|
463
466
|
};
|
|
467
|
+
var UNDERSCORE = 95;
|
|
468
|
+
var RESERVED_PREFIXES = ["_XLFN.", "_XLWS.", "_XLPM."];
|
|
464
469
|
var KNOWN_ERRORS = /* @__PURE__ */ new Set([
|
|
465
470
|
"#NULL!",
|
|
466
471
|
"#DIV/0!",
|
|
@@ -472,7 +477,7 @@ var KNOWN_ERRORS = /* @__PURE__ */ new Set([
|
|
|
472
477
|
"#CALC!",
|
|
473
478
|
"#SPILL!"
|
|
474
479
|
]);
|
|
475
|
-
var _formula, _pos, _FormulaLexer_instances, readNextToken_fn, readString_fn, readErrorLiteral_fn, readQuotedSheetRef_fn, readNumber_fn, readIdentifier_fn, readCellRefChars_fn, readOperator_fn, skipWhitespace_fn;
|
|
480
|
+
var _formula, _pos, _FormulaLexer_instances, readNextToken_fn, readString_fn, readErrorLiteral_fn, readQuotedSheetRef_fn, readNumber_fn, readIdentifier_fn, stripReservedPrefixes_fn, readCellRefChars_fn, readOperator_fn, skipWhitespace_fn;
|
|
476
481
|
var FormulaLexer = class {
|
|
477
482
|
constructor(formula) {
|
|
478
483
|
__privateAdd(this, _FormulaLexer_instances);
|
|
@@ -490,7 +495,7 @@ var FormulaLexer = class {
|
|
|
490
495
|
const token = __privateMethod(this, _FormulaLexer_instances, readNextToken_fn).call(this);
|
|
491
496
|
if (token) tokens.push(token);
|
|
492
497
|
}
|
|
493
|
-
tokens.push(makeToken(
|
|
498
|
+
tokens.push(makeToken(31 /* EOF */, "", __privateGet(this, _pos)));
|
|
494
499
|
return tokens;
|
|
495
500
|
}
|
|
496
501
|
};
|
|
@@ -544,7 +549,7 @@ readErrorLiteral_fn = function() {
|
|
|
544
549
|
if (!KNOWN_ERRORS.has(upper2)) {
|
|
545
550
|
throw new FormulaException(`Unknown error literal "${raw}" at position ${start}.`);
|
|
546
551
|
}
|
|
547
|
-
return makeToken(
|
|
552
|
+
return makeToken(30 /* ErrorLiteral */, upper2, start);
|
|
548
553
|
};
|
|
549
554
|
readQuotedSheetRef_fn = function() {
|
|
550
555
|
const start = __privateWrapper(this, _pos)._++;
|
|
@@ -607,10 +612,8 @@ readIdentifier_fn = function() {
|
|
|
607
612
|
} else break;
|
|
608
613
|
}
|
|
609
614
|
let upper2 = raw.toUpperCase();
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
upper2 = upper2.slice(6);
|
|
613
|
-
}
|
|
615
|
+
raw = __privateMethod(this, _FormulaLexer_instances, stripReservedPrefixes_fn).call(this, raw, upper2);
|
|
616
|
+
if (raw.length !== upper2.length) upper2 = upper2.slice(upper2.length - raw.length);
|
|
614
617
|
if (upper2 === "TRUE") return makeToken(2 /* Boolean */, "TRUE", start);
|
|
615
618
|
if (upper2 === "FALSE") return makeToken(2 /* Boolean */, "FALSE", start);
|
|
616
619
|
if (__privateGet(this, _formula)[__privateGet(this, _pos)] === "!") {
|
|
@@ -632,6 +635,30 @@ readIdentifier_fn = function() {
|
|
|
632
635
|
}
|
|
633
636
|
return makeToken(23 /* NamedRange */, raw, start);
|
|
634
637
|
};
|
|
638
|
+
/**
|
|
639
|
+
* Drops Excel's storage-level name decorations. They carry no semantics — the
|
|
640
|
+
* writer stamps them so an older reader fails loudly instead of silently
|
|
641
|
+
* mis-evaluating — and they *stack*: `_xlfn._xlws.FILTER` wears two. Stripping
|
|
642
|
+
* one prefix once, as this used to, leaves `_XLWS.FILTER` unresolvable.
|
|
643
|
+
*
|
|
644
|
+
* Every identifier in every formula passes through here, so the common case is
|
|
645
|
+
* gated on a single char-code test: a name not starting with '_' returns
|
|
646
|
+
* untouched without so much as a comparison against the prefix table.
|
|
647
|
+
*/
|
|
648
|
+
stripReservedPrefixes_fn = function(raw, upper2) {
|
|
649
|
+
if (raw.charCodeAt(0) !== UNDERSCORE) return raw;
|
|
650
|
+
let cut = 0;
|
|
651
|
+
outer: for (; ; ) {
|
|
652
|
+
for (const prefix of RESERVED_PREFIXES) {
|
|
653
|
+
if (upper2.startsWith(prefix, cut)) {
|
|
654
|
+
cut += prefix.length;
|
|
655
|
+
continue outer;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
return cut > 0 && cut < raw.length ? raw.slice(cut) : raw;
|
|
661
|
+
};
|
|
635
662
|
readCellRefChars_fn = function() {
|
|
636
663
|
let ref = "";
|
|
637
664
|
while (__privateGet(this, _pos) < __privateGet(this, _formula).length) {
|
|
@@ -675,6 +702,14 @@ readOperator_fn = function() {
|
|
|
675
702
|
return makeToken(26 /* AtSign */, "@", start);
|
|
676
703
|
case "!":
|
|
677
704
|
return makeToken(25 /* Exclamation */, "!", start);
|
|
705
|
+
case "{":
|
|
706
|
+
return makeToken(27 /* LeftBrace */, "{", start);
|
|
707
|
+
case "}":
|
|
708
|
+
return makeToken(28 /* RightBrace */, "}", start);
|
|
709
|
+
// Inside an array constant ';' separates rows. Excel always stores ',' as
|
|
710
|
+
// the argument separator regardless of locale, so ';' has no other role.
|
|
711
|
+
case ";":
|
|
712
|
+
return makeToken(29 /* Semicolon */, ";", start);
|
|
678
713
|
case "<":
|
|
679
714
|
if (__privateGet(this, _formula)[__privateGet(this, _pos)] === ">") {
|
|
680
715
|
__privateWrapper(this, _pos)._++;
|
|
@@ -757,6 +792,19 @@ var ErrorNode = class extends FormulaNode {
|
|
|
757
792
|
return this.errorValue;
|
|
758
793
|
}
|
|
759
794
|
};
|
|
795
|
+
var _value;
|
|
796
|
+
var ArrayLiteralNode = class extends FormulaNode {
|
|
797
|
+
constructor(array) {
|
|
798
|
+
super();
|
|
799
|
+
this.array = array;
|
|
800
|
+
__privateAdd(this, _value);
|
|
801
|
+
__privateSet(this, _value, FormulaValue.array(array));
|
|
802
|
+
}
|
|
803
|
+
evaluate() {
|
|
804
|
+
return __privateGet(this, _value);
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
_value = new WeakMap();
|
|
760
808
|
var BlankNode = class extends FormulaNode {
|
|
761
809
|
evaluate() {
|
|
762
810
|
return FormulaValue.blank;
|
|
@@ -1034,7 +1082,7 @@ var CallNode = class extends FormulaNode {
|
|
|
1034
1082
|
};
|
|
1035
1083
|
|
|
1036
1084
|
// src/formula-parser.ts
|
|
1037
|
-
var _tokens, _pos2, _FormulaParser_instances, current_get, advance_fn, expect_fn, parseComparison_fn, parseConcatenation_fn, parseAddSub_fn, parseMulDiv_fn, parseUnary_fn, parsePower_fn, parsePercent_fn, parsePostfix_fn, parsePrimary_fn, tryParseFullRange_fn, parseFunction_fn, parseArgList_fn;
|
|
1085
|
+
var _tokens, _pos2, _FormulaParser_instances, current_get, advance_fn, expect_fn, parseComparison_fn, parseConcatenation_fn, parseAddSub_fn, parseMulDiv_fn, parseUnary_fn, parsePower_fn, parsePercent_fn, parsePostfix_fn, parsePrimary_fn, tryParseFullRange_fn, parseArrayLiteral_fn, parseArrayElement_fn, parseFunction_fn, parseArgList_fn;
|
|
1038
1086
|
var FormulaParser = class {
|
|
1039
1087
|
constructor(tokens) {
|
|
1040
1088
|
__privateAdd(this, _FormulaParser_instances);
|
|
@@ -1045,7 +1093,7 @@ var FormulaParser = class {
|
|
|
1045
1093
|
parse() {
|
|
1046
1094
|
__privateSet(this, _pos2, 0);
|
|
1047
1095
|
const node = __privateMethod(this, _FormulaParser_instances, parseComparison_fn).call(this);
|
|
1048
|
-
if (__privateGet(this, _FormulaParser_instances, current_get).type !==
|
|
1096
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type !== 31 /* EOF */) {
|
|
1049
1097
|
throw new FormulaException(
|
|
1050
1098
|
`Unexpected token '${__privateGet(this, _FormulaParser_instances, current_get).value}' at position ${__privateGet(this, _FormulaParser_instances, current_get).position}.`
|
|
1051
1099
|
);
|
|
@@ -1057,7 +1105,7 @@ _tokens = new WeakMap();
|
|
|
1057
1105
|
_pos2 = new WeakMap();
|
|
1058
1106
|
_FormulaParser_instances = new WeakSet();
|
|
1059
1107
|
current_get = function() {
|
|
1060
|
-
return __privateGet(this, _tokens)[__privateGet(this, _pos2)] ?? { type:
|
|
1108
|
+
return __privateGet(this, _tokens)[__privateGet(this, _pos2)] ?? { type: 31 /* EOF */, value: "", position: -1 };
|
|
1061
1109
|
};
|
|
1062
1110
|
advance_fn = function() {
|
|
1063
1111
|
const t = __privateGet(this, _FormulaParser_instances, current_get);
|
|
@@ -1172,7 +1220,7 @@ parsePrimary_fn = function() {
|
|
|
1172
1220
|
case 2 /* Boolean */:
|
|
1173
1221
|
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1174
1222
|
return new BooleanNode(token.value === "TRUE");
|
|
1175
|
-
case
|
|
1223
|
+
case 30 /* ErrorLiteral */:
|
|
1176
1224
|
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1177
1225
|
return new ErrorNode(FormulaValue.error(FormulaValue.errorFromString(token.value)));
|
|
1178
1226
|
case 3 /* CellReference */: {
|
|
@@ -1211,6 +1259,8 @@ parsePrimary_fn = function() {
|
|
|
1211
1259
|
return new SpillReferenceNode(token.value);
|
|
1212
1260
|
case 5 /* Function */:
|
|
1213
1261
|
return __privateMethod(this, _FormulaParser_instances, parseFunction_fn).call(this);
|
|
1262
|
+
case 27 /* LeftBrace */:
|
|
1263
|
+
return __privateMethod(this, _FormulaParser_instances, parseArrayLiteral_fn).call(this);
|
|
1214
1264
|
case 6 /* LeftParen */: {
|
|
1215
1265
|
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1216
1266
|
const expr = __privateMethod(this, _FormulaParser_instances, parseComparison_fn).call(this);
|
|
@@ -1245,6 +1295,79 @@ tryParseFullRange_fn = function(startText, sheetName) {
|
|
|
1245
1295
|
const hi = Math.max(start.index, end.index);
|
|
1246
1296
|
return start.kind === "col" ? new FullRangeReferenceNode(lo, hi, null, null, sheetName) : new FullRangeReferenceNode(null, null, lo, hi, sheetName);
|
|
1247
1297
|
};
|
|
1298
|
+
/**
|
|
1299
|
+
* Parses an array constant: `{1,2;3,4}` — ',' separates columns, ';' rows.
|
|
1300
|
+
*
|
|
1301
|
+
* Excel permits only literals inside one (no references, no calls, not even
|
|
1302
|
+
* arithmetic beyond a leading sign), so the value is fully determined here and
|
|
1303
|
+
* is materialised once into the node instead of being rebuilt on every eval.
|
|
1304
|
+
*
|
|
1305
|
+
* A ragged constant is rejected rather than padded: Excel refuses to accept
|
|
1306
|
+
* one at entry, so a file cannot legitimately contain it, and silently filling
|
|
1307
|
+
* the gaps would invent data the author never wrote.
|
|
1308
|
+
*/
|
|
1309
|
+
parseArrayLiteral_fn = function() {
|
|
1310
|
+
const open = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1311
|
+
const rows2 = [];
|
|
1312
|
+
let row2 = [];
|
|
1313
|
+
for (; ; ) {
|
|
1314
|
+
row2.push(__privateMethod(this, _FormulaParser_instances, parseArrayElement_fn).call(this));
|
|
1315
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type === 8 /* Comma */) {
|
|
1316
|
+
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1317
|
+
continue;
|
|
1318
|
+
}
|
|
1319
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type === 29 /* Semicolon */) {
|
|
1320
|
+
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1321
|
+
rows2.push(row2);
|
|
1322
|
+
row2 = [];
|
|
1323
|
+
continue;
|
|
1324
|
+
}
|
|
1325
|
+
break;
|
|
1326
|
+
}
|
|
1327
|
+
rows2.push(row2);
|
|
1328
|
+
__privateMethod(this, _FormulaParser_instances, expect_fn).call(this, 28 /* RightBrace */);
|
|
1329
|
+
const cols = rows2[0].length;
|
|
1330
|
+
if (rows2.some((r) => r.length !== cols)) {
|
|
1331
|
+
throw new FormulaException(
|
|
1332
|
+
`Array constant at position ${open.position} has rows of differing lengths.`
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
const array = new ArrayValue(rows2.length, cols);
|
|
1336
|
+
for (let r = 0; r < rows2.length; r++) {
|
|
1337
|
+
for (let c = 0; c < cols; c++) array.set(r, c, rows2[r][c]);
|
|
1338
|
+
}
|
|
1339
|
+
return new ArrayLiteralNode(array);
|
|
1340
|
+
};
|
|
1341
|
+
/** One element of an array constant: a literal, optionally signed. */
|
|
1342
|
+
parseArrayElement_fn = function() {
|
|
1343
|
+
let negate = false;
|
|
1344
|
+
let signed = false;
|
|
1345
|
+
while (__privateGet(this, _FormulaParser_instances, current_get).type === 10 /* Minus */ || __privateGet(this, _FormulaParser_instances, current_get).type === 9 /* Plus */) {
|
|
1346
|
+
if (__privateGet(this, _FormulaParser_instances, current_get).type === 10 /* Minus */) negate = !negate;
|
|
1347
|
+
signed = true;
|
|
1348
|
+
__privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1349
|
+
}
|
|
1350
|
+
const token = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1351
|
+
switch (token.type) {
|
|
1352
|
+
case 0 /* Number */: {
|
|
1353
|
+
const n = parseFloat(token.value);
|
|
1354
|
+
if (isNaN(n)) throw new FormulaException(`Invalid number: ${token.value}`);
|
|
1355
|
+
return FormulaValue.number(negate ? -n : n);
|
|
1356
|
+
}
|
|
1357
|
+
case 1 /* String */:
|
|
1358
|
+
if (signed) break;
|
|
1359
|
+
return FormulaValue.text(token.value);
|
|
1360
|
+
case 2 /* Boolean */:
|
|
1361
|
+
if (signed) break;
|
|
1362
|
+
return FormulaValue.boolean(token.value === "TRUE");
|
|
1363
|
+
case 30 /* ErrorLiteral */:
|
|
1364
|
+
if (signed) break;
|
|
1365
|
+
return FormulaValue.error(FormulaValue.errorFromString(token.value));
|
|
1366
|
+
}
|
|
1367
|
+
throw new FormulaException(
|
|
1368
|
+
`Array constants accept only literal values; got '${token.value}' at position ${token.position}.`
|
|
1369
|
+
);
|
|
1370
|
+
};
|
|
1248
1371
|
parseFunction_fn = function() {
|
|
1249
1372
|
const nameToken = __privateMethod(this, _FormulaParser_instances, advance_fn).call(this);
|
|
1250
1373
|
__privateMethod(this, _FormulaParser_instances, expect_fn).call(this, 6 /* LeftParen */);
|
|
@@ -4061,22 +4184,46 @@ function unique(a, c) {
|
|
|
4061
4184
|
const out = fromRows(result);
|
|
4062
4185
|
return byCol2 ? FormulaValue.array(out.arrayVal.transpose()) : out;
|
|
4063
4186
|
}
|
|
4187
|
+
var numVector = (a, c, i, dflt) => {
|
|
4188
|
+
if (i >= a.length) return { ok: true, v: [dflt] };
|
|
4189
|
+
const val = a[i].evaluate(c);
|
|
4190
|
+
if (val.isError) return { ok: false, e: val };
|
|
4191
|
+
const out = [];
|
|
4192
|
+
for (const item of val.isArray ? val.arrayVal.values() : [val]) {
|
|
4193
|
+
if (item.isBlank) continue;
|
|
4194
|
+
const d = item.tryAsDouble();
|
|
4195
|
+
if (!d.ok) return { ok: false, e: FormulaValue.errorValue };
|
|
4196
|
+
out.push(d.value);
|
|
4197
|
+
}
|
|
4198
|
+
return out.length > 0 ? { ok: true, v: out } : { ok: true, v: [dflt] };
|
|
4199
|
+
};
|
|
4064
4200
|
function sort(a, c) {
|
|
4065
4201
|
const v = a[0].evaluate(c);
|
|
4066
4202
|
if (v.isError) return v;
|
|
4067
|
-
const
|
|
4068
|
-
if (!
|
|
4069
|
-
const
|
|
4070
|
-
if (!
|
|
4203
|
+
const indices = numVector(a, c, 1, 1);
|
|
4204
|
+
if (!indices.ok) return indices.e;
|
|
4205
|
+
const orders = numVector(a, c, 2, 1);
|
|
4206
|
+
if (!orders.ok) return orders.e;
|
|
4071
4207
|
const byCol2 = a.length > 3 ? a[3].evaluate(c).coerceToBool().booleanValue : false;
|
|
4208
|
+
if (orders.v.length !== 1 && orders.v.length !== indices.v.length) {
|
|
4209
|
+
return FormulaValue.errorValue;
|
|
4210
|
+
}
|
|
4072
4211
|
const base = byCol2 ? asArray2(v).transpose() : asArray2(v);
|
|
4073
4212
|
const rows2 = toRows(base);
|
|
4074
|
-
const
|
|
4075
|
-
|
|
4076
|
-
|
|
4213
|
+
const keys = indices.v.map((n, i) => ({
|
|
4214
|
+
col: Math.trunc(n) - 1,
|
|
4215
|
+
dir: (orders.v.length === 1 ? orders.v[0] : orders.v[i]) < 0 ? -1 : 1
|
|
4216
|
+
}));
|
|
4217
|
+
const width = rows2.length > 0 ? rows2[0].length : 0;
|
|
4218
|
+
if (rows2.length > 0 && keys.some((k) => k.col < 0 || k.col >= width)) {
|
|
4219
|
+
return FormulaValue.errorValue;
|
|
4220
|
+
}
|
|
4077
4221
|
const sorted = rows2.map((r, i) => ({ r, i })).sort((x, y) => {
|
|
4078
|
-
const
|
|
4079
|
-
|
|
4222
|
+
for (const k of keys) {
|
|
4223
|
+
const cmp = FormulaValue.compare(x.r[k.col], y.r[k.col]) * k.dir;
|
|
4224
|
+
if (cmp !== 0) return cmp;
|
|
4225
|
+
}
|
|
4226
|
+
return x.i - y.i;
|
|
4080
4227
|
}).map((o) => o.r);
|
|
4081
4228
|
const out = fromRows(sorted);
|
|
4082
4229
|
return byCol2 ? FormulaValue.array(out.arrayVal.transpose()) : out;
|