从对象数组中选出最大值

选出select为true 并且id最大的一项,返回该label

    evaluate: [{
        id: 1,
        select: true,
        label: '较差'
      },
      {
        id: 2,
        select: true,
        label: '差'
      },
      {
        id: 3,
        select: true,
        label: '一般'
      },
      {
        id: 4,
        select: false,
        label: '满意'
      },
      {
        id: 5,
        select: false,
        label: '十分满意'
      }
    ],
阅读 3.6k
5 个回答

先 filter 再 reduce 需要遍历两次,可以直接在 reduce 里处理掉

const r = evaluate.reduce((prev, curr) => curr.select && curr.id > prev.id ? curr : prev, { id: 0 });

如果原数组有序(按 id 排序),那么可以反向查找,这个直接用 for 循环来完成

function findLastSelect(list) {
    for (let i = list.length - 1; i >= 0; i--) {
        if (list[i].select) {
            return list[i];
        }
    }
}

要在不改变原数组的情况下反向再查找也是可以的,但是不应该使用 filter,它会遍历完,应该使用 find,的到即止。

[...evaluate].reverse().find(({select}) => select)

但是跑下来看性能就知道,反向 for 循环查找最快,因为它假设有序,甚至都不用遍历完就能找到需要的元素。
其实是直接 reduce 的方案,毕竟这个是假设无序的,需要完整遍历。但它比先 filter 再 reduce 快了一倍。
最慢的是 reverse,因为要复制一个数组,再反向,再查找。

image.png

evaluate.reverse().filter(item=>{
    return item.select
})[0]['label']

既然大佬们提到了性能问题
那就朴实无华的双指针咯

function fn(arr){
    if(!Array.isArray(arr) || !arr.length) return ''
    let maxID = -1
    let str = ''
    for(let s=0,e=arr.length-1; s<=e; s++,e--){
        if(arr[s].select && arr[s].id > maxID){
            maxID = arr[s].id
            str = arr[s].label
        }
        if(s !== e && arr[e].select && arr[e].id > maxID){
            maxID = arr[e].id
            str = arr[e].label
        }
    }
    return str
}

image.png

[{
        id: 1,
        select: true,
        label: '较差'
      },
      {
        id: 2,
        select: true,
        label: '差'
      },
      {
        id: 3,
        select: true,
        label: '一般'
      },
      {
        id: 4,
        select: false,
        label: '满意'
      },
      {
        id: 5,
        select: false,
        label: '十分满意'
      }
    ].filter(v => v.select).reduce((prev, cur) => prev.id > cur.id ? prev : cur).label

相当于@然后去远足 的原生js版本

用 lodash 的 maxBy 就很简单。

let maxLabel = _.maxBy(array.filter(e => e.select), 'id').label;

当然你要是不想引入 lodash 也可以自己手写个函数,本质就是个 reduce。

P.S. 注意自己处理一下潜在的空指针问题。

排序即可。

Array.from(evaluate)
    .sort((m, n) => n.select - m.select || n.id - m.id)[0]
    .label;

排序算法内部实现,性能要优于脚本计算。且当前JS运行器都会选用更优的算法实现,例如V8使用的是Timsort算法。可以不考虑性能方面的问题。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题