js 对象数组排序+去重问题

[
  {
    name: '玉骨遥寒薇结海报',
    created_at: '2021-06-04 04:54:06.164',
  },
  {
    name: '玉骨遥寒薇结海报',
    created_at: '2021-06-04 04:52:49.753',
  },
  {
    name: '玉骨遥寒薇结海报',
    created_at: '2021-06-04 05:02:02.398',
  },
  {
    name: '公交车抛锚警民携手推车为考生开路',
    created_at: '2021-06-04 04:52:40.588',
  },
  {
    name: '公交车抛锚警民携手推车为考生开路',
    created_at: '2021-06-04 05:07:21.587',
  },
]

1.去除name相同的对象
2.且保留的是创建时间最早的对象

我只能想到reduce去重对象数组,但是第二步不会了

阅读 6.1k
5 个回答

对于少量数据,用 @linong 的方法就很好。但如果对于数量较大的情况,可能需要建立映射表来加速查找

const r = data
    .reduce(
        (map, it) => {
            // 如果不存在于 map 中,或者需要替换
            if (!map.has(it.name) || map.get(it.name).created_at > it.created_at) {
                map.set(it.name, it);
            }
            return map;
        },
        new Map()
    )
    .values();

考虑到 Map 不能保存顺序,如果需要保留原顺序的话,就使用列表来保存数据,映射表里保存 name => index

const r = data
    .reduce(
        ({ map, list }, it) => {
            const index = map.get(it.name);
            if (index === undefined) {
                map.set(it.name, list.length);
                list.push(it);
            } else if (list[index].created_at > it.created_at) {
                list[index] = it;
            }
            return { map, list };
        },
        { map: new Map(), list: [] }
    )
    .list;

另外,@Chobits 使用 Lodash 的方法也不错,但有一点需要优化:排序之后应该使用 .sortedUniqBy。不过很遗憾 Lodash 中的 uniq 系列方法只提供唯一判断,不提供选择能力,所以必须先排序再取唯一,这样即使 Lodash 有优化效果应该也不如 reduce 一次循环的效率高(猜的,没实测)。

image.png

这是单纯的去重复

image.png

你可以使用排序来完成时间操作。

image.png

当然了,你也可以把重复再去比较时间的逻辑加在 reduce 里面

[
  {
    name: '玉骨遥寒薇结海报',
    created_at: '2021-06-04 04:54:06.164',
  },
  {
    name: '玉骨遥寒薇结海报',
    created_at: '2021-06-04 04:52:49.753',
  },
  {
    name: '玉骨遥寒薇结海报',
    created_at: '2021-06-04 05:02:02.398',
  },
  {
    name: '公交车抛锚警民携手推车为考生开路',
    created_at: '2021-06-04 04:52:40.588',
  },
  {
    name: '公交车抛锚警民携手推车为考生开路',
    created_at: '2021-06-04 05:07:21.587',
  },
].reduce(function(s,v){
    var itemIdx = s.findIndex(v1=>v1.name==v.name);
    if(itemIdx == -1){
        s.push(v)
    }else{
        var item = s[itemIdx];
        if(v.created_at < item.created_at){
            s.splice(itemIdx,1,v)
        }
    }
    
    return s
},[])
Array.from([
  {
    name: '玉骨遥寒薇结海报',
    created_at: '2021-06-04 04:54:06.164',
  },
  {
    name: '玉骨遥寒薇结海报',
    created_at: '2021-06-04 04:52:49.753',
  },
  {
    name: '玉骨遥寒薇结海报',
    created_at: '2021-06-04 05:02:02.398',
  },
  {
    name: '公交车抛锚警民携手推车为考生开路',
    created_at: '2021-06-04 04:52:40.588',
  },
  {
    name: '公交车抛锚警民携手推车为考生开路',
    created_at: '2021-06-04 05:07:21.587',
  },
]
.sort((a,b)=>new Date(b.created_at) - new Date(a.created_at))
.reduce((prev, cur)=>prev.set(cur.name, cur),new Map)
.values())

这个如果最后的结果不考虑时间排序的话,可以考虑一次遍历转换为以name值为key,created_at值为value值的k-v对象集合,这样遍历数组来更新这个对象集合,形成最终的k-v集合,再输出为数组即可。
大致代码逻辑为:

var a2a=function(inArr){
   var rt=[];
   let kv={};
   for(let i=0;i<inArr.length;i++){
       if( (kv[ inArr[i]["name"] ]==undefined) || 
           ( kv[ inArr[i]["name"] ]>inArr[i]["created_at"]) 
         ) {
          kv[ inArr[i]["name"] ] = inArr[i]["created_at"];
       }
   }
   for( akey in kv){
       let tmp={name:akey,created_at:kv[akey]};
       rt.push(tmp);
   }
   return rt;
}
推荐问题
宣传栏