JS: 按类别筛选对象数组中的最大值

7

如何实现类似SQL筛选的高效/优雅方式。 我想过滤它们并仅获取某些组中最大值的对象。

这是我的代码,它能运行但可能不是最佳方式:

uniqueValues = (arr) => [...new Set(arr)];
getMaxTimeOf = (arr) => Math.max(...arr.map(o => o.timeStamp), 0);
selectorName = (name) => (obj) => obj.name === name;
selectorTime = (time) => (obj) => obj.timeStamp === time;
getGroup = (obj, selector) => obj.filter(selector)

onlyLastChangedFrom = (history) => {
const uniqueNames = uniqueValues(history.map(o => o.name))
let filtered = []
 uniqueNames.forEach(name => {
  const group = getGroup(history, selectorName(name))
  const groupLastTime = getMaxTimeOf(group)
  const lastChange = getGroup(group, selectorTime(groupLastTime))
  filtered.push(lastChange[0])
 });
 return filtered
}   
onlyLastChangedFrom(history)

    // Input:
    [ { name: 'bathroom',
        value: 54,
        timeStamp: 1562318089713 },
      { name: 'bathroom',
        value: 55,
        timeStamp: 1562318090807 },
      { name: 'bedroom',
        value: 48,
        timeStamp: 1562318092084 },
      { name: 'bedroom',
        value: 49,
        timeStamp: 1562318092223 },
      { name: 'room',
        value: 41,
        timeStamp: 1562318093467 } ]

    // Output:
    [ { name: 'bathroom',
        value: 55,
        timeStamp: 1562318090807 },
      { name: 'bedroom',
        value: 49,
        timeStamp: 1562318092223 },
      { name: 'room',
        value: 41,
        timeStamp: 1562318093467 } ]
8个回答

13

使用name属性作为键,将数组Reduce为一个对象。对于每个项目,检查累加器中存在的项目是否比当前项目具有更高的值,如果没有,则用当前项目替换它。使用Object.values()转换回数组:

const arr = [{"name":"bathroom","value":54,"timeStamp":1562318089713},{"name":"bathroom","value":55,"timeStamp":1562318090807},{"name":"bedroom","value":48,"timeStamp":1562318092084},{"name":"bedroom","value":49,"timeStamp":1562318092223},{"name":"room","value":41,"timeStamp":1562318093467}]

const result = Object.values(arr.reduce((r, o) => {
  r[o.name] = (r[o.name] && r[o.name].value > o.value) ? r[o.name] : o

  return r
}, {}))

console.log(result)


1
不,我们需要将 r[o.name] 分配为三元运算的结果 r[o.name] && r[o.name].value > o.value ? r[o.name] : o - Ori Drori
1
我看到 "r[o.name] = r[o.name] && r[o.name].value > o.value ? r[o.name] : o" 应该是 "r[o.name] = (r[o.name] && r[o.name].value > o.value) ? r[o.name] : o"。 - Cuban coffee

2
我喜欢在这种情况下使用 lodash。它非常实用,因此非常清晰和直观。
请看以下代码:
const DATA = [
  {
    name: "bathroom",
    value: 54,
    timeStamp: 1562318089713
  },
  {
    name: "bathroom",
    value: 55,
    timeStamp: 1562318090807
  },
  {
    name: "bedroom",
    value: 48,
    timeStamp: 1562318092084
  },
  {
    name: "bedroom",
    value: 49,
    timeStamp: 1562318092223
  },
  {
    name: "room",
    value: 41,
    timeStamp: 1562318093467
  }
];

let max = _
  .chain(DATA)
  .groupBy('name')
  .sortBy('value')
  .map(o => _(o).reverse().first())
  .flatten()
  .value();

console.log(max); // returns [{"name":"bathroom","value":55,"timeStamp":1562318090807},{"name":"bedroom","value":49,"timeStamp":1562318092223},{"name":"room","value":41,"timeStamp":1562318093467}]

链式调用 lodash 函数在使用 treeshaking 时与 lodash es 不兼容。如果想要在 es 导入中使用链式调用函数,请参考 GitHub 上的答案:https://github.com/lodash/lodash/issues/3298#issuecomment-341685354。 - rdhainaut

1
这是另一种reduce的替代方法:

