温馨提示:本文翻译自stackoverflow.com,查看原文请点击:javascript - Sorting an array of objects but prioritising certain items

javascript - 排序对象数组但优先处理某些项目

发布于 2020-03-31 23:26:33

这是一个示例数据:

[
  {
    "name": "test",
    "is_folder": "Y",
    "is_file": "N",
    "sort": 0,
    "sort_reverse": 1
  },
  {
    "name": "1.jpg",
    "is_folder": "N",
    "is_file": "Y",
    "sort": 1,
    "sort_reverse": 0
  }
]

想法是,文件夹将始终位于列表的顶部,然后是文件,但我也希望对文件夹和文件进行排序。

结果示例:

升序(默认):

  • 资料夹1
  • 资料夹2
  • 资料夹3
  • 1.jpg
  • 2.jpg
  • 3.jpg

降序(反向):

  • 资料夹3
  • 资料夹2
  • 资料夹1
  • 3.jpg
  • 2.jpg
  • 1.jpg

如何通过示例数据实现这一目标?

到目前为止,这里是我对名称进行排序的内容,但没有考虑文件夹/文件的顺序:

items.sort((a: any, b: any) => {
    if (a.name > b.name) {
        return 1;
    } else if (a.name < b.name) {
        return -1;
    } else {
        return 0;
    }
});

查看更多

提问者
J.Do
被浏览
20
VLAZ 2020-01-31 23:54

您可以简单地执行以下操作:

  • 如果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)

在TypeScript Playground上查看(包括类型)

使用localeCompare与数字整理选项可以确保数字将被正确排序,其中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)

如您所见,我们基本上重复了所有代码以进行升序/降序排序。这使维护变得更加困难,但是我们可以改进它。我们可以将每个部分提取到一个单独的函数中:

  • 排序相似项目的名称-升序
  • 排序相似项目的名称-降序
  • 在文件之前对文件夹进行排序

幸运的是,对于的升/降,翻转和的顺序ab乘以相同,-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最后,使用helper 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));

在TypeScript Playground上查看(包括类型)