用sort实现orderby

工做到了这个年数, 感受那些基本函数语法已经跟人合一了, 根本不会为操做一些数据结构而思考半天了. 在作小程序的时候遇到了个orderby的场景, 结果发现没有觉得的那么简单. 也许是以前不求甚解的缘由, 那么如今来解决orderby的问题.数据库

问题的产生与探讨方向

在小程序中有个将list的某一条置顶的需求, 在初始化数据到时候可使用数据库的orderby, 但在更新数据之后再从新初始化就显得有些不妥, 因此我尝试直接使用computed列表来解决这个问题.小程序

因此如今的问题是: 输入list, 输出orderby置顶字段.数组

以前觉得的sort很简单, 我就尝试了: arr.sort(i => i.stick). 字面看起来是根据stick字段来排序. 输出结果一团糟. 仔细思考了下又尝试了别的方法, 仍是失败, 才决定仔细想一下应该如何处理.数据结构

对sort的理解与快速shuffle

先说一下以前对sort的理解.dom

sort接受的参数返回大于0或者小于0. 根据结果来排序.函数

因此有一个快速shuffle数组的方法:code

arr.sort(() => Math.random() - 0.5)

由于函数的返回结果一半是大于0一半是小于0的(不严格, 但以后也认为几率是一半一半). 因此任何输出进行了如此处理, 都会变成一个随机顺序的数组.排序

另一个例子, 对一个数组: [1, 2, 3, 4, 5, 10, 11, 12]进行排序, 若是不传参数排序结果是错的, 由于默认是localCompare. 因此要写成:get

arr.sort((a, b) => a - b)

这样才能获得正确从小到大的排列.string

以上就是我多年以来对sort的全部理解. 因此才会写出上面的: arr.sort(i => i.stick)这样搞笑的东西. 由于理解是有问题的.

sort是如何排序的

由于不知道sort函数获得告终果后是如何排序的. 因此对sort的理解有问题. 而咱们知道reduce就是从头至尾遍历并传递每次计算的结果. sort殊不知道. 因此打出每次的返回值来看一下每次获得返回值后sort作了些什么.

咱们要对不一样数组进行一样的操做, 排序方法是同样的, 先写一下:

const log = (a, b) => {
    console.log(a, b, a - b > 0)
    return a - b
}

开始对不一样数组进行排序: 先来1到5

[1, 2, 3, 4, 5].sort(log)

结果: [1, 2, 3, 4, 5]

2 1 true
3 2 true
4 3 true
5 4 true

尝试: 从5到1

[5, 4, 3, 2, 1].sort(log)

结果: [1, 2, 3, 4, 5]

4 5 false
3 4 false
2 3 false
1 2 false

目前看来, sort应该是插入排序.

[3, 5, 7, 9, 2, 1, 6].sort(log)

看log的时候我把当前排序结果也打一下:

5 3 true [3, 5]
7 5 true [3, 5, 7]
9 7 true [3, 5, 7, 9]
2 9 false // 2仍是与当前最大的9比.结果第一次false
2 7 false // 因而一路比下来
2 5 false
2 3 false // 比到最小的, 因而肯定了位置 [2, 3, 5, 7, 9]
1 5 false // 1选择了与5比, 此时5是中间位置的数, 而不是最大的数
1 3 false // 而后一个一个比较下来
1 2 false [1, 2, 3, 5, 7, 9]
6 5 true // 6仍是于5比, 此时5也是中间位置的数
6 9 false // 没有选择与7, 而是与9比了
6 7 false

从这些log能得出一些粗浅的结论:

  1. sort是插入排序
  2. 每次比较的数字会根据两个因素来决定: 分别是以前比较的结果和当前排序的位置

如何实现orderby

首先明确思路:

sort认为每一个元素之间的关系是比大小, 因此咱们须要作的是写出任意两个元素的相对顺序的广泛公式.

先构建一组数据:

let gnrt = () => ({ age: Math.round(Math.random() * 50), height: Math.round(Math.random() * 200) })
let arr = Array.from({length: 10}).map(() => gnrt())

咱们先创建纯数字, 无顺序的orderby来理这个思路.

let orderby = function (arr, ...orders) {
    return arr.sort((a, b) => {
        let res = 0
        for (let order of orders) {
            if (a[order] - b[order] !== 0) {
                res = a[order] - b[order]
                break
            } 
        }
        return res
    })
}

调用orderby(arr, 'height', 'age')就获得了理想的orderby结果了: 根据权重排序, 若是都同样就保持顺序.

#后续#

这个思路清晰之后, 作兼容就容易了:

  1. 若是要指定顺序, 在排序参数里带特征, 例如'height', '-height', 来决定在执行的时候是a - b 仍是b - a.
  2. 若是要指定排序函数(在非数字状况下). 把排序参数改写成兼容function的, 判断是string就执行默认, 是function就调用function便可.

固然, 功能越完善的函数就越复杂, 函数自己只是函数复杂度和业务复杂度交换的做用. 具体实现就不写了.

因此置顶排序如何实现

咱们已经想清楚了orderby的实现, 那么置顶排序是stick这个布尔值字段, 就必须根据我上面说的传函数进去, 而且改写orderby函数.

这样又要多些2个函数, 因此我选择:

[...arr.filter({stick} => stick), ...arr.filter({stick} => !stick)]

搞定.

原文地址

相关文章
相关标签/搜索