@fileverse-dev/formulajs 4.4.17 → 4.4.18

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
@@ -1270,48 +1270,75 @@ function ROWS(array) {
1270
1270
  * @param {*} by_col Optional. A logical value indicating the desired sort direction; FALSE to sort by row (default), TRUE to sort by column
1271
1271
  * @returns
1272
1272
  */
1273
- function SORT(array, sort_index = 1, sort_order = 1, by_col = false) {
1274
- if (!array || !Array.isArray(array)) {
1275
- return na
1276
- }
1273
+ function SORT(array, sort_index = 1, is_ascending, by_col = false) {
1274
+ if (!array || !Array.isArray(array)) return na;
1275
+ if (array.length === 0) return 0;
1277
1276
 
1278
- if (array.length === 0) {
1279
- return 0
1280
- }
1277
+ let idx = parseNumber(sort_index);
1278
+ if (!idx || idx < 1) return value;
1279
+ idx = idx - 1;
1281
1280
 
1282
- sort_index = parseNumber(sort_index);
1283
- if (!sort_index || sort_index < 1) {
1284
- return value
1285
- }
1281
+ const sortOrderNumber = is_ascending === 'FALSE' ? -1 : 1;
1282
+ let order = parseNumber(sortOrderNumber);
1283
+ if (order !== 1 && order !== -1) return value;
1286
1284
 
1287
- sort_order = parseNumber(sort_order);
1288
- if (sort_order !== 1 && sort_order !== -1) {
1289
- return value
1290
- }
1285
+ const byCol = parseBool(by_col);
1286
+ if (typeof byCol !== "boolean") return name;
1287
+
1288
+ // Rectangularize then orient
1289
+ const matrix = fillMatrix(array);
1290
+ const working = byCol ? transpose(matrix) : matrix;
1291
1291
 
1292
- by_col = parseBool(by_col);
1293
- if (typeof by_col !== 'boolean') {
1294
- return name
1292
+ if (!working.length || !working[0] || idx >= working[0].length) {
1293
+ return value; // out of bounds
1295
1294
  }
1296
1295
 
1297
- const sortArray = (arr) =>
1298
- arr.sort((a, b) => {
1299
- a = parseString(a[sort_index - 1]);
1300
- b = parseString(b[sort_index - 1]);
1296
+ // Helpers (aiming for spreadsheet-ish behavior)
1297
+ const isBlank = (v) => v === "" || v === null || v === undefined;
1301
1298
 
1302
- return sort_order === 1 ? (a < b ? sort_order * -1 : sort_order) : a > b ? sort_order : sort_order * -1
1303
- });
1299
+ const cmp = (a, b) => {
1300
+ // Blanks last (ascending); we'll multiply by order later for descending.
1301
+ const aBlank = isBlank(a);
1302
+ const bBlank = isBlank(b);
1303
+ if (aBlank && bBlank) return 0;
1304
+ if (aBlank) return 1;
1305
+ if (bBlank) return -1;
1304
1306
 
1305
- const matrix = fillMatrix(array);
1306
- const result = by_col ? transpose(matrix) : matrix;
1307
+ // If both parse as finite numbers, compare numerically
1308
+ const na = parseNumber(a);
1309
+ const nb = parseNumber(b);
1310
+ const aNum = Number.isFinite(na);
1311
+ const bNum = Number.isFinite(nb);
1312
+
1313
+ if (aNum && bNum) {
1314
+ if (na < nb) return -1;
1315
+ if (na > nb) return 1;
1316
+ return 0;
1317
+ }
1318
+
1319
+ // Fallback: case-insensitive string compare
1320
+ const sa = (parseString(a) ?? "").toString().toLowerCase();
1321
+ const sb = (parseString(b) ?? "").toString().toLowerCase();
1322
+ if (sa < sb) return -1;
1323
+ if (sa > sb) return 1;
1324
+ return 0;
1325
+ };
1326
+
1327
+ // Stable sort: decorate with original index
1328
+ const decorated = working.map((row, i) => ({ row, i }));
1307
1329
 
1308
- return sort_index >= 1 && sort_index <= result[0].length
1309
- ? by_col
1310
- ? transpose(sortArray(result))
1311
- : sortArray(result)
1312
- : value
1330
+ decorated.sort((A, B) => {
1331
+ const base = cmp(A.row[idx], B.row[idx]);
1332
+ if (base !== 0) return base * order;
1333
+ // stable tiebreaker: original order
1334
+ return A.i - B.i;
1335
+ });
1336
+
1337
+ const sorted = decorated.map((d) => d.row);
1338
+ return byCol ? transpose(sorted) : sorted;
1313
1339
  }
1314
1340
 
1341
+
1315
1342
  /**
1316
1343
  * Returns the transpose of an array.
1317
1344
  *
package/lib/esm/index.mjs CHANGED
@@ -1268,48 +1268,75 @@ function ROWS(array) {
1268
1268
  * @param {*} by_col Optional. A logical value indicating the desired sort direction; FALSE to sort by row (default), TRUE to sort by column
1269
1269
  * @returns
1270
1270
  */
1271
- function SORT(array, sort_index = 1, sort_order = 1, by_col = false) {
1272
- if (!array || !Array.isArray(array)) {
1273
- return na
1274
- }
1271
+ function SORT(array, sort_index = 1, is_ascending, by_col = false) {
1272
+ if (!array || !Array.isArray(array)) return na;
1273
+ if (array.length === 0) return 0;
1275
1274
 
1276
- if (array.length === 0) {
1277
- return 0
1278
- }
1275
+ let idx = parseNumber(sort_index);
1276
+ if (!idx || idx < 1) return value;
1277
+ idx = idx - 1;
1279
1278
 
1280
- sort_index = parseNumber(sort_index);
1281
- if (!sort_index || sort_index < 1) {
1282
- return value
1283
- }
1279
+ const sortOrderNumber = is_ascending === 'FALSE' ? -1 : 1;
1280
+ let order = parseNumber(sortOrderNumber);
1281
+ if (order !== 1 && order !== -1) return value;
1284
1282
 
1285
- sort_order = parseNumber(sort_order);
1286
- if (sort_order !== 1 && sort_order !== -1) {
1287
- return value
1288
- }
1283
+ const byCol = parseBool(by_col);
1284
+ if (typeof byCol !== "boolean") return name;
1285
+
1286
+ // Rectangularize then orient
1287
+ const matrix = fillMatrix(array);
1288
+ const working = byCol ? transpose(matrix) : matrix;
1289
1289
 
1290
- by_col = parseBool(by_col);
1291
- if (typeof by_col !== 'boolean') {
1292
- return name
1290
+ if (!working.length || !working[0] || idx >= working[0].length) {
1291
+ return value; // out of bounds
1293
1292
  }
1294
1293
 
1295
- const sortArray = (arr) =>
1296
- arr.sort((a, b) => {
1297
- a = parseString(a[sort_index - 1]);
1298
- b = parseString(b[sort_index - 1]);
1294
+ // Helpers (aiming for spreadsheet-ish behavior)
1295
+ const isBlank = (v) => v === "" || v === null || v === undefined;
1299
1296
 
1300
- return sort_order === 1 ? (a < b ? sort_order * -1 : sort_order) : a > b ? sort_order : sort_order * -1
1301
- });
1297
+ const cmp = (a, b) => {
1298
+ // Blanks last (ascending); we'll multiply by order later for descending.
1299
+ const aBlank = isBlank(a);
1300
+ const bBlank = isBlank(b);
1301
+ if (aBlank && bBlank) return 0;
1302
+ if (aBlank) return 1;
1303
+ if (bBlank) return -1;
1302
1304
 
1303
- const matrix = fillMatrix(array);
1304
- const result = by_col ? transpose(matrix) : matrix;
1305
+ // If both parse as finite numbers, compare numerically
1306
+ const na = parseNumber(a);
1307
+ const nb = parseNumber(b);
1308
+ const aNum = Number.isFinite(na);
1309
+ const bNum = Number.isFinite(nb);
1310
+
1311
+ if (aNum && bNum) {
1312
+ if (na < nb) return -1;
1313
+ if (na > nb) return 1;
1314
+ return 0;
1315
+ }
1316
+
1317
+ // Fallback: case-insensitive string compare
1318
+ const sa = (parseString(a) ?? "").toString().toLowerCase();
1319
+ const sb = (parseString(b) ?? "").toString().toLowerCase();
1320
+ if (sa < sb) return -1;
1321
+ if (sa > sb) return 1;
1322
+ return 0;
1323
+ };
1324
+
1325
+ // Stable sort: decorate with original index
1326
+ const decorated = working.map((row, i) => ({ row, i }));
1305
1327
 
1306
- return sort_index >= 1 && sort_index <= result[0].length
1307
- ? by_col
1308
- ? transpose(sortArray(result))
1309
- : sortArray(result)
1310
- : value
1328
+ decorated.sort((A, B) => {
1329
+ const base = cmp(A.row[idx], B.row[idx]);
1330
+ if (base !== 0) return base * order;
1331
+ // stable tiebreaker: original order
1332
+ return A.i - B.i;
1333
+ });
1334
+
1335
+ const sorted = decorated.map((d) => d.row);
1336
+ return byCol ? transpose(sorted) : sorted;
1311
1337
  }
1312
1338
 
1339
+
1313
1340
  /**
1314
1341
  * Returns the transpose of an array.
1315
1342
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fileverse-dev/formulajs",
3
- "version": "4.4.17",
3
+ "version": "4.4.18",
4
4
  "description": "JavaScript implementation of most Microsoft Excel formula functions",
5
5
  "author": "Formulajs",
6
6
  "publishConfig": {
@@ -3806,7 +3806,7 @@ export function SMARTCONTRACT(...args: any[]): Promise<any>;
3806
3806
  * @param {*} by_col Optional. A logical value indicating the desired sort direction; FALSE to sort by row (default), TRUE to sort by column
3807
3807
  * @returns
3808
3808
  */
3809
- export function SORT(array: any, sort_index?: any, sort_order?: any, by_col?: any): any;
3809
+ export function SORT(array: any, sort_index: any, is_ascending: any, by_col?: any): any;
3810
3810
  /**
3811
3811
  * Returns a positive square root.
3812
3812
  *
@@ -3806,7 +3806,7 @@ export function SMARTCONTRACT(...args: any[]): Promise<any>;
3806
3806
  * @param {*} by_col Optional. A logical value indicating the desired sort direction; FALSE to sort by row (default), TRUE to sort by column
3807
3807
  * @returns
3808
3808
  */
3809
- export function SORT(array: any, sort_index?: any, sort_order?: any, by_col?: any): any;
3809
+ export function SORT(array: any, sort_index: any, is_ascending: any, by_col?: any): any;
3810
3810
  /**
3811
3811
  * Returns a positive square root.
3812
3812
  *