2

I have an object newData where I assign properties to it like so:

newData['5'] = 'test 1';
newData['3'] = 'test 2';
newData['8'] = 'test 3';

I get:

{
    '3': 'test 2',
    '5': 'test 1',
    '8': 'test 3'
}

But I want to maintain insertion order of properties, such that this is what I get:

{
    '5': 'test 1',
    '3': 'test 2',
    '8': 'test 3'
}

Where 5, 3, and 8 stay in the order of insertion. JavaScript objects should not have an order, but it obviously does... and this is severely messing up the loop.

I have to have keys and since JavaScript arrays don't support custom keys, this needs to be an object. How do I add to an object and keep it unordered as it is supposed to be?

Andrew Li
  • 55,805
  • 14
  • 125
  • 143

2 Answers2

4

Object properties do have a certain order, differing from insertion order. It's described in the ECMAScript 2015 Language Specification:

9.1.12 [[OwnPropertyKeys]] ( )

When the [[OwnPropertyKeys]] internal method of O is called the following steps are taken:

  1. Let keys be a new empty List.
  2. For each own property key P of O that is an integer index, in ascending numeric index order
    a. Add P as the last element of keys.
  3. For each own property key P of O that is a String but is not an integer index, in property creation order
    a. Add P as the last element of keys.
  4. For each own property key P of O that is a Symbol, in property creation order
    a. Add P as the last element of keys.
  5. Return keys.

In the above, the properties are defined as the List keys. Then, when a property that is an integer is encountered, it is added to List keys in ascending numeric order, thus leading to 3, 5, 8 you observe.

If you want ordered keys and value pairs that respects order based on insertion, you must use something that respects order, such as an ES6 Map. Per the documentation:

A Map object iterates its elements in insertion order — a for...of loop returns an array of [key, value] for each iteration.

Here's an example:

const obj = new Map([
  [5, "test 1"],
  [3, "test 2"],
  [8, "test 3"]
]);

for(let [key, value] of obj) {
  console.log(key, value);
}

First, you pass an array of arrays to the constructor. The Map constructor takes in an iterable, and uses the subarrays as key and value pairs. The Map essentially looks like this:

+-----+----------+
| Key | Value    |
+-----+----------+
|  5  | "test 1" |
|  3  | "test 2" |
|  8  | "test 3" |
+-----+----------+

Next you use a for...of loop to iterate over the Map. As noted in the documentation, an array of [key, value] is returned by the iterator so you can use array destructuring. Thus, key and value hold the values of the keys and values of the Map respectively, and logging them provides the desired, ordered result.

Community
  • 1
  • 1
Andrew Li
  • 55,805
  • 14
  • 125
  • 143
0

You can have a look at Map. It respects traversal based on insertion order.

JohanP
  • 5,252
  • 2
  • 24
  • 34