Skip to main content

Command Palette

Search for a command to run...

How to Preserve Key Insertion Order in JavaScript

Updated
•5 min read•View as Markdown
How to Preserve Key Insertion Order in JavaScript

TL;DR: JavaScript objects do not guarantee insertion order when using integer keys. The ES6 specification forces integer-like keys (array indices) to the front of the object, sorted numerically. To maintain exact insertion order across all key types, you should use a JavaScript Map or restructure your data payload as an array.

We have all been there. You write some JavaScript code, build an object, populate it with some keys, and expect them to come back in the exact order you put them in. For the most part, they do.

But then integers enter the picture, and your carefully ordered data gets completely scrambled.

In JavaScript, integer keys are the ultimate line-cutters. They do not care when they were added; they jump straight to the front of the queue, sort themselves in ascending order, and leave your string keys trailing behind. Let's look at why this happens and how it can break your applications.

Why do JavaScript object keys change order?

JavaScript object keys change order because the ECMAScript specification mandates that keys behaving as integer indices must be sorted numerically and placed at the beginning of the key iteration order. Other string and symbol keys are then appended in the order they were created.

This behavior is defined by the OrdinaryOwnPropertyKeys internal algorithm introduced in ES6. When you call methods like Object.keys(), Object.entries(), or use a for...in loop, the engine groups and orders keys into three distinct phases:

  1. Integer Properties: Keys that can be parsed as a 32-bit unsigned integer (plain whole numbers from 0 up to 4,294,967,294). These are sorted in ascending numerical order and placed first.
  2. String Properties: Standard string keys (including floats, negative numbers, and alphabetic strings). These are preserved in chronological insertion order and placed second.
  3. Symbol Properties: Symbol keys are grouped last, also preserving insertion order.

Here is a quick demonstration of this sorting behavior in action:

const obj = {};
obj['b'] = 'first string';
obj['2'] = 'second integer';
obj['1'] = 'first integer';
obj['-1'] = 'negative integer';

console.log(Object.keys(obj));
// Output: ['1', '2', 'b', '-1']

Notice how '1' and '2' immediately jumped to the front and sorted themselves, while 'b' and '-1' (which is not a valid array index) stayed in their original insertion order.

How does this behavior affect API data fetching?

When a server sends a JSON payload where the top-level keys are database IDs (like 300, then 100, then 200), parsing this payload in JavaScript automatically reorders the keys numerically (100, 200, 300). This destroys any intentional ordering, such as ranking or chronological sorting, applied by your backend.

Imagine you are building a dashboard that displays a leaderboard. The backend does the heavy lifting of sorting the users and sends back a JSON response keyed by user ID:

{
  "301": { "name": "Alice", "score": 95 },
  "102": { "name": "Bob", "score": 88 },
  "205": { "name": "Charlie", "score": 74 }
}

The moment you run JSON.parse() on this response in your frontend code, JavaScript reconstructs it as a standard object. Because the keys are integer-like, the engine instantly resort-sorts them to 102, 205, 301. Your carefully calculated leaderboard sequence is completely broken before you even render a single component.

How can you preserve insertion order in JavaScript?

To guarantee that your data keeps its exact sequence, you should either return an array of objects from your API or use a JavaScript Map on the frontend. Unlike plain objects, the Map object preserves the insertion order of all keys, regardless of their type.

Depending on where you are in the stack, you have a few ways to tackle this issue:

Data Structure Preserves Integer Key Order? Best Use Case
Plain Object ({}) No (forced to front & sorted) General key-value lookups where order is irrelevant
ES6 Map Yes (strict insertion order) Frontend state where key-value pairs require strict ordering
Array of Objects ([]) Yes (strict array index order) API payloads and lists transferred over the network

If you have control over the backend, the most resilient fix is to stop keying your collections by ID at the root level. Send an array instead:

[
  { "id": 301, "name": "Alice" },
  { "id": 102, "name": "Bob" }
]

If you must handle sorted keys purely on the client side, read the data into an ES6 Map instead of a plain object. Maps are guaranteed to respect insertion order for all keys—whether they are integers, strings, or symbols.

FAQ

Are float keys or negative numbers sorted numerically in JavaScript objects?

No. Floating-point numbers (such as 1.5) and negative integers (such as -5) are not valid 32-bit unsigned integers, meaning they cannot function as array indices. JavaScript treats them as standard string keys, so they will preserve their chronological insertion order.

Does JSON.parse() preserve key order?

No, JSON.parse() does not preserve key order if your keys are integers. Because it produces a standard JavaScript object, the JS engine applies the same ES6 spec rules, forcing all integer-like keys to the front in numeric order.

When should I choose a Map over a standard Object?

You should choose a Map when key insertion order is critical to your application logic, when you need keys that are not strings (like objects or functions), or when you are frequently adding and removing entries, as Map is highly optimized for frequent writes.