var arr = [{"name":"bathroom","value":54,"timeStamp":1562318089713},{"name":"bathroom","value":55,"timeStamp":1562318090807},{"name":"bedroom","value":48,"timeStamp":1562318092084},{"name":"bedroom","value":49,"timeStamp":1562318092223},{"name":"room","value":41,"timeStamp":1562318093467}];

var obj = arr.reduce((r, o) => (o.value < (r[o.name] || {}).value || (r[o.name] = o), r), {});

console.log( Object.values(obj) );


1

如何实现类似SQL的过滤效果最有效/优雅的方法。

您可以为每个步骤编写函数,并将所有函数连接起来以获得单个结果。

例如,在SQL中,您将拥有以下查询:

SELECT name, value, MAX(timeStamp) 
FROM data 
GROUP BY name;

使用类似SQL的方法,你可以先进行分组,然后从结果集中取出最大的对象。
result = pipe(
    groupBy('name'),
    select(max('timeStamp'))
)(data);

const
    pipe = (...functions) => input => functions.reduce((acc, fn) => fn(acc), input),
    groupBy = key => array => array.reduce((r, o) => {
        var temp = r.find(([p]) => o[key] === p[key])
        if (temp) temp.push(o);
        else r.push([o]);
        return r;
    }, []),
    max = key => array => array.reduce((a, b) => a[key] > b[key] ? a : b),
    select = fn => array => array.map(fn);


var data = [{ name: 'bathroom', value: 54, timeStamp: 1562318089713 }, { name: 'bathroom', value: 55, timeStamp: 1562318090807 }, { name: 'bedroom', value: 48, timeStamp: 1562318092084 }, { name: 'bedroom', value: 49, timeStamp: 1562318092223 }, { name: 'room', value: 41, timeStamp: 1562318093467 }],
    result = pipe(
        groupBy('name'),
        select(max('timeStamp'))
    )(data);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }


0
您可以使用.reduce(),通过保留一个积累的对象来保持当前找到的最大组,并使用Object.values()来获取这些对象的数组(而不是键值对关系)。
请参见以下示例:

const arr=[{name:"bathroom",value:54,timeStamp:1562318089713},{name:"bathroom",value:55,timeStamp:1562318090807},{name:"bedroom",value:48,timeStamp:1562318092084},{name:"bedroom",value:49,timeStamp:1562318092223},{name:"room",value:41,timeStamp:1562318093467}];

const res = Object.values(arr.reduce((acc, o) => {
  acc[o.name] = acc[o.name] || o;
  if (o.value > acc[o.name].value)
    acc[o.name] = o;
  return acc;
}, {}));

console.log(res);


0

分阶段来完成这个。

  1. 获取一组名称
  2. 创建一个按降序排列的数组
  3. 然后只需使用 find 函数映射以获取第一个

以下是一个示例。

const input = [{"name":"bathroom","value":54,"timeStamp":1562318089713},{"name":"bathroom","value":55,"timeStamp":1562318090807},{"name":"bedroom","value":48,"timeStamp":1562318092084},{"name":"bedroom","value":49,"timeStamp":1562318092223},{"name":"room","value":41,"timeStamp":1562318093467}];

const output = [...new Set(input.map(m => m.name))].
  map(m => [...input].sort(
    (a,b) => b.value - a.value).
    find(x => m === x.name));
  
console.log(output);


0

您可以使用排序和筛选方法

const arr = [{"name":"bathroom","value":54,"timeStamp":1562318089713},{"name":"bathroom","value":55,"timeStamp":1562318090807},{"name":"bedroom","value":48,"timeStamp":1562318092084},{"name":"bedroom","value":49,"timeStamp":1562318092223},{"name":"room","value":41,"timeStamp":1562318093467}]
arr.sort(function (a, b) {
    return b.value - a.value;
}).filter((v, i, a) => a.findIndex((v2) => v2.name === v.name) === i);

console.log(arr);


0
使用map()foreach()来获得所需的输出。
const arr=[{name:"bathroom",value:54,timeStamp:1562318089713}, 
    {name:"bathroom",value:55,timeStamp:1562318090807}, 
    {name:"bedroom",value:48,timeStamp:1562318092084}, 
    {name:"bedroom",value:49,timeStamp:1562318092223}, 
    {name:"room",value:41,timeStamp:1562318093467}];

let res = new Map();
arr.forEach((obj) => {
    let values = res.get(obj.name);
    if(!(values && values.value > obj.value)){ 
        res.set(obj.name, obj) 
    }
})
console.log(res);
console.log([...res])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接