您可以简单地执行以下操作:
- 如果
is_folder 属性相等,则它们都是文件或都是文件夹。比较他们的名字
- 否则排序
is_folder === "Y"更高
升序:
const arr = [
{ "name": "Folder 3", "is_folder": "Y" },
{ "name": "2.jpg", "is_folder": "N" },
{ "name": "Folder 2", "is_folder": "Y" },
{ "name": "Folder 10", "is_folder": "Y" },
{ "name": "Folder 1", "is_folder": "Y" },
{ "name": "15.jpg", "is_folder": "N" },
{ "name": "3.jpg", "is_folder": "N" },
{ "name": "abc.txt", "is_folder": "N" },
{ "name": "1.jpg", "is_folder": "N" },
];
arr.sort((a, b) => {
if (a.is_folder === b.is_folder)
return a.name.localeCompare(b.name, undefined, {numeric: true});
if (a.is_folder === "Y")
return -1;
if (b.is_folder === "Y")
return 1;
return 0;
});
console.log(arr)
降序:
const arr = [
{ "name": "Folder 3", "is_folder": "Y" },
{ "name": "2.jpg", "is_folder": "N" },
{ "name": "Folder 2", "is_folder": "Y" },
{ "name": "Folder 10", "is_folder": "Y" },
{ "name": "Folder 1", "is_folder": "Y" },
{ "name": "15.jpg", "is_folder": "N" },
{ "name": "3.jpg", "is_folder": "N" },
{ "name": "abc.txt", "is_folder": "N" },
{ "name": "1.jpg", "is_folder": "N" },
];
arr.sort((a, b) => {
if (a.is_folder === b.is_folder)
return b.name.localeCompare(a.name, undefined, {numeric: true}); //<-- flip `a` and `b`
if (a.is_folder === "Y")
return -1;
if (b.is_folder === "Y")
return 1;
return 0;
});
console.log(arr)
See on TypeScript Playground (including types)
使用localeCompare with the numeric collation option 可确保正确排序数字,例如10 在 2 之后。如果您不使用它会发生以下情况:
const arr = [
{ "name": "Folder 3", "is_folder": "Y" },
{ "name": "2.jpg", "is_folder": "N" },
{ "name": "Folder 2", "is_folder": "Y" },
{ "name": "Folder 10", "is_folder": "Y" },
{ "name": "Folder 1", "is_folder": "Y" },
{ "name": "15.jpg", "is_folder": "N" },
{ "name": "3.jpg", "is_folder": "N" },
{ "name": "abc.txt", "is_folder": "N" },
{ "name": "1.jpg", "is_folder": "N" },
];
arr.sort((a, b) => {
if (a.is_folder === b.is_folder)
return a.name.localeCompare(b.name); //<-- no numeric collation
if (a.is_folder === "Y")
return -1;
if (b.is_folder === "Y")
return 1;
});
console.log(arr)
如您所见,我们基本上重复所有代码来进行升序/降序排序。这使得维护它变得更加困难,但是我们可以改进它。我们可以将每个部分提取到一个单独的函数中:
- 对相似项目的名称进行排序 - 升序
- 对相似项目的名称进行排序 - 降序
- 在文件之前对文件夹进行排序
幸运的是,对于升序/降序翻转a 和b 的顺序与乘以-1 相同,因为localeCompare 返回一个数字 - 正数、负数或零。所以,我们只能有一次逻辑,不能重复两次:
const compareFoldersFirst = (a, b) => {
if (a.is_folder === "Y")
return -1;
if (b.is_folder === "Y")
return 1;
return 0;
}
const compareNameAsc = (a, b) => {
if (a.is_folder === b.is_folder)
return a.name.localeCompare(b.name, undefined, {numeric: true});
return 0;
}
const compareNameDesc = (a, b) => compareNameAsc(a, b) * -1;
我们实际上可以概括compareNameDesc 中使用的逻辑——它只是运行一个带有两个参数的函数并将其乘以-1,因此我们可以创建一个通用的reverse 函数,它可以反转任何排序顺序:
const reverse = compareFn => (a, b) => compareFn(a, b) * -1;
const compareNameDesc = reverse(compareNameAsc);
此外,我们可以稍微改变每个比较的逻辑以使其完全自给自足,因为现在名称排序取决于某物是否为文件夹。
const compareFoldersFirst = (a, b) => {
if (a.is_folder === b.is_folder)
return 0;
if (a.is_folder === "Y")
return -1;
if (b.is_folder === "Y")
return 1;
};
这甚至更短,用布尔和数字转换规则的轻微......“创造性用法”来表达:
const compareFoldersFirst = (a, b) => Number(b.is_folder === "Y") - Number(a.is_folder === "Y");
无论如何,这允许我们从名称比较中删除is_folder 检查,我们只剩下以下比较器:
const compareFoldersFirst = (a, b) => Number(b.is_folder === "Y") - Number(a.is_folder === "Y");
const compareNameAsc = (a, b) => a.name.localeCompare(b.name, undefined, {numeric: true});
const compareNameDesc = reverse(compareNameAsc);
我们几乎拥有所有我们需要的工具,通过添加更多逻辑一次然后将其反转或不反转来生成我们想要的任何排序顺序。我们只需要能够轻松组合不同的比较器。为此,我们可以将排序概括如下:我们得到任意数量的排序函数。我们生成一个新函数,它将一个一个地运行它们,直到一个返回非零结果,此时我们返回它。这可以这样做
const comparer = (...comparers) =>
(a, b) => {
for(let compareFn of comparers){
const result = compareFn(a, b);
if (result !== 0)
return result;
}
}
但可以使用Array#reduce 使其更紧凑。最后代码更容易维护和组合,使用帮助器compare函数:
const arr = [
{ "name": "Folder 3", "is_folder": "Y" },
{ "name": "2.jpg", "is_folder": "N" },
{ "name": "Folder 2", "is_folder": "Y" },
{ "name": "Folder 10", "is_folder": "Y" },
{ "name": "Folder 1", "is_folder": "Y" },
{ "name": "15.jpg", "is_folder": "N" },
{ "name": "3.jpg", "is_folder": "N" },
{ "name": "abc.txt", "is_folder": "N" },
{ "name": "1.jpg", "is_folder": "N" },
];
//helper function that takes any amount of compare functions
//produces a function that runs each until a non-zero result
const compare = (...comparers) =>
(a, b) => comparers.reduce(
(result, compareFn) => result || compareFn(a, b),
0
);
//reverse the result of any compare function after it runs:
const reverse = compareFn => (a, b) => compareFn (a, b) * -1;
//the basic comparer functions:
const compareFoldersFirst = (a, b) => Number(b.is_folder === "Y") - Number(a.is_folder === "Y");
const compareNameAsc = (a, b) => a.name.localeCompare(b.name, undefined, {numeric: true});
const compareNameDesc = reverse(compareNameAsc);
//final comparison function derived form the basic ones
const asc = compare(
compareFoldersFirst,
compareNameAsc
);
const desc = compare(
compareFoldersFirst,
compareNameDesc
);
console.log("--- Ascending sort ---\n", arr.sort(asc));
console.log("--- Descending sort ---\n", arr.sort(desc));
See on TypeScript Playground (including types)