JS: Filter array of objects by max value per category

后端 未结 7 1488
北荒
北荒 2020-12-19 18:21

What is most efficient / elegant way to achieve sql-like filtering effect. I want to filter them and get only that objects which are max value in some group.

This is

相关标签:
7条回答
  • 2020-12-19 18:30

    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)

    0 讨论(0)
  • 2020-12-19 18:30

    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) );

    0 讨论(0)
  • 2020-12-19 18:31

    Doing this in stages.

    1. get a set of names
    2. create a sorted array in descending order
    3. then just map using find to get the first one

    Below is an example.

    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 讨论(0)
  • 2020-12-19 18:33

    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; }

    0 讨论(0)
  • 2020-12-19 18:43

    You can use .reduce() by keeping an accumulated object which keeps the max group currently found and then use Object.values() to get an array of those objects (instead of an key-value pair object relationship).

    See example below:

    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 讨论(0)
  • 2020-12-19 18:48

    Use map() and foreach() to get desired output

    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])
    
    0 讨论(0)
提交回复
热议问题