3
\$\begingroup\$

I'm using the matomo reports api to pull out some data in js. For some charts, I'm in the need to sort the array data based on inner property value ("val"), further to get only the 3 highest (Hong Kooong, Munich, Tokyo). Finally, this data should be used for the creation of a new key/val object (newObj).

This is what my code looks like what does what it should. I would be interested if it can be optimized using es6 syntax.

    let data = {
      0 : {"label":"New York", "val": 20},
      1 : {"label":"Rio", "val": 10},
      3 : {"label":"Tokyo", "val": 50},
      4 : {"label":"Santiago", "val": 20},
      5 : {"label":"Hong Kooong", "val": 100},
      6 : {"label":"Munich", "val": 90},
    }
    
    let compare = (a, b)  => {
      let valA = a.val;
      let valB = b.val;
      let c = 0;
    
      if (valA > valB) {
        c = 1;
      } else if (valA < valB) {
        c = -1;
      }
      return c * -1;
    }
    
    let dataArray = Object.values(data).sort(compare)
    dataArray = dataArray.slice(0, 3)
    
    let newLabel = []
    let newValue = []
    Object.keys(dataArray).map(function(key, index) {
      newLabel.push(dataArray[index]['label'])
      newValue.push(dataArray[index]['val'])
    });
    
    let newObj = {}
    newObj.data = {}
    newObj.data.labels = newLabel
    newObj.data.values = newValue
    
    console.log(newObj)

\$\endgroup\$
5
\$\begingroup\$

Let's start with how I would have done it.

const data = {
  0 : {"label":"New York", "val": 20},
  1 : {"label":"Rio", "val": 10},
  3 : {"label":"Tokyo", "val": 50},
  4 : {"label":"Santiago", "val": 20},
  5 : {"label":"Hong Kooong", "val": 100},
  6 : {"label":"Munich", "val": 90},
}

const result = Object.values(data)
  .sort((a, b) => b.val - a.val)
  .slice(0, 3)
  .reduce((c, v) => {
    c.data.labels.push(v.label)
    c.data.values.push(v.val)
    return c
  }, { data: { labels: [], values: []}})
  
console.log(result)

Now onto your code:

let dataArray = Object.values(data).sort(compare)
dataArray = dataArray.slice(0, 3)

You were off to a good start here. One minor nitpick is to just chain the operations since you're just reassigning dataArray with sort's results.

let newLabel = []
let newValue = []
Object.keys(dataArray).map(function(key, index) {
  newLabel.push(dataArray[index]['label'])
  newValue.push(dataArray[index]['val'])
});

let newObj = {}
newObj.data = {}
newObj.data.labels = newLabel
newObj.data.values = newValue

This is where it went crazy.

Firstly, you used Object.keys() on dataArray (an array), which is not needed. If you need the array item index, all of the array methods provide the item index as second argument of the callback.

Also, this is a bad use of array.map(). array.map() is used to transform values in one array to another array of values, i.e. a 1:1 transform operation. In this case, you're merely using it for looping, something better suited for array.forEach().

Since you're "reducing"/"aggregating" an array of values into one value (turning an array of values into an object), array.reduce() is the better option.

\$\endgroup\$
  • \$\begingroup\$ great answer. learned a lot, thanks! \$\endgroup\$ – t Book Aug 20 at 6:48
4
\$\begingroup\$

Your compare function can be simplified a bit using Math.sign:

let compare = (a, b)  => {
  return -Math.sign(a.val - b.val);
}

sign will return one of -1, 0, or 1, which correspond to the sign of the number passed to it. This is basically what you were doing manually. Most languages have a sign (or signum) function for cases like this.

I also used a unary prefix - to negate instead of using * -1. As an alternative, you could change it to just:

let compare = (a, b)  => {
  return Math.sign(b.val - a.val);  // a and b were swapped
}

Which will have the same effect. It depends on what looks more readable to you.


I'll leave it off there as someone else went in depth.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.