0

In order to better understand how to manipulate objects, I'm trying to sort an object based on its values.

eg.

let obj = {
    1: 5,
    2: 4,
    3: 200,
    4: 3,
    5: 1
}

and I want an output of

{
    3: 200,
    1: 5,
    2: 4,
    4: 3,
    5: 1
}

I've looped through the object and added its key value pairs into a new array, which I then sort by the values.

the new array looks like this

[
    [3, 200],
    [1, 5],
    [2, 4],
    [4, 3],
    [5, 1]
]

I then loop through this array and create a new object from it, but it's coming out as

{ 1: 5, 2: 4, 3: 200, 4: 3, 5: 1 }

why?

let obj = {
  1: 5,
  2: 4,
  3: 200,
  4: 3,
  5: 1
}

const sortObj = o => {
  const arr = [];
  const _obj = {};

  for (const v in o) arr.push([v, o[v]]);

  arr.sort((a, b) => b[1] - a[1]);

  for (const v of arr) {
    console.log(v);
    /*
    [
      [3, 200],
      [1, 5],
      [2, 4],
      [4, 3],
      [5, 1]
    ]
    */
    const [key, val] = v;
    console.log(key, val);
    _obj[key] = val;
  }

  return _obj;
}

console.log(sortObj(obj));
totalnoob
  • 2,521
  • 8
  • 35
  • 69

3 Answers3

1

As of ES2015, your objects keys will almost always preserve their order of insertion. However, one of the few notable exceptions is when your keys are integers.

The example below shows that the sort works fine when we use string keys instead...

let obj = {
  test: 5,
  this: 4,
  out: 200,
  and: 3,
  see: 1
}

const sortObj = o => {
  const arr = [];
  const _obj = {};

  for (const v in o) arr.push([v, o[v]]);

  arr.sort((a, b) => b[1] - a[1]);

  for (const v of arr) {
    const [key, val] = v;
    _obj[key] = val;
  }

  return _obj;
}

console.log(sortObj(obj));

Alternatively, use a Map instead, which always preserves order.

let obj = {
  1: 5,
  2: 4,
  3: 200,
  4: 3,
  5: 1
}

const sortObj = o => {
  const arr = [];
  const _obj = new Map();

  for (const v in o) arr.push([v, o[v]]);

  arr.sort((a, b) => b[1] - a[1]);

  for (const v of arr) {
    const [key, val] = v;
    _obj.set(key, val);
  }

  return _obj;
}

var sortedMap = sortObj(obj);
sortedMap.forEach((val, key) => console.log(`${key}: ${val}`));
Tyler Roper
  • 21,445
  • 6
  • 33
  • 56
  • Symbols are also treated differently. IMO it makes zero sense to rely on insertion order across arbitrary keys: treat an object as it was originally intended, an unordered collection of key/values. If you need order, enforce it in other ways. E.g., http://2ality.com/2015/10/property-traversal-order-es6.html – Dave Newton Oct 24 '18 at 14:55
  • @DaveNewton Correct - and I agree! I'd provided the alternative solution of using `Map` for this reason. The first point stands to explain the "Why is this not working", whereas hopefully the second part tackles the "What should I be doing instead?". – Tyler Roper Oct 24 '18 at 14:56
1

Well, there is already a valid answer above but ill post it since i already started to play around with the ordering and have a bit shorter version.

Related reading: Does JavaScript Guarantee Object Property Order?

let obj = {
    1: 5,
    2: 4,
    3: 200,
    4: 3,
    5: 1
}
sortedObj = Object.keys(obj).sort((key1, key2) => {
    if (obj[key1] > obj[key2]) {return -1;}
    if (obj[key1] < obj[key2]) {return 1;}
    return 0;
}).reduce((acc, key) => {
    acc['i_'+key] = obj[key];
    return acc;
}, {});
Rainer Plumer
  • 3,693
  • 2
  • 24
  • 42
0

Use Map if you can, it'll preserve order as mentioned in my comment above. The bonus of using Map, your keys won't get converted to strings, it preserves the type too.

let obj = {
  1: 5,
  2: 4,
  3: 200,
  4: 3,
  5: 1
}

const sortObj = o => {
  const arr = [];
  const map = new Map();

  for (const v in o) arr.push([v, o[v]]);

  arr.sort((a, b) => b[1] - a[1]);

  for (const v of arr) {
    console.log(v);
    /*
    [
      [3, 200],
      [1, 5],
      [2, 4],
      [4, 3],
      [5, 1]
    ]
    */
    const [key, val] = v;
    //console.log(key, val);
    map.set(key,val);
  }

  return map;
}
sortObj(obj).forEach((k,v)=>console.log(v,k))
JohanP
  • 5,252
  • 2
  • 24
  • 34
  • While you're correct that `Map` preserves type, the keys will turn to string values in this example. If you do `var obj = { ... }; console.log(obj)`, you'll see that the keys have already been converted to strings, therefore they're added to the `Map` as strings :) – Tyler Roper Oct 24 '18 at 04:01