DEV Community

Aykut Akgün
Aykut Akgün

Posted on • Updated on

[Weekly Js Perf] Fastest way to initialize a Map with initial values

Welcome to Weekly JavaScript Performance, Here we explore weekly JavaScript-related performance tips and tricks so that you write better and faster apps!

The topic today: Performance of initializing Maps, and what the best method is to do so.

When working with bulk amounts of data it's important to initialize and use the data in the right way to ensure a fast and effective method of work.

We compare the following situations:
Item Initialization: 1, 100, 10000 from an Object. It's important to keep in mind that the way, you store your data also has a big impact.

Setup Code:

// Set the number of iterations
const sampleSize = 1

// object to store the samples
const samples = {};

for (let i = 0; i < sampleSize; i++) {
  samples[crypto.randomUUID()] = crypto.randomUUID();
}
Enter fullscreen mode Exit fullscreen mode

Benchmark 1 (Initializing in constructor):

// Get an array of the keys in the samples object
const keys = Object.keys(samples)

// Create an empty array to store the key-value pairs as two-element arrays [Required by Map]
const initial = []
for (let i = 0; i<keys.length;i++) {
    initial.push([keys[i], samples[keys[i]]])
}
// initial schema: [key, value][]
const map = new Map(initial)
Enter fullscreen mode Exit fullscreen mode

Benchmark 2 (Initializing in constructor via Object.entries):

new Map(Object.entries(samples))
Enter fullscreen mode Exit fullscreen mode

Benchmark 3 (Initializing it via Map.set):

// Get an array of the keys in the samples object
const keys = Object.keys(samples)
// create a new empty Map
const map = new Map()
for (let i = 0; i<keys.length;i++) {
    // assign properties via set
    map.set(keys[i], samples[keys[i]])
}
Enter fullscreen mode Exit fullscreen mode

So now how is the performance?
sampleSize: 1
Benchmark 1: 25812739 ops / S (33.83% slower) ±1.99%
Benchmark 2: 10358422 ops / S (73.4% slower) ±1.32%
Benchmark 3: 39010393 ops / S (fastest) ±1.57%
sampleSize: 100
Benchmark 1: 197028 ops / S (18.51% slower) ±2.21%
Benchmark 2: 74668 ops / S (69.13% slower) ±1.69% nice²
Benchmark 3: 39010393 ops / S (fastest) ±1.36%
sampleSize: 10_000
Benchmark 1: 680 ops / S (18.51% slower) ±15.66%
Benchmark 1: 419 ops / S (51.6% slower) ±1.19%
Benchmark 3: 812 ops / S (fastest) ±12.13%

So what do we learn from this?
Method 1 Requires us to create additional arrays so we can store them in the required Schema: [key, value][] is slower.
Method 2 Makes the step in Method 1 automatically but is much slower, but extremely consistent.
Method 3 is the fastest and recommended Method.
Jsbench Link to Benchmark

But what If the Data is already prepared in the correct schema?

Setup Code:

// Set the number of iterations
const iterations = 1

// object to store the samples
const samples = {};

for (let i = 0; i < iterations; i++) {
  samples[crypto.randomUUID()] = crypto.randomUUID();
}

// Get an array of the keys in the samples object
const keys = Object.keys(samples)

// Create an empty array to store the key-value pairs as two-element arrays [Required by Map]
const initial = []
for (let i = 0; i<keys.length;i++) {
    initial.push([keys[i], samples[keys[i]]])
}
Enter fullscreen mode Exit fullscreen mode

Benchmark 1 & 2 (Initializing in constructor):

const map = new Map(initial)
Enter fullscreen mode Exit fullscreen mode

Benchmark 3 (Initializing it via Map.set):

const map = new Map()
for (let i = 0; i<keys.length;i++) {
    map.set(keys[i], samples[keys[i]])
}
Enter fullscreen mode Exit fullscreen mode

So now how is the performance when the Data is prepared?
sampleSize: 1
Benchmark 1: 43308604 ops / S (8.19% slower) ±1.69% nice
Benchmark 3: 47173798 ops / S (fastest) ±1.57%
sampleSize: 100
Benchmark 1: 424558 ops / S (fastest) ±2.07%
Benchmark 3: 399689 ops / S (5.86% slower) ±1.43%
sampleSize: 10_000
Benchmark 1: 2213 ops / S (fastest) ±1.83%
Benchmark 3: 2037 ops / S (7.98 % slower) ±0.73%

This is surprising! If we have more than 1 entry to set (2 is already 11% slower), then using the entries method is always faster!
Jsbench Link to Benchmark

Conclusion:
If you have the entries already perfectly set up, then I recommend you to use the initializer to set up your Map.
But if you have it in any other form (even if you can choose how you want to save the Data) I strongly recommend you to use From our First test the Benchmark 3 Method (via Object.keys then for loop). The reason why it's usually slower is that when we use functions like Object.entries, they tend to create a lot of arrays that only hold key, value data. So If you for example have to load a JSON to convert them to Maps, It's much better to store them as an Object and then use the Method described Above. (Also it will cost you much less Memory, all of those arrays take a lot of Memory!)

TLTR: Use set in every case (including the way to save and load the Data) with object.keys to create the Maps, unless you need the Data in a format like Object.entries()

Keep in mind that results may vary from browser to browser, tests have been conducted with Chrome, run the benchmarks yourself! (Results may vary depending on CPU Too, on intel Firefox, object.entries is faster (by 5% on 10k))

Top comments (1)

Collapse
 
khayal0 profile image
xeyal

Great examples, thanks!