我有一个包含负数、空字符串''
、零0
和正数的数组。我想对数组进行排序,以便空字符串''
始终显示在末尾。
[-1, 2, '', -2, 0, 4, '', 1,5,0, -5, 7, 11, '',3]
和预期输出
[-5, -2, -1, 0, 0, 1, 2, 3, 4, 5, 7, 11, '', '', '']
您可以在排序条件中优先比较空字符串。
let arr = [-1, 2, '', -2, 0, 4, '', 1,5,0, -5, 7, 11, '',3];
arr.sort((a, b) => (a === '') - (b === '') || a - b);
console.log(arr);
您可以编写一个自定义排序函数,以便将空字符串移动到数组的后面[...arr]
如果您不想改变原始数组,因为排序会改变原始数组。
const arr = [-1, 2, '', -2, 0, 4, '', 1,5,0, -5, 7, 11, '',3];
const sorted = [...arr].sort((a, b) => {
if (a === '') return 1; //a after b
if (b === '') return -1; //b after a
return a - b; //number sorting
});
//or shorten using ternary operator
//const sorted = [...arr].sort((a, b) => a === '' ? 1 : b === '' ? -1 : a - b);
console.log(sorted);
如果b
是空的,把它放在前面;但如果a
是空的,把它放在最后。
如果a
和b
都为非空,则执行标准a - b
返回。
const arr = [-1, 2, '', -2, 0, 4, '', 1, 5, 0, -5, 7, 11, '', 3];
arr.sort((a, b) => {
if (b === '') return -1;
if (a === '') return 1;
return a - b;
});
console.log(JSON.stringify(arr));
如果要始终将空值保留在末尾,则需要跟踪排序方向:
const
DIRECTION_ASC = 1,
DIRECTION_DESC = -1,
customSort = (direction = DIRECTION_ASC) => {
const dir = direction === DIRECTION_DESC ? -1 : 1;
return (a, b) => {
if (b === '') return -1;
if (a === '') return 1;
return (a - b) * dir;
};
};
const
custSortAsc = customSort(),
custSortDesc = customSort(DIRECTION_DESC),
sortTextJsonArr = (textarea, sorter) => {
textarea.value = JSON.stringify(
JSON.parse(textarea.value.replace(/'/g, '"'))
.sort(sorter))
.replace(/"/g, "'")
.replace(/,/g, ', ');
},
sortAsc = (selector) =>
sortTextJsonArr(document.querySelector(selector), custSortAsc),
sortDesc = (selector) =>
sortTextJsonArr(document.querySelector(selector), custSortDesc);
body, textarea { text-align: center; }
<textarea id="data" rows="2" cols="60">
[-1, 2, '', -2, 0, 4, '', 1, 5, 0, -5, 7, 11, '', 3]
</textarea>
<br />
<button onclick="sortAsc('#data')">Sort Ascending</button>
<button onclick="sortDesc('#data')">Sort Descending</button>
我个人会定义一些通用的排序帮助程序,这些帮助程序在各种sort()
场景中都很有用。
让我们先看看答案,不知道助手是做什么的:
import { fallThrough, by, asc, desc, front, back } from './lib/sort-helpers';
const items = [-1, 2, '', -2, 0, 4, '', 1,5,0, -5, 7, 11, '',3];
items.sort(fallThrough(
back(item => typeof item === "string"),
asc,
));
console.log(items);
function solution({ fallThrough, by, asc, desc, front, back, itself }) {
const items = [-1, 2, '', -2, 0, 4, '', 1,5,0, -5, 7, 11, '',3];
items.sort(fallThrough(
back(item => typeof item === "string"),
asc,
));
console.log(items);
}
const SortHelpers = (function () {
const fallThrough = (...comparators) => (a, b) => (
comparators.reduce((diff, comparator) => diff || comparator(a, b), 0)
);
const by = (fn, comparator = asc) => (a, b) => comparator(fn(a), fn(b));
const asc = (a, b) => -(a < b) || +(a > b);
const desc = (a, b) => asc(b, a);
const bool = (fn) => (item) => !!fn(item);
const front = (fn) => by(bool(fn), desc);
const back = (fn) => by(bool(fn), asc);
return { fallThrough, by, asc, desc, front, back };
})();
solution(SortHelpers);
如您所见,此解决方案看起来相对简单。请注意,asc
的第二个排序标准将同时应用于数字和字符串。因此,如果字符串为非空,它们也将按升序排序。
助手
现在,让我们解释一下通用帮助程序(请参阅折叠的代码段)。
我将经常使用比较器一词。比较器是一个接受 2 个参数的函数,然后返回一个数字来告诉sort()
应该如何对 2 个参数进行排序。
负值表示第一个参数应放在第二个参数之前。正值表示第一个参数应放在第二个参数之后。零表示两个参数可以被视为相等。
比较器的一个基本示例可以在asc
的解释中看到。
fallThrough
fallThrough
允许我们传递多个比较器函数。如果第一个比较器说值相等,则调用下一个比较器。如果第二个比较器也说值相等,我们将继续第三个比较器。
这种情况一直持续到其中一个比较器说出哪个值先出现。如果没有更多可用的比较器,则认为这些值相等。
下面是一个不使用其他帮助程序的示例:
const people = [
{ firstName: "John", lastName: "Doe" },
{ firstName: "Jane", lastName: "Doe" },
{ firstName: "Dave", lastName: "Dex" },
];
people.sort(fallThrough(
(personA, personB) => personA.lastName.localeCompare(personA.lastName),
(personA, personB) => personB.firstName.localeCompare(personB.firstName),
));
以上将首先尝试根据姓氏进行排序。如果它们相等,我们按名字排序。
by
by
是一个简单的帮助程序,它接受映射函数和比较器。它将映射函数应用于两个比较器参数。目的主要是减少代码重复。
在上面的例子中,我们可以看到我们访问personA.lastName
和personB.lastName
,这是两个比较项目的相同映射。通过使用by
我们可以简化此示例:
const localeCompare = (a, b) => a.localeCompare(b);
people.sort(fallThrough(
by(person => person.LastName, localeCompare),
by(person => person.firstName, localeCompare),
));
请注意,您不必使用fallThrough
。如果只有一个排序标准,那么以下内容就足够了:
people.sort(by(person => person.LastName, localeCompare));
asc
&desc
asc
和desc
是基于<
(小于)和>
(大于)的比较器。
asc
的定义是:
const asc = (a, b) => -(a < b) || +(a > b);
如果你还是 JavaScript 的新手,这可能很难阅读。它也可以写成:
function asc(a, b) {
if (a < b) return -1; // a before b
if (a > b) return 1; // a after b
return 0; // equal
}
更神秘的版本使用-(a < b)
,其计算结果为-(true)
,而又计算为-1
。由于-1
是真值||
短路并返回-1
。如果第一个表达式不为 true,则它的计算结果为-(false)
,这将产生-0
。由于-0
是一个虚假值,我们将转到||
的第二个操作数。在这里,如果a > b
为 true,我们将执行相同的操作,则+(true)
将导致返回值1
。如果a > b
为假(即值相等),则+(false)
将返回0
。
请注意,将字符串与<
和>
进行比较时,将根据字符的代码点值进行比较。
desc
与asc
相同,但通过交换比较器参数产生相反的顺序。
front
back
front
和back
用于简化排序中的布尔逻辑。
by(item => typeof item === "string", asc)
有点难以阅读。琴弦放在前面还是后面?让我们考虑一下。typeof item === "string"
生成true
或false
值。true
会胁迫1
,false
成0
。我们按升序排序,因此我们从小到高排序。1
大于0
,意味着字符串放在后面。
在阅读代码时,您不想进行这种思考。因此,我提供了front
和back
帮助者。这些期望您通过测试并使上述内容更具可读性。
back(item => typeof item === "string")
在此版本中,您可以立即看到字符串位于数组的后面。