13

i got the following object

{
  "20170007": {
    "id": 1
  },
  "20170008": {
    "id" : 2
  },
  "20170009": {
   "id": 3
  },
  "20170010": {
    "id": 4
  }
}

desired output:

{
  "20170010": {
    "id": 4
  },
  "20170009": {
   "id": 3
  },
  "20170008": {
    "id" : 2
  },
  "20170007": {
    "id": 1
  }
}

my attempt:

const obj = {
  "20170007": {
    "id": 1
  },
  "20170008": {
    "id" : 2
  },
  "20170009": {
   "id": 3
  },
  "20170010": {
    "id": 4
  }
}

const reverseObj = (obj) => {
  let newObj = {}

  Object.keys(obj)
    .sort()
    .reverse()
    .forEach((key) => {
      console.log(key)
      newObj[key] = obj[key]
    })

  console.log(newObj)
  return newObj  
}

reverseObj(obj)

the wierd part is that when i console.log the key inside the forEach, the keys are reversed. but when i assign the key to the newObj the output is still in the original order... whats going on here ?

EDIT:

thanks for all the responses i took a look into The Map object. Map

new Map([iterable])

Wich was actually i was looking for and the order is guaranteed.

3
  • Objects do not guarantee the order in which keys are stored, unlike arrays. You should read this: stackoverflow.com/questions/5525795/… Commented Aug 10, 2017 at 10:45
  • If you want order ...use an array Commented Aug 10, 2017 at 10:45
  • how did you do it with the map? I'm literally having this problem now Commented Jun 25, 2020 at 3:57

2 Answers 2

5

The elements of an object do not have a reliable order

Different browsers or environments will order them differently. Sometimes by order of insertion, sometimes by lexicographical order of key, and potentially any way it feels like. The JavaScript language definition does not enforce a behaviour, so even if you see it ordered the way you want when you try it, it might behave differently later or for a different user.

If you want them to have a reliable order, you should store the elements in an array.

const obj = {
  "20170007": {
    "id": 1
  },
  "20170008": {
    "id" : 2
  },
  "20170009": {
   "id": 3
  },
  "20170010": {
    "id": 4
  }
}

const arrayReverseObj = (obj) => {
  let newArray = []

  Object.keys(obj)
    .sort()
    .reverse()
    .forEach(key => {
      console.log(key)
      newArray.push( {
      'key':key, 
      'id':obj[key].id
      })
    })

  console.log(newArray)
  return newArray  
}

arrayReverseObj(obj)

The result is similar to what you want, but is an array (so that the elements can have an order):

[{
 "key": "20170010",
 "id": 4
},
{
 "key": "20170009",
 "id": 3
},
{
 "key": "20170008",
 "id": 2
},
{
 "key": "20170007",
 "id": 1
}]

Being more concise, using .map

Instead of exactly mirroring your code layout, we could use `.map()` as indicated in the comment below. An example would be:
const arrayReverseObj = 
  obj => Object.keys(obj).sort().reverse().map(key=> ({key:key,id:obj[key].id}) );

If you want the whole of each sub-object, not just the `id` property

As pointed out in the further comment below,
const arrayReverseObj = 
  obj => Object.keys(obj).sort().reverse().map(key=> ({...obj[key],key:key}) );

This returns an array of objects, in reverse-sorted order of their key, and with each object having an extra property key containing their key.

In general, for any object o, the {... o } construction is creating a new object, composed of all the properties of o, i.e. a kind of duplicate of o. {... o, foo:"bar" } does the same, but adds a new property foo with value "bar".

Therefore we are unbundling ("destructuring") the original sub-object, merging in a new property key, and then rebundling it into a new object which is like the original, but with the extra property.

This achieves the same as Object.assign but is declarative (functional) rather than imperative (procedural), which makes it easier to read.

A further shorthand also suggested by a commenter is that when you are building an object with an expression like { a: 1, b: "hello", c: c }, in the case of the property c whose value is being set to a variable that is also called c, you can just skip the : c, and Javascript will assume you mean c: c. Therefore it saves space to write { a: 1, b: "hello", c }.

So our statement would become:

const arrayReverseObj = 
  obj => Object.keys(obj).sort().reverse().map(key=> ({ ...obj[key], key }) );
Sign up to request clarification or add additional context in comments.

5 Comments

I think you're looking for map.
I'm looking at a JSON response that contains an object listing keys (relating to page elements) in the order that they appear on the page. How is this not an ordered object?
If you need an array with objects whose order is the reverse of the original keys you don't need sort. Also, if you don't want only the id but the entire original object together with the key, you could do const arrayReverseObj = (obj) => Object.keys(obj).reverse().map(key => ({ ...obj[key], key, }));
@GiorgioTempesta, yes you are right if all you want is to reverse the order in which the items appear on that system. However that will vary between systems. I was guessing he wanted to have it in reverse date order rather than reverse version of whatever the interpreter happened to give him, therefore I sorted and reversed.
Great question @ESR! Caught me out too, in the past. It will always appear in some sort of order, but the problem is that the order can vary by machine and browser in unhelpful ways. I've revised the answer to clarify, thanks
1

An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function. A function stored in a property of an object is called a method.

ECMA specification

Since objects are unordered, you should use an array instead.

2 Comments

Is there some reason you are quoting a spec that's nearly two decades old?
@torazaburo Probably because the latest spec edition doesn't contain that specific wording any more

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.