JavaScript 混合值类型的数组排序 - 寻找合适的比较函数
JavaScript Array Sort with Mixed Value Types - Looking for Appropriate Compare Function
请参考下面的table
我的 objective 是对包含混合数据类型的 JavaScript 数组进行排序:数字、字符串、布尔值、日期和未定义的值。
这不是自然排序。相反,我需要 尽可能匹配 MS Excel 使用的排序顺序 ...。
这是一个 JavaScript ES5 问题,但数据来自 Excel 通过 VBA 中的数组。
该平台是托管在 Excel VB 用户窗体中的 MS WebBrowser 控件 (IE11)。
我知道这是相当深奥的,但希望最终的问题不是。
Microsoft 的 JavaScript 版本有一个名为 VBArray Object 的语言扩展,它有一个方法可用于将传递的 VB 安全数组转换为普通 JavaScript数组:
function convertVBArray(safearray){return new VBArray(safearray).toArray()}
.toArray()
方法进行转换,包括每个元素的数据类型转换。 VBA 数组的类型为 Variant(标记联合),它支持许多不同的变量数据子类型。 .toArray()
方法将这些转换为 JavaScript 更有限的数据类型调色板。
下面的table显示了23个值。想象一下它们在 Excel 的专栏中。我从该列填充 VBA 变体数组(看起来像您在下面 table 的 Excel Displays
列中看到的内容。
接下来的两列显示数据一旦进入 VBA 数组后的样子。
接下来的三列显示数据在使用 convertVBArray()
.
转换为 JavaScript 数组后的样子
接下来,我使用以下比较函数对 JavaScript 数组进行排序:
a.sort(function (a, b) { return isNaN(a) ? isNaN(b) ? a.localeCompare(b) : 1 : isNaN(b) ? -1 : parseFloat(a) - parseFloat(b) })
...但这并不适用。
请参考下面table的JS sortArray()
栏。数据按上述排序后返回的顺序呈现。
我正在寻找对比较函数的更改,以便它尽可能接近[中的下一列进行排序=101=], Excel Sort ASC
.
最后,我还希望能够模仿最后一列 Excel Sort DESC
中所示的反向排序。
我意识到各种错误值在 JavaScript 中被转换为 undefined
值,对此可能无能为力。我确实喜欢它们都排在排序列表的底部。
总而言之,我希望 Excel Displays
中的值按照 Excel Sort ASC
中显示的顺序按 JavaScript 排序。但是我当前的比较函数产生了 JS sortArray()
.
中的不良排序
我当前的比较功能无法按需要的顺序排序.
.
以下 table 描述了数据和数据类型在从 Excel 传递到 VBA 到 JavaScipt 再返回到 Excel 时如何变化。
+-----+---------------+------------------------------+-----------------+-----------------------+----------------------+--------------+------------------------------------------------------------+-----------------------+----------------+-----------------+-----------------+
| Row | Excel Entered | Excel Number Format | Excel Displays | VBA Array Value | VBA Array Value Type | JS Array Ndx | JS Array Value | JS Array Value typeof | JS sortArray() | Excel Sort ASC | Excel Sort DESC |
+-----+---------------+------------------------------+-----------------+-----------------------+----------------------+--------------+------------------------------------------------------------+-----------------------+----------------+-----------------+-----------------+
| 1 | anchorage | General | anchorage | anchorage | 8 - vbString | 0 | anchorage | string | -78.96 | -78.96 | #NAME? |
| 2 | 123 | General | 123 | 123 | 5 - vbDouble | 1 | 123 | number | 123 | -1 | #N/A |
| 3 | FALSE | General | FALSE | False | 11 - vbBoolean | 2 | false | boolean | FALSE | 0 | #DIV/0! |
| 4 | =qqq | General | #NAME? | Error 2029 | 10 - vbError | 3 | undefined | undefined | -1 | 0.60625 | TRUE |
| 5 | 0 | 0.000_);[Red](0.000) | 0 | 0 | 5 - vbDouble | 4 | 0 | number | 0 | 1 | FALSE |
| 6 | 43514.49663 | m/d/yyyy h:mm | 2/18/2019 11:55 | 2/18/2019 10:59:03 AM | 7 - vbDate | 5 | Mon Feb 18 2019 11:59:09 GMT-0800 (Pacific Standard Time) | date | 43514.49663 | 99.01 | zimmer |
| 7 | =NA() | General | #N/A | Error 2042 | 10 - vbError | 6 | undefined | undefined | 0.60625 | 123 | Major Tom |
| 8 | 99.01 | $#,##0.00_);[Red]($#,##0.00) | 99.01 | 99.01 | 6 - vbCurrency | 7 | 99.01 | number | 1 | 3/20/2017 | anchorage |
| 9 | | General | | | 0 - vbEmpty | 8 | undefined | undefined | 99.01 | 2/18/2019 11:55 | ABC |
| 10 | =1/0 | General | #DIV/0! | Error 2007 | 10 - vbError | 9 | undefined | undefined | 888.87 | | 888.87 |
| 11 | ="" | General | | | 8 - vbString | 10 | | string | | $%^%$^ | $%^%$^ |
| 12 | ABC | @ | ABC | ABC | 8 - vbString | 11 | ABC | string | TRUE | 888.87 | |
| 13 | -78.96 | General | -78.96 | -78.96 | 5 - vbDouble | 12 | -78.96 | number | 42814 | ABC | 2/18/2019 11:55 |
| 14 | Major Tom | @ | Major Tom | Major Tom | 8 - vbString | 13 | Major Tom | string | $%^%$^ | anchorage | 3/20/2017 |
| 15 | TRUE | General | TRUE | True | 11 - vbBoolean | 14 | true | boolean | ABC | Major Tom | 123 |
| 16 | =TODAY()-700 | m/d/yyyy | 3/20/2017 | 3/20/2017 | 7 - vbDate | 15 | Mon Mar 120 2017 00:00:00 GMT-0700 (Pacific Standard Time) | date | anchorage | zimmer | 99.01 |
| 17 | zimmer | General | zimmer | zimmer | 8 - vbString | 16 | zimmer | string | Major Tom | FALSE | 1 |
| 18 | 1 | General | 1 | 1 | 5 - vbDouble | 17 | 1 | number | zimmer | TRUE | 0.60625 |
| 19 | | General | | | 0 - vbEmpty | 18 | undefined | undefined | | #NAME? | 0 |
| 20 | =0-1 | General | -1 | -1 | 5 - vbDouble | 19 | -1 | number | | #N/A | -1 |
| 21 | 0.60625 | h:mm | 0.60625 | 0.60625 | 5 - vbDouble | 20 | 0.60625 | number | | #DIV/0! | -78.96 |
| 22 | ="888.87" | General | 888.87 | 888.87 | 8 - vbString | 21 | 888.87 | string | | | |
| 23 | $%^%$^ | General | $%^%$^ | $%^%$^ | 8 - vbString | 22 | $%^%$^ | string | | | |
+-----+---------------+------------------------------+-----------------+-----------------------+----------------------+--------------+------------------------------------------------------------+-----------------------+----------------+-----------------+-----------------+
这里只是 JavaScript 数组和目标排序顺序:
+--------------+------------------------------------------------------------+-----------------------+-------------------+
| JS Array Ndx | JS Array Value | JS Array Value typeof | TARGET SORT ORDER |
+--------------+------------------------------------------------------------+-----------------------+-------------------+
| 0 | anchorage | string | -78.96 |
| 1 | 123 | number | -1 |
| 2 | false | boolean | 0 |
| 3 | undefined | undefined | 0.60625 |
| 4 | 0 | number | 1 |
| 5 | Mon Feb 18 2019 11:59:09 GMT-0800 (Pacific Standard Time) | date | 99.01 |
| 6 | undefined | undefined | 123 |
| 7 | 99.01 | number | 3/20/2017 |
| 8 | undefined | undefined | 2/18/2019 11:55 |
| 9 | undefined | undefined | |
| 10 | | string | $%^%$^ |
| 11 | ABC | string | 888.87 |
| 12 | -78.96 | number | ABC |
| 13 | Major Tom | string | anchorage |
| 14 | true | boolean | Major Tom |
| 15 | Mon Mar 120 2017 00:00:00 GMT-0700 (Pacific Standard Time) | date | zimmer |
| 16 | zimmer | string | false |
| 17 | 1 | number | true |
| 18 | undefined | undefined | undefined |
| 19 | -1 | number | undefined |
| 20 | 0.60625 | number | undefined |
| 21 | 888.87 | string | undefined |
| 22 | $%^%$^ | string | undefined |
+--------------+------------------------------------------------------------+-----------------------+-------------------+
我想这就是你想要的东西。
在你的 JS 数组值 typeOf 中,它说 date 是一个日期类型,但在正常情况下 Javascript typeof new Date()
会给你对象,所以在你的情况下你可能想要更改为 date
.
我在这里做的是创建一个复合排序,首先我们按 typeof
排序,如果 a 和 b 的类型相同,你得到的 return 值为 0,这如果你做了复合排序的第二部分,你在这里保证 a 和 b 是同一类型,所以只需要根据类型进行适当的排序。
下面是一个工作片段,您可以 运行 查看结果。
var data = [
"anchorage",
123,
false,
undefined,
0,
new Date("Mon Feb 18 2019 11:59:09 GMT-0800"),
undefined,
99.01,
undefined,
undefined,
"",
"ABC",
-78.96,
"Major Tom",
true,
new Date("Mon Mar 12 2017 00:00:00 GMT-0700"),
"zimmer",
1,
undefined,
-1,
0.60625,
"888.87",
"$%^%$^"
];
//what ordering do we want our types?.
var typesort = [
"number", "object" /*date*/, "string", "boolean", "undefined"
];
data.sort(function (a, b) {
//first lets sort by type
var r = typesort.indexOf(typeof a) - typesort.indexOf(typeof b);
if (r === 0) {
//types are the same, need compound sort
if (typeof a === "object") return a.getTime() - b.getTime()
else if (typeof a === "string") return a.localeCompare(b)
else return a - b;
}
return r;
});
console.log(data);
我不能说我很熟悉 VB,但如果你只是想要 ASC 和 DESC 排序,我会
// slice on desc as to not affect asc
var asc = new VBArray(yourArray).toArray(), desc = asc.slice().reverse();
有点晚了,但这里有一个使用 lodash 的解决方案。
var values = [
"anchorage",
123,
false,
undefined,
0,
new Date("Mon Feb 18 2019 11:59:09 GMT-0800 (Pacific Standard Time)"),
undefined,
99.01,
undefined,
undefined,
"",
"ABC",
-78.96,
"Major Tom",
true,
new Date("Mon Mar 12 2017 00:00:00 GMT-0700 (Pacific Standard Time)"),
"zimmer",
1,
undefined,
-1,
0.60625,
"888.87",
"$%^%$^"
]
var typesAsc = ["number", "date", "string", "boolean", "undefined"]
var typeOfValue = function (v) { return v instanceof Date ? "date" : typeof v }
var sortAsc = function (arr) {
return _.orderBy(arr, _.identity, "asc")
}
var sortDesc = function (arr) {
return _.orderBy(arr, _.identity, "desc")
}
var flattenInOrder = function (groups, ordering) {
return _(ordering).map(function (type) { return groups[type] })
.flatten()
.value()
}
var valuesByType = _.groupBy(values, typeOfValue)
var valuesAscByType = _.mapValues(valuesByType, sortAsc)
var valuesAsc = flattenInOrder(valuesAscByType, typesAsc)
console.log(valuesAsc)
var valuesDescByType = _.mapValues(valuesByType, sortDesc)
var valuesDesc = flattenInOrder(valuesDescByType, typesAsc.reverse())
console.log(valuesDesc)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
请参考下面的table
我的 objective 是对包含混合数据类型的 JavaScript 数组进行排序:数字、字符串、布尔值、日期和未定义的值。
这不是自然排序。相反,我需要 尽可能匹配 MS Excel 使用的排序顺序 ...。
这是一个 JavaScript ES5 问题,但数据来自 Excel 通过 VBA 中的数组。
该平台是托管在 Excel VB 用户窗体中的 MS WebBrowser 控件 (IE11)。
我知道这是相当深奥的,但希望最终的问题不是。
Microsoft 的 JavaScript 版本有一个名为 VBArray Object 的语言扩展,它有一个方法可用于将传递的 VB 安全数组转换为普通 JavaScript数组:
function convertVBArray(safearray){return new VBArray(safearray).toArray()}
.toArray()
方法进行转换,包括每个元素的数据类型转换。 VBA 数组的类型为 Variant(标记联合),它支持许多不同的变量数据子类型。 .toArray()
方法将这些转换为 JavaScript 更有限的数据类型调色板。
下面的table显示了23个值。想象一下它们在 Excel 的专栏中。我从该列填充 VBA 变体数组(看起来像您在下面 table 的 Excel Displays
列中看到的内容。
接下来的两列显示数据一旦进入 VBA 数组后的样子。
接下来的三列显示数据在使用 convertVBArray()
.
接下来,我使用以下比较函数对 JavaScript 数组进行排序:
a.sort(function (a, b) { return isNaN(a) ? isNaN(b) ? a.localeCompare(b) : 1 : isNaN(b) ? -1 : parseFloat(a) - parseFloat(b) })
...但这并不适用。
请参考下面table的JS sortArray()
栏。数据按上述排序后返回的顺序呈现。
我正在寻找对比较函数的更改,以便它尽可能接近[中的下一列进行排序=101=], Excel Sort ASC
.
最后,我还希望能够模仿最后一列 Excel Sort DESC
中所示的反向排序。
我意识到各种错误值在 JavaScript 中被转换为 undefined
值,对此可能无能为力。我确实喜欢它们都排在排序列表的底部。
总而言之,我希望 Excel Displays
中的值按照 Excel Sort ASC
中显示的顺序按 JavaScript 排序。但是我当前的比较函数产生了 JS sortArray()
.
我当前的比较功能无法按需要的顺序排序.
.
以下 table 描述了数据和数据类型在从 Excel 传递到 VBA 到 JavaScipt 再返回到 Excel 时如何变化。
+-----+---------------+------------------------------+-----------------+-----------------------+----------------------+--------------+------------------------------------------------------------+-----------------------+----------------+-----------------+-----------------+
| Row | Excel Entered | Excel Number Format | Excel Displays | VBA Array Value | VBA Array Value Type | JS Array Ndx | JS Array Value | JS Array Value typeof | JS sortArray() | Excel Sort ASC | Excel Sort DESC |
+-----+---------------+------------------------------+-----------------+-----------------------+----------------------+--------------+------------------------------------------------------------+-----------------------+----------------+-----------------+-----------------+
| 1 | anchorage | General | anchorage | anchorage | 8 - vbString | 0 | anchorage | string | -78.96 | -78.96 | #NAME? |
| 2 | 123 | General | 123 | 123 | 5 - vbDouble | 1 | 123 | number | 123 | -1 | #N/A |
| 3 | FALSE | General | FALSE | False | 11 - vbBoolean | 2 | false | boolean | FALSE | 0 | #DIV/0! |
| 4 | =qqq | General | #NAME? | Error 2029 | 10 - vbError | 3 | undefined | undefined | -1 | 0.60625 | TRUE |
| 5 | 0 | 0.000_);[Red](0.000) | 0 | 0 | 5 - vbDouble | 4 | 0 | number | 0 | 1 | FALSE |
| 6 | 43514.49663 | m/d/yyyy h:mm | 2/18/2019 11:55 | 2/18/2019 10:59:03 AM | 7 - vbDate | 5 | Mon Feb 18 2019 11:59:09 GMT-0800 (Pacific Standard Time) | date | 43514.49663 | 99.01 | zimmer |
| 7 | =NA() | General | #N/A | Error 2042 | 10 - vbError | 6 | undefined | undefined | 0.60625 | 123 | Major Tom |
| 8 | 99.01 | $#,##0.00_);[Red]($#,##0.00) | 99.01 | 99.01 | 6 - vbCurrency | 7 | 99.01 | number | 1 | 3/20/2017 | anchorage |
| 9 | | General | | | 0 - vbEmpty | 8 | undefined | undefined | 99.01 | 2/18/2019 11:55 | ABC |
| 10 | =1/0 | General | #DIV/0! | Error 2007 | 10 - vbError | 9 | undefined | undefined | 888.87 | | 888.87 |
| 11 | ="" | General | | | 8 - vbString | 10 | | string | | $%^%$^ | $%^%$^ |
| 12 | ABC | @ | ABC | ABC | 8 - vbString | 11 | ABC | string | TRUE | 888.87 | |
| 13 | -78.96 | General | -78.96 | -78.96 | 5 - vbDouble | 12 | -78.96 | number | 42814 | ABC | 2/18/2019 11:55 |
| 14 | Major Tom | @ | Major Tom | Major Tom | 8 - vbString | 13 | Major Tom | string | $%^%$^ | anchorage | 3/20/2017 |
| 15 | TRUE | General | TRUE | True | 11 - vbBoolean | 14 | true | boolean | ABC | Major Tom | 123 |
| 16 | =TODAY()-700 | m/d/yyyy | 3/20/2017 | 3/20/2017 | 7 - vbDate | 15 | Mon Mar 120 2017 00:00:00 GMT-0700 (Pacific Standard Time) | date | anchorage | zimmer | 99.01 |
| 17 | zimmer | General | zimmer | zimmer | 8 - vbString | 16 | zimmer | string | Major Tom | FALSE | 1 |
| 18 | 1 | General | 1 | 1 | 5 - vbDouble | 17 | 1 | number | zimmer | TRUE | 0.60625 |
| 19 | | General | | | 0 - vbEmpty | 18 | undefined | undefined | | #NAME? | 0 |
| 20 | =0-1 | General | -1 | -1 | 5 - vbDouble | 19 | -1 | number | | #N/A | -1 |
| 21 | 0.60625 | h:mm | 0.60625 | 0.60625 | 5 - vbDouble | 20 | 0.60625 | number | | #DIV/0! | -78.96 |
| 22 | ="888.87" | General | 888.87 | 888.87 | 8 - vbString | 21 | 888.87 | string | | | |
| 23 | $%^%$^ | General | $%^%$^ | $%^%$^ | 8 - vbString | 22 | $%^%$^ | string | | | |
+-----+---------------+------------------------------+-----------------+-----------------------+----------------------+--------------+------------------------------------------------------------+-----------------------+----------------+-----------------+-----------------+
这里只是 JavaScript 数组和目标排序顺序:
+--------------+------------------------------------------------------------+-----------------------+-------------------+
| JS Array Ndx | JS Array Value | JS Array Value typeof | TARGET SORT ORDER |
+--------------+------------------------------------------------------------+-----------------------+-------------------+
| 0 | anchorage | string | -78.96 |
| 1 | 123 | number | -1 |
| 2 | false | boolean | 0 |
| 3 | undefined | undefined | 0.60625 |
| 4 | 0 | number | 1 |
| 5 | Mon Feb 18 2019 11:59:09 GMT-0800 (Pacific Standard Time) | date | 99.01 |
| 6 | undefined | undefined | 123 |
| 7 | 99.01 | number | 3/20/2017 |
| 8 | undefined | undefined | 2/18/2019 11:55 |
| 9 | undefined | undefined | |
| 10 | | string | $%^%$^ |
| 11 | ABC | string | 888.87 |
| 12 | -78.96 | number | ABC |
| 13 | Major Tom | string | anchorage |
| 14 | true | boolean | Major Tom |
| 15 | Mon Mar 120 2017 00:00:00 GMT-0700 (Pacific Standard Time) | date | zimmer |
| 16 | zimmer | string | false |
| 17 | 1 | number | true |
| 18 | undefined | undefined | undefined |
| 19 | -1 | number | undefined |
| 20 | 0.60625 | number | undefined |
| 21 | 888.87 | string | undefined |
| 22 | $%^%$^ | string | undefined |
+--------------+------------------------------------------------------------+-----------------------+-------------------+
我想这就是你想要的东西。
在你的 JS 数组值 typeOf 中,它说 date 是一个日期类型,但在正常情况下 Javascript typeof new Date()
会给你对象,所以在你的情况下你可能想要更改为 date
.
我在这里做的是创建一个复合排序,首先我们按 typeof
排序,如果 a 和 b 的类型相同,你得到的 return 值为 0,这如果你做了复合排序的第二部分,你在这里保证 a 和 b 是同一类型,所以只需要根据类型进行适当的排序。
下面是一个工作片段,您可以 运行 查看结果。
var data = [
"anchorage",
123,
false,
undefined,
0,
new Date("Mon Feb 18 2019 11:59:09 GMT-0800"),
undefined,
99.01,
undefined,
undefined,
"",
"ABC",
-78.96,
"Major Tom",
true,
new Date("Mon Mar 12 2017 00:00:00 GMT-0700"),
"zimmer",
1,
undefined,
-1,
0.60625,
"888.87",
"$%^%$^"
];
//what ordering do we want our types?.
var typesort = [
"number", "object" /*date*/, "string", "boolean", "undefined"
];
data.sort(function (a, b) {
//first lets sort by type
var r = typesort.indexOf(typeof a) - typesort.indexOf(typeof b);
if (r === 0) {
//types are the same, need compound sort
if (typeof a === "object") return a.getTime() - b.getTime()
else if (typeof a === "string") return a.localeCompare(b)
else return a - b;
}
return r;
});
console.log(data);
我不能说我很熟悉 VB,但如果你只是想要 ASC 和 DESC 排序,我会
// slice on desc as to not affect asc
var asc = new VBArray(yourArray).toArray(), desc = asc.slice().reverse();
有点晚了,但这里有一个使用 lodash 的解决方案。
var values = [
"anchorage",
123,
false,
undefined,
0,
new Date("Mon Feb 18 2019 11:59:09 GMT-0800 (Pacific Standard Time)"),
undefined,
99.01,
undefined,
undefined,
"",
"ABC",
-78.96,
"Major Tom",
true,
new Date("Mon Mar 12 2017 00:00:00 GMT-0700 (Pacific Standard Time)"),
"zimmer",
1,
undefined,
-1,
0.60625,
"888.87",
"$%^%$^"
]
var typesAsc = ["number", "date", "string", "boolean", "undefined"]
var typeOfValue = function (v) { return v instanceof Date ? "date" : typeof v }
var sortAsc = function (arr) {
return _.orderBy(arr, _.identity, "asc")
}
var sortDesc = function (arr) {
return _.orderBy(arr, _.identity, "desc")
}
var flattenInOrder = function (groups, ordering) {
return _(ordering).map(function (type) { return groups[type] })
.flatten()
.value()
}
var valuesByType = _.groupBy(values, typeOfValue)
var valuesAscByType = _.mapValues(valuesByType, sortAsc)
var valuesAsc = flattenInOrder(valuesAscByType, typesAsc)
console.log(valuesAsc)
var valuesDescByType = _.mapValues(valuesByType, sortDesc)
var valuesDesc = flattenInOrder(valuesDescByType, typesAsc.reverse())
console.log(valuesDesc)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>