@fileverse-dev/formulajs 4.4.12-mod-3 → 4.4.12-mod-3-patch-1

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/lib/cjs/index.cjs CHANGED
@@ -1271,6 +1271,7 @@ function ROWS(array) {
1271
1271
  * @returns
1272
1272
  */
1273
1273
  function SORT(array, sort_index = 1, sort_order = 1, by_col = false) {
1274
+ // Accept multiple sort keys like Google Sheets: SORT(range, sort_column, is_ascending, [sort_column2, is_ascending2, ...])
1274
1275
  if (!array || !Array.isArray(array)) {
1275
1276
  return na
1276
1277
  }
@@ -1279,37 +1280,104 @@ function SORT(array, sort_index = 1, sort_order = 1, by_col = false) {
1279
1280
  return 0
1280
1281
  }
1281
1282
 
1282
- sort_index = parseNumber(sort_index);
1283
- if (!sort_index || sort_index < 1) {
1284
- return value
1283
+ by_col = parseBool(by_col);
1284
+ if (typeof by_col !== 'boolean') {
1285
+ return name
1285
1286
  }
1286
1287
 
1287
- sort_order = parseNumber(sort_order);
1288
- if (sort_order !== 1 && sort_order !== -1) {
1288
+ // Collect sort keys (index and order). First key comes from sort_index/sort_order.
1289
+ const sortKeys = [];
1290
+
1291
+ const normalizeOrder = (o) => {
1292
+ // Try boolean first (TRUE -> ascending)
1293
+ const b = parseBool(o);
1294
+ if (typeof b === 'boolean') {
1295
+ return b ? 1 : -1
1296
+ }
1297
+
1298
+ const n = parseNumber(o);
1299
+ if (n === 1 || n === -1) {
1300
+ return n
1301
+ }
1302
+
1303
+ return null
1304
+ };
1305
+
1306
+ const firstIndex = parseNumber(sort_index);
1307
+ if (!firstIndex || firstIndex < 1) {
1289
1308
  return value
1290
1309
  }
1291
1310
 
1292
- by_col = parseBool(by_col);
1293
- if (typeof by_col !== 'boolean') {
1294
- return name
1311
+ const firstOrder = normalizeOrder(sort_order);
1312
+ if (firstOrder === null) {
1313
+ return value
1295
1314
  }
1296
1315
 
1297
- const sortArray = (arr) =>
1298
- arr.sort((a, b) => {
1299
- a = parseString(a[sort_index - 1]);
1300
- b = parseString(b[sort_index - 1]);
1316
+ sortKeys.push({ index: firstIndex, order: firstOrder });
1301
1317
 
1302
- return sort_order === 1 ? (a < b ? sort_order * -1 : sort_order) : a > b ? sort_order : sort_order * -1
1303
- });
1318
+ // Additional args may contain more sort_index/sort_order pairs
1319
+ if (arguments.length > 4) {
1320
+ // arguments: 0=array,1=sort_index,2=sort_order,3=by_col,4=nextIndex,5=nextOrder,...
1321
+ for (let i = 4; i < arguments.length; i += 2) {
1322
+ const idx = parseNumber(arguments[i]);
1323
+ if (!idx || idx < 1) {
1324
+ return value
1325
+ }
1326
+
1327
+ const ordArg = i + 1 < arguments.length ? arguments[i + 1] : 1;
1328
+ const ord = normalizeOrder(ordArg);
1329
+ if (ord === null) {
1330
+ return value
1331
+ }
1332
+
1333
+ sortKeys.push({ index: idx, order: ord });
1334
+ }
1335
+ }
1304
1336
 
1305
1337
  const matrix = fillMatrix(array);
1306
- const result = by_col ? transpose(matrix) : matrix;
1338
+ const working = by_col ? transpose(matrix) : matrix;
1339
+
1340
+ // Validate that all sort indexes are within column bounds
1341
+ const numCols = working[0] ? working[0].length : 0;
1342
+ for (const k of sortKeys) {
1343
+ if (k.index < 1 || k.index > numCols) {
1344
+ return value
1345
+ }
1346
+ }
1347
+
1348
+ const parseForCompare = (val) => {
1349
+ // Preserve numbers when possible
1350
+ if (typeof val === 'number') return val
1351
+ const num = parseNumber(val);
1352
+ if (typeof num === 'number' && !Number.isNaN(num)) return num
1353
+ return parseString(val)
1354
+ };
1355
+
1356
+ const comparator = (aRow, bRow) => {
1357
+ for (const k of sortKeys) {
1358
+ const aVal = parseForCompare(aRow[k.index - 1]);
1359
+ const bVal = parseForCompare(bRow[k.index - 1]);
1360
+
1361
+ if (aVal === bVal) continue
1362
+
1363
+ // Numeric comparison if both are numbers
1364
+ if (typeof aVal === 'number' && typeof bVal === 'number') {
1365
+ return (aVal < bVal ? -1 : 1) * k.order
1366
+ }
1367
+
1368
+ // String comparison
1369
+ const aStr = String(aVal).toLowerCase();
1370
+ const bStr = String(bVal).toLowerCase();
1371
+ return aStr.localeCompare(bStr) * k.order
1372
+ }
1373
+
1374
+ return 0
1375
+ };
1376
+
1377
+ // Sort in-place and return
1378
+ working.sort(comparator);
1307
1379
 
1308
- return sort_index >= 1 && sort_index <= result[0].length
1309
- ? by_col
1310
- ? transpose(sortArray(result))
1311
- : sortArray(result)
1312
- : value
1380
+ return by_col ? transpose(working) : working
1313
1381
  }
1314
1382
 
1315
1383
  /**
package/lib/esm/index.mjs CHANGED
@@ -1269,6 +1269,7 @@ function ROWS(array) {
1269
1269
  * @returns
1270
1270
  */
1271
1271
  function SORT(array, sort_index = 1, sort_order = 1, by_col = false) {
1272
+ // Accept multiple sort keys like Google Sheets: SORT(range, sort_column, is_ascending, [sort_column2, is_ascending2, ...])
1272
1273
  if (!array || !Array.isArray(array)) {
1273
1274
  return na
1274
1275
  }
@@ -1277,37 +1278,104 @@ function SORT(array, sort_index = 1, sort_order = 1, by_col = false) {
1277
1278
  return 0
1278
1279
  }
1279
1280
 
1280
- sort_index = parseNumber(sort_index);
1281
- if (!sort_index || sort_index < 1) {
1282
- return value
1281
+ by_col = parseBool(by_col);
1282
+ if (typeof by_col !== 'boolean') {
1283
+ return name
1283
1284
  }
1284
1285
 
1285
- sort_order = parseNumber(sort_order);
1286
- if (sort_order !== 1 && sort_order !== -1) {
1286
+ // Collect sort keys (index and order). First key comes from sort_index/sort_order.
1287
+ const sortKeys = [];
1288
+
1289
+ const normalizeOrder = (o) => {
1290
+ // Try boolean first (TRUE -> ascending)
1291
+ const b = parseBool(o);
1292
+ if (typeof b === 'boolean') {
1293
+ return b ? 1 : -1
1294
+ }
1295
+
1296
+ const n = parseNumber(o);
1297
+ if (n === 1 || n === -1) {
1298
+ return n
1299
+ }
1300
+
1301
+ return null
1302
+ };
1303
+
1304
+ const firstIndex = parseNumber(sort_index);
1305
+ if (!firstIndex || firstIndex < 1) {
1287
1306
  return value
1288
1307
  }
1289
1308
 
1290
- by_col = parseBool(by_col);
1291
- if (typeof by_col !== 'boolean') {
1292
- return name
1309
+ const firstOrder = normalizeOrder(sort_order);
1310
+ if (firstOrder === null) {
1311
+ return value
1293
1312
  }
1294
1313
 
1295
- const sortArray = (arr) =>
1296
- arr.sort((a, b) => {
1297
- a = parseString(a[sort_index - 1]);
1298
- b = parseString(b[sort_index - 1]);
1314
+ sortKeys.push({ index: firstIndex, order: firstOrder });
1299
1315
 
1300
- return sort_order === 1 ? (a < b ? sort_order * -1 : sort_order) : a > b ? sort_order : sort_order * -1
1301
- });
1316
+ // Additional args may contain more sort_index/sort_order pairs
1317
+ if (arguments.length > 4) {
1318
+ // arguments: 0=array,1=sort_index,2=sort_order,3=by_col,4=nextIndex,5=nextOrder,...
1319
+ for (let i = 4; i < arguments.length; i += 2) {
1320
+ const idx = parseNumber(arguments[i]);
1321
+ if (!idx || idx < 1) {
1322
+ return value
1323
+ }
1324
+
1325
+ const ordArg = i + 1 < arguments.length ? arguments[i + 1] : 1;
1326
+ const ord = normalizeOrder(ordArg);
1327
+ if (ord === null) {
1328
+ return value
1329
+ }
1330
+
1331
+ sortKeys.push({ index: idx, order: ord });
1332
+ }
1333
+ }
1302
1334
 
1303
1335
  const matrix = fillMatrix(array);
1304
- const result = by_col ? transpose(matrix) : matrix;
1336
+ const working = by_col ? transpose(matrix) : matrix;
1337
+
1338
+ // Validate that all sort indexes are within column bounds
1339
+ const numCols = working[0] ? working[0].length : 0;
1340
+ for (const k of sortKeys) {
1341
+ if (k.index < 1 || k.index > numCols) {
1342
+ return value
1343
+ }
1344
+ }
1345
+
1346
+ const parseForCompare = (val) => {
1347
+ // Preserve numbers when possible
1348
+ if (typeof val === 'number') return val
1349
+ const num = parseNumber(val);
1350
+ if (typeof num === 'number' && !Number.isNaN(num)) return num
1351
+ return parseString(val)
1352
+ };
1353
+
1354
+ const comparator = (aRow, bRow) => {
1355
+ for (const k of sortKeys) {
1356
+ const aVal = parseForCompare(aRow[k.index - 1]);
1357
+ const bVal = parseForCompare(bRow[k.index - 1]);
1358
+
1359
+ if (aVal === bVal) continue
1360
+
1361
+ // Numeric comparison if both are numbers
1362
+ if (typeof aVal === 'number' && typeof bVal === 'number') {
1363
+ return (aVal < bVal ? -1 : 1) * k.order
1364
+ }
1365
+
1366
+ // String comparison
1367
+ const aStr = String(aVal).toLowerCase();
1368
+ const bStr = String(bVal).toLowerCase();
1369
+ return aStr.localeCompare(bStr) * k.order
1370
+ }
1371
+
1372
+ return 0
1373
+ };
1374
+
1375
+ // Sort in-place and return
1376
+ working.sort(comparator);
1305
1377
 
1306
- return sort_index >= 1 && sort_index <= result[0].length
1307
- ? by_col
1308
- ? transpose(sortArray(result))
1309
- : sortArray(result)
1310
- : value
1378
+ return by_col ? transpose(working) : working
1311
1379
  }
1312
1380
 
1313
1381
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fileverse-dev/formulajs",
3
- "version": "4.4.12-mod-3",
3
+ "version": "4.4.12-mod-3-patch-1",
4
4
  "description": "JavaScript implementation of most Microsoft Excel formula functions",
5
5
  "author": "Formulajs",
6
6
  "publishConfig": {
@@ -3788,7 +3788,7 @@ export function SMARTCONTRACT(...args: any[]): Promise<any>;
3788
3788
  * @param {*} by_col Optional. A logical value indicating the desired sort direction; FALSE to sort by row (default), TRUE to sort by column
3789
3789
  * @returns
3790
3790
  */
3791
- export function SORT(array: any, sort_index?: any, sort_order?: any, by_col?: any): any;
3791
+ export function SORT(array: any, sort_index?: any, sort_order?: any, by_col?: any, ...args: any[]): any;
3792
3792
  /**
3793
3793
  * Returns a positive square root.
3794
3794
  *
@@ -3788,7 +3788,7 @@ export function SMARTCONTRACT(...args: any[]): Promise<any>;
3788
3788
  * @param {*} by_col Optional. A logical value indicating the desired sort direction; FALSE to sort by row (default), TRUE to sort by column
3789
3789
  * @returns
3790
3790
  */
3791
- export function SORT(array: any, sort_index?: any, sort_order?: any, by_col?: any): any;
3791
+ export function SORT(array: any, sort_index?: any, sort_order?: any, by_col?: any, ...args: any[]): any;
3792
3792
  /**
3793
3793
  * Returns a positive square root.
3794
3794
  *