1

I have looked at this question in stack overflow about objects guaranteeing order. Does JavaScript Guarantee Object Property Order?

some says they guarantee, some says they don't, depending on situations. Meantime I've encountered following problem.

I have an array of objects, similar to below:

const arrayObject = [
{id:'a123', bar:'hello'}, 
{id:'a321', bar: 'foo'} ];

now I wish to turn this arrayObject into object of object, with structure as follows with the same order as the array:

const object = {
   'a123': {id:'a123', bar:'hello'},
   'a321': {id:'a321', bar: 'foo'},
}

basically using the id of each item in the array as the key of the object. Below is the code I used to try to achieve it:

let newObj = {};
arrayObject.forEach(data=>{
   const temp = {
     [data.id]:{
        id: data.id,
        bar: data.bar
     },
   };
   newObj={...newObj, ...temp};
})

I do get the correct structure, however the order is not the same as the order of arrayObject, i.e. it returns:

const object = {
   'a321': {id:'a321', bar: 'foo'},
   'a123': {id:'a123', bar:'hello'},
}

I've tried with more items in the array, and I get same result. It does not guarantee the order.

Is there something wrong with my code, or is it simply not guaranteeing the order?

What do I have to do to make the object be the same order as the array?

Eric Ahn
  • 391
  • 2
  • 7
  • 18
  • It is not possible to guarantee the order of an object properties, because it is, in essence, not meant to be ordered... – BJRINT Jan 03 '20 at 16:12
  • Short answer: there's no [defined order](http://stackoverflow.com/questions/30076219/does-es6-introduce-a-well-defined-order-of-enumeration-for-object-properties). Long answer: if you need a specific order which should be the same regardless of the underlying JavaScript engine, than you have to define the order "by hand". In this case you could use the content of `arrayObject`. Iterate of the elements in the array and use the `id` to access the values in `object`/`newObject` – Andreas Jan 03 '20 at 16:16
  • The order of the objects is determined in the memory management made by the operating system. When the JS interpreter runs out of memory it requests a new memory allocation to the system and the latter delivers a portion of memory which can be found anywhere or depending on what it recovers from other tasks. – Mister Jojo Jan 03 '20 at 16:19
  • I have tested your code on Chrome 79 and I can tell `Object.keys(newObj)` always outputs `["a123", "a321"]` – Guerric P Jan 03 '20 at 16:19

3 Answers3

3

Preserve the order by including ordering information in the (unordered) object. Anytime later, when you need to recover the original order, use the saved ordering information...

const arrayObject = [{
    id: 'a123',
    bar: 'hello'
  },
  {
    id: 'a321',
    bar: 'foo'
  }
];

let object = {}
arrayObject.forEach((e, i) => {
  object[e.id] = { ...e, orderWith: i } // the index will tell us how to sort later
})

// later on
let sorted = Object.values(object).sort((a, b) => a.orderWith - b.orderWith)
console.log(sorted)
danh
  • 62,181
  • 10
  • 95
  • 136
  • Since by definition Object are "unordered" I really like this approach – Plastic Jan 03 '20 at 17:37
  • This works didn't think to include extra information to preserve the order thanks! Though a little downside may be that we have to include this extra 'orderWith' that we may not use otherwise and Object.values(object).sort((a, b) => a.orderWith - b.orderWith) doesn't give the structure I wanted (returns array instead of object). Any way to return the structure I have specified? – Eric Ahn Jan 04 '20 at 02:20
  • No. The bottom line is that you can depend on the ordering of an array, and you cannot depend on the object key order. – danh Jan 04 '20 at 02:33
1

I think what you're looking for is a Map() object. See here -> Map and Set

const arrayObject = [
  {id:'a123', bar:'hello'}, 
  {id:'a321', bar: 'foo'},
  {id:'a234', bar: 'more'},
  {id:'a735', bar: 'words'},
  {id:'a167', bar: 'added'},
  {id:'a857', bar: 'now'},
];

var newObj = new Map();

for (var i=0; i<arrayObject.length; i++) {
  const temp = {
        id: arrayObject[i].id,
        bar: arrayObject[i].bar
   };
   newObj.set(arrayObject[i].id, temp);
}

var jsonText = JSON.stringify(Array.from(newObj.entries()));

console.log(jsonText);

// To get at the elements, use .get()

console.log(newObj.get("a321").bar);
thingEvery
  • 3,368
  • 1
  • 19
  • 25
  • This indeed does preserve the order! Though, we have to use getters and setters which is somewhat an overhead?. Thanks for the answer! – Eric Ahn Jan 04 '20 at 02:13
0

A simpler bit of code would be like this (use a for loop instead of forEach:

let newObj = {};
for (const data of arrayObject) {
  newObj[data.id] = data;
}

This might get you what you want because it will guarantee that the order the object is built matches the order in the array. Using forEach causes multiple functions to be called in whatever order they run, which might be out of order. But realize that even using the for-loop does not guarantee the order will always match. An Array will guarantee the order, but the Object made this way does not. Even if the above code does give you the desired order, it might not in the future. Use an Array if you need to preserve order.

Always Learning
  • 5,510
  • 2
  • 17
  • 34
  • tried the code, still does not guarantee the order. As you said 'for-loop does not guarantee the order' I guess. Thanks for the answer though! – Eric Ahn Jan 04 '20 at 02:14