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

12
  • That isn’t possible with objects. Properties don’t have a reliable order. Commented Oct 24, 2018 at 2:14
  • You can never guarantee the order of properties when printing an object. Object properties just don't have order. You have to use array when you need order. Commented Oct 24, 2018 at 2:15
  • "Sorting an object" makes no sense: you can create an array with key/value pairs and sort that: objects are unordered. Commented Oct 24, 2018 at 2:15
  • 1
    I partially disagree with the previous comments. In modern browsers and ES spec, the order of objects is almost always preserved. One of the few times they're not, however, is when the keys are integers. Commented Oct 24, 2018 at 2:16
  • 2
    If you're using ES6, you can use Map, it respects insertion order Commented Oct 24, 2018 at 2:16

3 Answers 3

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;
}, {});
Sign up to request clarification or add additional context in comments.

Comments

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

2 Comments

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., 2ality.com/2015/10/property-traversal-order-es6.html
@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?".
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))

1 Comment

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.