爲了保證的可讀性,本文采用意譯而非直譯。
一般情況咱們排序大都按數字或字母順序,但也有一些情況下,咱們可能需要自定義排序順序。
在此之前先簡單介紹一下 reduce 方法:
語法:arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
callback:執行數組中每個值的函數,包含四個參數:
accumulator:累計器累計回調的返回值; 它是上一次調用回調時返回的累積值,或initialValue(見於下方)。
currentValue:數組中正在處理的元素。
currentIndex (可選):數組中正在處理的當前元素的索引。如果提供了initialValue,則起始索引號爲0,否則爲1。
array(可選): 調用 reduce() 的數組
initialValue(可選):作爲第一次調用 callback 函數時的第一個參數的值。如果沒有提供初始值,則將使用數組中的第一個元素。在沒有初始值的空數組上調用 reduce 將報錯。
rudeuce 過程描述:
回調函數第一次執行時,accumulator 和currentValue的取值有兩種情況:如果調用reduce()時提供了initialValue,accumulator取值爲initialValue,currentValue取數組中的第一個值;如果沒有提供 initialValue,那麼accumulator取數組中的第一個值,currentValue取數組中的第二個值。
回到原文:
如下面的例子所示,咱們想讓 inProgress 在第一位,接着是 todo,然後是 done。
const tasks = [
{id:1, title: 'Job A', status: 'done'},
{id:2, title: 'Job B', status: 'inProgress'},
{id:3, title: 'Job C', status: 'todo'},
{id:4, title: 'Job D', status: 'inProgress'},
{id:5, title: 'Job E', status: 'todo'}
]
首先按照所需的排序順序創建一個數組。
const sortBy = ['inProgress', 'todo', 'done']
使用reduce來創建一個函數,參數爲一個數組,最後輸出以數組項爲鍵,索引爲值,如 {inProgress:0,todo:1,done:2}。
const sortByObject = data => data.reduce(
(obj,item,index) => ({
...obj,
[item]:index
}), {}
)
console.log(sortByObject(sortBy))
/* {inProgress: 0, todo: 1, done: 2} */
這樣就有了排序設置,咱們可以將它與一個可重用的函數放在一起,該函數傳入一個數組(data)、一個sortby數組和一個sortField,這樣咱們就知道要在哪個字段上排序:
const customSort = ({data, sortBy, sortField}) => {
const sortByObject = sortBy.reduce(
(obj, item, index) => ({
...obj,
[item]: index
}), {})
return data.sort((a, b) => sortByObject[a[sortField]] - sortByObject[b[sortField]])
}
console.log(customSort({data:tasks, sortBy, sortField: 'status'}))
這樣就可以按照咱們的自定義順序排序,不過還有一個問題,如果列表中有一個status不同的項(不在咱們的排序順序中),就會出現問題。因此,爲了處理這個問題,咱們需要設置一個默認的sort字段來捕獲排序中不需要的所有項。
const tasksWithDefault = tasks.map(item => (
{
...item,
sortStatus: sortBy.includes(item.status) ? item.status:'other'
})
)
這次傳遞的是更新後的sort字段,那麼現在就有了正確的排序順序,列表底部還有包含狀態爲 other 的項目。
完整代碼:
const tasks = [
{ id: 1, title: "Job A", status: "done" },
{ id: 2, title: "Job B", status: "inProgress" },
{ id: 3, title: "Job C", status: "todo" },
{ id: 3, title: "Job D", status: "onHold" },
{ id: 4, title: "Job E", status: "inProgress" },
{ id: 5, title: "Job F", status: "todo" }
];
const sortBy = ["inProgress", "todo", "done"];
const customSort = ({ data, sortBy, sortField }) => {
const sortByObject = sortBy.reduce(
(obj, item, index) => ({
...obj,
[item]: index
}),
{}
);
return data.sort(
(a, b) => sortByObject[a[sortField]] - sortByObject[b[sortField]]
);
};
const tasksWithDefault = tasks.map(item => ({
...item,
sortStatus: sortBy.includes(item.status) ? item.status : "other"
}));
console.log(
customSort({
data: tasksWithDefault,
sortBy: [...sortBy, "other"],
sortField: "sortStatus"
})
);
運行結果: