多维数组排序顺序跨浏览器兼容"natural case"
Sorting sequence of multi-dimensional array cross-browser-compatible and with "natural case"
我需要为我的 Web 应用程序对较大的数组(1000-2000 个键)进行复杂的排序。我在 Safari 12.0/FF 66.0 上有点用,但 chrome 74.0 似乎完全不一样。
我要排序的序列 - 不区分大小写,自然大小写:
1. "scene"
2. "shot"
3. "take"
4. "name"
这些值中的每一个都可以是字符串(例如 4
、4b
或 4-PU üöä!"
)或 'undefined',并且可能类似于:
[
{"scene": "1", "shot": "1", "take": "4", "name": "A031C006_170718_R1W0"},
{"scene": "8", "shot": "8", "take": "4", "name": "A020C004_170716_R1W0"},
{"scene": "1", "shot": "1", "take": "10", "name": "A031C013_170718_R1W0"},
{"scene": undefined, "shot": undefined, "take": undefined, "name": "A001C549_190226_R04Q"},
{"scene": "2", "shot": "2", "take": "1", "name": "A008C010_170715_R1W0"},
{"scene": "5", "shot": "5", "take": "1", "name": "A015C005_170716_R1W0"},
{"scene": "3", "shot": "3", "take": "7", "name": "A002C003_170714_R1W0"},
{"scene": "5", "shot": "5", "take": "5", "name": "A021C005_170716_R1W0"},
{"scene": "5", "shot": "5", "take": "9", "name": "A024C006_170717_R1W0"},
{"scene": "1", "shot": "1", "take": "3", "name": "A004C006_170714_R1W0"},
{"scene": "1-b", "shot": "1", "take": "3", "name": "A004C007_170718_R1W0*"},
{"scene": "5", "shot": "5PU", "take": "6", "name": "A021C005_170716_R1W0*"},
]
如果键scene
不存在,排序到末尾,按"name"排序。
这是 vue.js 项目的一部分,我希望在没有额外库的情况下完成它。
这是我目前的功能。我使用 localCompare()
来比较例如。 4a
到 4
或 4ä
。我很确定这很慢,但可以做得更好!
sortedFiles = sortClips(files)
sortClips(clips) {
let firstBy=(function(){function e(f){f.thenBy=t;return f}function t(y,x){x=this;return e(function(a,b){return x(a,b)||y(a,b)})}return e})();
let options = {
numeric: true,
sensitivity: 'base',
ignorePunctuation: true
};
clips.sort(
firstBy(function (v1, v2) {
if (!v1.scene) {
return v1.name.localeCompare(v2.name, undefined, options);
}
return v1.scene.localeCompare(v2.scene, undefined, options);
})
.thenBy(function (v1, v2) {
if (!v1.shot) return -1;
return v1.shot.localeCompare(v2.shot, undefined, options);
})
.thenBy(function (v1, v2) {
if (!v1.take) return -1;
return v1.take.localeCompare(v2.take, undefined, options);
})
);
return clips;
},
原始数据(场景、镜头、镜头、名称):
1 1 4 A031C006_170718_R1W0
8 8 4 A020C004_170716_R1W0
1 1 10 A031C013_170718_R1W0
undefined undefined undefined "A001C549_190226_R04Q"
2 2 1 A008C010_170715_R1W0
5 5 1 A015C005_170716_R1W0
3 3 7 A002C003_170714_R1W0
5 5 5 A021C005_170716_R1W0
5 5 9 A024C006_170717_R1W0
1 1 3 A004C006_170714_R1W0
1-b 1 3 A004C007_170718_R1W0*
5 5PU 6 A021C005_170716_R1W0*
Firefox/safari 排序:
8 8 4 A020C004_170716_R1W0
1 1 4 A031C006_170718_R1W0
1 1 10 A031C013_170718_R1W0
undefined undefined undefined A001C549_190226_R04Q
5 5 1 A015C005_170716_R1W0
5 5 9 A024C006_170717_R1W0
3 3 7 A002C003_170714_R1W0
5 5 5 A021C005_170716_R1W0
1 1 3 A004C006_170714_R1W0
2 2 1 A008C010_170715_R1W0
1-b 1 3 A004C007_170718_R1W0*
5 5PU 6 A021C005_170716_R1W0*
Chrome 排序不同:
5 5 1 A015C005_170716_R1W0
5 5 9 A024C006_170717_R1W0
3 3 7 A002C003_170714_R1W0
5 5 5 A021C005_170716_R1W0
1 1 3 A004C006_170714_R1W0
undefined undefined undefined "A001C549_190226_R04Q"
8 8 4 A020C004_170716_R1W0
1 1 4 A031C006_170718_R1W0
1 1 10 A031C013_170718_R1W0
2 2 1 A008C010_170715_R1W0
1-b 1 3 A004C007_170718_R1W0*
5 5PU 6 A021C005_170716_R1W0*
目标:
1 1 3 A004C006_170714_R1W0
1 1 4 A031C006_170718_R1W0
1 1 10 A031C013_170718_R1W0
1-b 1 3 A004C007_170718_R1W0*
2 2 1 A008C010_170715_R1W0
3 3 7 A002C003_170714_R1W0
5 5 1 A015C005_170716_R1W0
5 5 5 A021C005_170716_R1W0
5 5PU 6 A021C005_170716_R1W0*
5 5 9 A024C006_170717_R1W0
8 8 4 A020C004_170716_R1W0
undefined undefined undefined A001C549_190226_R04Q
我该如何排序?
更新:
我已经包含了边缘案例:
场景“1-b”当key为"scene".
[=时,添加的字符“-b”应该排在场景“1”的所有元素之后60=]
Shot "5PU" 当键为"shot"或"take"时,添加的字符"PU"应该被忽略。
您可以取一个键数组进行排序,然后取另一个键,只要比较的结果为零即可。
var options = { numeric: true, sensitivity: 'base', ignorePunctuation: true },
array = [{ scene: "1", shot: "1", take: "4", namw: "A031C006_170718_R1W0" }, { scene: "8", shot: "8", take: "4", name: "A020C004_170716_R1W0" }, { scene: "1", shot: "1", take: "10", name: "A031C013_170718_R1W0" }, { scene: undefined, shot: undefined, take: undefined, name: "A001C549_190226_R04Q" }, { scene: "2", shot: "2", take: "1", name: "A008C010_170715_R1W0" }, { scene: "5", shot: "5", take: "1", name: "A015C005_170716_R1W0" }, { scene: "3", shot: "3", take: "7", name: "A002C003_170714_R1W0" }, { scene: "5", shot: "5", take: "5", name: "A021C005_170716_R1W0" }, { scene: "5", shot: "5", take: "9", name: "A024C006_170717_R1W0" }, { scene: "1", shot: "1", take: "3", name: "A004C006_170714_R1W0" }, { scene: "1-b", shot: "1", take: "3", name: "A004C007_170718_R1W0*" }, { scene: "5", shot: "5PU", take: "6", name: "A021C005_170716_R1W0*" }],
keys = ["scene", "shot", "take", "name"];
array.sort((a, b) => {
var result;
keys.some(k => result = String(a[k]).localeCompare(b[k], undefined, options));
return result;
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
您的排序功能不稳定。
if (!v1.shot) return -1;
如果 v2
也没有 "shot" 属性怎么办?您的排序函数应该始终 return 与 sort(a, b)
和 sort(b, a)
相反的结果,而您的则不然。由于 Chrome 似乎使用了另一种排序顺序,因此仅针对此浏览器失败(到目前为止,算法将来可能会更改)。
这是一个复杂的问题。先是代码,再是解释:
const scenes = [
{"scene": "1", "shot": "1", "take": "4", "name": "A031C006_170718_R1W0"},
{"scene": "8", "shot": "8", "take": "4", "name": "A020C004_170716_R1W0"},
{"scene": "1", "shot": "1", "take": "10", "name": "A031C013_170718_R1W0"},
{"scene": undefined, "shot": undefined, "take": undefined, "name": "A001C549_190226_R04Q"},
{"scene": "2", "shot": "2", "take": "1", "name": "A008C010_170715_R1W0"},
{"scene": "5", "shot": "5", "take": "1", "name": "A015C005_170716_R1W0"},
{"scene": "3", "shot": "3", "take": "7", "name": "A002C003_170714_R1W0"},
{"scene": "5", "shot": "5", "take": "5", "name": "A021C005_170716_R1W0"},
{"scene": "5", "shot": "5", "take": "9", "name": "A024C006_170717_R1W0"},
{"scene": "1", "shot": "1", "take": "3", "name": "A004C006_170714_R1W0"},
]
const compareString = (a, b) => {
if (`${a}` !== a) return 1
if (`${b}` !== b) return -1
return a.localeCompare(b, undefined, {
numeric: true,
sensitivity: 'base',
ignorePunctuation: true
})
}
const sorts = [
'scene',
'shot',
'take',
'name',
]
const globalCompare = (a, b) =>
sorts.reduce((acc, key) => {
if (acc == 0) return compareString(a[key], b[key])
return acc
}, 0)
scenes.sort(globalCompare)
console.log(scenes)
说明:
1- 比较函数有两个参数(传统上是 a
和 b
),return 一个整数,它定义了一个 "before" 另一个。整数如下:1
如果a
应该在b
之前,-1
如果应该在b
之后,0
如果它们相等。
2- 我们需要确保 "second order" 只能是排序 等价的 对象! (这就是我们使用 reduce()
的原因)
我需要为我的 Web 应用程序对较大的数组(1000-2000 个键)进行复杂的排序。我在 Safari 12.0/FF 66.0 上有点用,但 chrome 74.0 似乎完全不一样。
我要排序的序列 - 不区分大小写,自然大小写:
1. "scene"
2. "shot"
3. "take"
4. "name"
这些值中的每一个都可以是字符串(例如 4
、4b
或 4-PU üöä!"
)或 'undefined',并且可能类似于:
[
{"scene": "1", "shot": "1", "take": "4", "name": "A031C006_170718_R1W0"},
{"scene": "8", "shot": "8", "take": "4", "name": "A020C004_170716_R1W0"},
{"scene": "1", "shot": "1", "take": "10", "name": "A031C013_170718_R1W0"},
{"scene": undefined, "shot": undefined, "take": undefined, "name": "A001C549_190226_R04Q"},
{"scene": "2", "shot": "2", "take": "1", "name": "A008C010_170715_R1W0"},
{"scene": "5", "shot": "5", "take": "1", "name": "A015C005_170716_R1W0"},
{"scene": "3", "shot": "3", "take": "7", "name": "A002C003_170714_R1W0"},
{"scene": "5", "shot": "5", "take": "5", "name": "A021C005_170716_R1W0"},
{"scene": "5", "shot": "5", "take": "9", "name": "A024C006_170717_R1W0"},
{"scene": "1", "shot": "1", "take": "3", "name": "A004C006_170714_R1W0"},
{"scene": "1-b", "shot": "1", "take": "3", "name": "A004C007_170718_R1W0*"},
{"scene": "5", "shot": "5PU", "take": "6", "name": "A021C005_170716_R1W0*"},
]
如果键scene
不存在,排序到末尾,按"name"排序。
这是 vue.js 项目的一部分,我希望在没有额外库的情况下完成它。
这是我目前的功能。我使用 localCompare()
来比较例如。 4a
到 4
或 4ä
。我很确定这很慢,但可以做得更好!
sortedFiles = sortClips(files)
sortClips(clips) {
let firstBy=(function(){function e(f){f.thenBy=t;return f}function t(y,x){x=this;return e(function(a,b){return x(a,b)||y(a,b)})}return e})();
let options = {
numeric: true,
sensitivity: 'base',
ignorePunctuation: true
};
clips.sort(
firstBy(function (v1, v2) {
if (!v1.scene) {
return v1.name.localeCompare(v2.name, undefined, options);
}
return v1.scene.localeCompare(v2.scene, undefined, options);
})
.thenBy(function (v1, v2) {
if (!v1.shot) return -1;
return v1.shot.localeCompare(v2.shot, undefined, options);
})
.thenBy(function (v1, v2) {
if (!v1.take) return -1;
return v1.take.localeCompare(v2.take, undefined, options);
})
);
return clips;
},
原始数据(场景、镜头、镜头、名称):
1 1 4 A031C006_170718_R1W0
8 8 4 A020C004_170716_R1W0
1 1 10 A031C013_170718_R1W0
undefined undefined undefined "A001C549_190226_R04Q"
2 2 1 A008C010_170715_R1W0
5 5 1 A015C005_170716_R1W0
3 3 7 A002C003_170714_R1W0
5 5 5 A021C005_170716_R1W0
5 5 9 A024C006_170717_R1W0
1 1 3 A004C006_170714_R1W0
1-b 1 3 A004C007_170718_R1W0*
5 5PU 6 A021C005_170716_R1W0*
Firefox/safari 排序:
8 8 4 A020C004_170716_R1W0
1 1 4 A031C006_170718_R1W0
1 1 10 A031C013_170718_R1W0
undefined undefined undefined A001C549_190226_R04Q
5 5 1 A015C005_170716_R1W0
5 5 9 A024C006_170717_R1W0
3 3 7 A002C003_170714_R1W0
5 5 5 A021C005_170716_R1W0
1 1 3 A004C006_170714_R1W0
2 2 1 A008C010_170715_R1W0
1-b 1 3 A004C007_170718_R1W0*
5 5PU 6 A021C005_170716_R1W0*
Chrome 排序不同:
5 5 1 A015C005_170716_R1W0
5 5 9 A024C006_170717_R1W0
3 3 7 A002C003_170714_R1W0
5 5 5 A021C005_170716_R1W0
1 1 3 A004C006_170714_R1W0
undefined undefined undefined "A001C549_190226_R04Q"
8 8 4 A020C004_170716_R1W0
1 1 4 A031C006_170718_R1W0
1 1 10 A031C013_170718_R1W0
2 2 1 A008C010_170715_R1W0
1-b 1 3 A004C007_170718_R1W0*
5 5PU 6 A021C005_170716_R1W0*
目标:
1 1 3 A004C006_170714_R1W0
1 1 4 A031C006_170718_R1W0
1 1 10 A031C013_170718_R1W0
1-b 1 3 A004C007_170718_R1W0*
2 2 1 A008C010_170715_R1W0
3 3 7 A002C003_170714_R1W0
5 5 1 A015C005_170716_R1W0
5 5 5 A021C005_170716_R1W0
5 5PU 6 A021C005_170716_R1W0*
5 5 9 A024C006_170717_R1W0
8 8 4 A020C004_170716_R1W0
undefined undefined undefined A001C549_190226_R04Q
我该如何排序?
更新:
我已经包含了边缘案例:
场景“1-b”当key为"scene".
[=时,添加的字符“-b”应该排在场景“1”的所有元素之后60=]Shot "5PU" 当键为"shot"或"take"时,添加的字符"PU"应该被忽略。
您可以取一个键数组进行排序,然后取另一个键,只要比较的结果为零即可。
var options = { numeric: true, sensitivity: 'base', ignorePunctuation: true },
array = [{ scene: "1", shot: "1", take: "4", namw: "A031C006_170718_R1W0" }, { scene: "8", shot: "8", take: "4", name: "A020C004_170716_R1W0" }, { scene: "1", shot: "1", take: "10", name: "A031C013_170718_R1W0" }, { scene: undefined, shot: undefined, take: undefined, name: "A001C549_190226_R04Q" }, { scene: "2", shot: "2", take: "1", name: "A008C010_170715_R1W0" }, { scene: "5", shot: "5", take: "1", name: "A015C005_170716_R1W0" }, { scene: "3", shot: "3", take: "7", name: "A002C003_170714_R1W0" }, { scene: "5", shot: "5", take: "5", name: "A021C005_170716_R1W0" }, { scene: "5", shot: "5", take: "9", name: "A024C006_170717_R1W0" }, { scene: "1", shot: "1", take: "3", name: "A004C006_170714_R1W0" }, { scene: "1-b", shot: "1", take: "3", name: "A004C007_170718_R1W0*" }, { scene: "5", shot: "5PU", take: "6", name: "A021C005_170716_R1W0*" }],
keys = ["scene", "shot", "take", "name"];
array.sort((a, b) => {
var result;
keys.some(k => result = String(a[k]).localeCompare(b[k], undefined, options));
return result;
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
您的排序功能不稳定。
if (!v1.shot) return -1;
如果 v2
也没有 "shot" 属性怎么办?您的排序函数应该始终 return 与 sort(a, b)
和 sort(b, a)
相反的结果,而您的则不然。由于 Chrome 似乎使用了另一种排序顺序,因此仅针对此浏览器失败(到目前为止,算法将来可能会更改)。
这是一个复杂的问题。先是代码,再是解释:
const scenes = [
{"scene": "1", "shot": "1", "take": "4", "name": "A031C006_170718_R1W0"},
{"scene": "8", "shot": "8", "take": "4", "name": "A020C004_170716_R1W0"},
{"scene": "1", "shot": "1", "take": "10", "name": "A031C013_170718_R1W0"},
{"scene": undefined, "shot": undefined, "take": undefined, "name": "A001C549_190226_R04Q"},
{"scene": "2", "shot": "2", "take": "1", "name": "A008C010_170715_R1W0"},
{"scene": "5", "shot": "5", "take": "1", "name": "A015C005_170716_R1W0"},
{"scene": "3", "shot": "3", "take": "7", "name": "A002C003_170714_R1W0"},
{"scene": "5", "shot": "5", "take": "5", "name": "A021C005_170716_R1W0"},
{"scene": "5", "shot": "5", "take": "9", "name": "A024C006_170717_R1W0"},
{"scene": "1", "shot": "1", "take": "3", "name": "A004C006_170714_R1W0"},
]
const compareString = (a, b) => {
if (`${a}` !== a) return 1
if (`${b}` !== b) return -1
return a.localeCompare(b, undefined, {
numeric: true,
sensitivity: 'base',
ignorePunctuation: true
})
}
const sorts = [
'scene',
'shot',
'take',
'name',
]
const globalCompare = (a, b) =>
sorts.reduce((acc, key) => {
if (acc == 0) return compareString(a[key], b[key])
return acc
}, 0)
scenes.sort(globalCompare)
console.log(scenes)
说明:
1- 比较函数有两个参数(传统上是 a
和 b
),return 一个整数,它定义了一个 "before" 另一个。整数如下:1
如果a
应该在b
之前,-1
如果应该在b
之后,0
如果它们相等。
2- 我们需要确保 "second order" 只能是排序 等价的 对象! (这就是我们使用 reduce()
的原因)