4
\$\begingroup\$

Given an object with unsorted numeric values, such as:

const unsorted = {
  green: 80,
  blue: 90,
  red: 30,
  yellow: 100,
}

We want a data structure with the same key/value pairs sorted in descending order of the values, e.g.:

const sorted = {
  yellow: 100,
  blue: 90,
  green: 80,
  red: 30,
}

My solution:

function sortObjectByValue (obj) {
  const sorted = Object.keys(obj)
    .sort((a, b) => obj[b] - obj[a])
    .reduce((acc, cur) => {
      acc[cur] = obj[cur]
      return acc
    }, {})

  return new Map(Object.entries(sorted))
}

What I hope could be improved:

  • Two iterations, first sort then reduce; this seems inefficient.
  • Using Map. I wish we could just use objects, it would make using this elsewhere much easier. I doubt this can be avoided though, as we need to ensure that the order is preserved.
\$\endgroup\$
4
\$\begingroup\$

Here's my proposal:

function sortObjectByValue (obj) {
  const map = new Map()

  Object.entries(obj)
    .sort((a, b) => obj[b][1] - obj[a][1])
    .forEach(([key, value]) => {
      map.set(key, value)
    });

  return map;
}

You don't need to create a sorted object unless you want to use it, I understand that you are using a Map so that you can preserve the order of the elements, but keep in mind this:

Note: Since ECMAScript 2015, objects do preserve creation order for string and Symbol keys. In JavaScript engines that comply with the ECMAScript 2015 spec, iterating over an object with only string keys will yield the keys in order of insertion.

Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

So with that in mind we can skip the Map and we have something like this:

function sortObjectByValue2 (obj) {
  return Object.keys(obj)
    .sort((a, b) => obj[b] - obj[a])
    .reduce((acc, cur) => {
      acc[cur] = obj[cur]
      return acc
    }, {})
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Great that we can skip the Map! Thanks for pointing that out. Otherwise you think that a two-step sort + reduce is the most efficient way? \$\endgroup\$ – Alacritas Jul 4 at 18:17
  • 2
    \$\begingroup\$ Yeah, 1 pass to convert object to array, 1 pass to sort, 1 pass to convert back to object. You may also be able to use the Object.fromEntries to do the conversion of sorted key-value pairs array to object, i.e. Object.fromEntries(Object.entries(object).sort(/* sort function */)), but it's still the same number of passes over the data. \$\endgroup\$ – Drew Reese Jul 4 at 21:37
  • \$\begingroup\$ OK, that's what I was afraid of. Well at least we got rid of the Map (disclaimer: nothing against Map, just unneeded here). \$\endgroup\$ – Alacritas Jul 5 at 19:07

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.