JS: Filter array of objects by max value per category
Solution 1:
Reduce the array to an object, using the name
property as the key. For each item, check if the item that exists in the accumulator has a higher value than the current item, and if not replace it with the current item. Convert back to an array with 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)
Solution 2:
I love to use lodash for stuff like this. It's very functional and therefore very clear and straightforward.
Take a look at the following code:
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}]
Solution 3:
Here is another reduce alternative :
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) );
Solution 4:
What is most efficient / elegant way to achieve sql-like filtering effect.
You could take functions for every step and pipe all functions for a single result.
For example in SQL, you would have the following query:
SELECT name, value, MAX(timeStamp)
FROM data
GROUP BY name;
With an SQL like approach, you could group first and take the max object out of the result sets.
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; }