As covered briefly by MDN, Map has a number of technical benefits — while a simple Array or an Object may suffice for simple data structures, oftentimes the increasing complexity of modern web development justifies using something else.
Let's look at a few specific examples:
The keys of a Map can be anything
In both modern libraries as well as webapps, memoization is often used to cut down performance costs of repetitive operations. Briefly, memoization is the act of storing the output of a given function for given inputs after the first run.
Maps are a great candidate for storing this data, since you can use them regardless of what you want to index by.
Show code snippet
const cache = new Map();
const input = [2, 3, 2]; // Assuming this is immutable for simplicity
function expensive(n, ...args) {
if (!args.length) return n;
return Math.pow(n, expensive(...args));
}
function memoExpensive(arr) {
const memoResult = cache.get(arr);
if (memoResult) {
console.log('used memo', memoResult);
return memoResult;
}
const result = expensive(...arr);
console.log('calculated', result);
cache.set(arr, result);
return result;
}
memoExpensive(input);
memoExpensive(input);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
There are cases where you could also use a Symbol and a regular Object, but a Map is more universal in this regard.
Map keys are ordered
For a regular Object, keys are partially ordered (since ECMAScript 2015, before that it was a bit iffy on different engines). Keys keep their order only for string and Symbol keys, while numerical keys are sorted.
When adding keys to an Object in the order c, b, a, 3, 2, 1, the stored order will be 1, 2, 3, c, b, a, for example.
Show code snippet
const obj = {};
obj.c = 1;
obj.b = 2;
obj.a = 3;
obj[3] = 4;
obj[2] = 5;
obj[1] = 6;
console.log(Object.keys(obj));
Run code snippetEdit code snippet Hide Results Copy to answer Expand
A Map will keep the order of the inserted keys regardless of their type.
Show code snippet
const map = new Map();
map.set('c', 1);
map.set('b', 2);
map.set('a', 3);
map.set(3, 4);
map.set(2, 5);
map.set(1, 6);
console.log(Array.from(map.keys()));
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Oftentimes this won't matter, especially in casual web dev, but there are numerous notorious cases when the order of the keys does matter.
Map is iterable
While an Array, as brought up in the question, is also iterable, a Map offers a better use-case for key-value mappings than an Object would. Being iterable has a number of benefits, just-in-time execution and guaranteed order being two examples.
Show code snippet
const map = new Map()
map.set(document.body, 'blue')
map.set(document.documentElement, 'green')
for (let pair of map) {
console.log(pair);
}
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Other benefits
There are numerous other benefits why you might pick a Map over other alternatives, like performance as covered by Asthmatic, avoiding Object prototype issues, convenience etc.
Hopefully this generic overview has covered most questions you had.
Answer from Etheryte on Stack OverflowAs covered briefly by MDN, Map has a number of technical benefits — while a simple Array or an Object may suffice for simple data structures, oftentimes the increasing complexity of modern web development justifies using something else.
Let's look at a few specific examples:
The keys of a Map can be anything
In both modern libraries as well as webapps, memoization is often used to cut down performance costs of repetitive operations. Briefly, memoization is the act of storing the output of a given function for given inputs after the first run.
Maps are a great candidate for storing this data, since you can use them regardless of what you want to index by.
Show code snippet
const cache = new Map();
const input = [2, 3, 2]; // Assuming this is immutable for simplicity
function expensive(n, ...args) {
if (!args.length) return n;
return Math.pow(n, expensive(...args));
}
function memoExpensive(arr) {
const memoResult = cache.get(arr);
if (memoResult) {
console.log('used memo', memoResult);
return memoResult;
}
const result = expensive(...arr);
console.log('calculated', result);
cache.set(arr, result);
return result;
}
memoExpensive(input);
memoExpensive(input);
Run code snippetEdit code snippet Hide Results Copy to answer Expand
There are cases where you could also use a Symbol and a regular Object, but a Map is more universal in this regard.
Map keys are ordered
For a regular Object, keys are partially ordered (since ECMAScript 2015, before that it was a bit iffy on different engines). Keys keep their order only for string and Symbol keys, while numerical keys are sorted.
When adding keys to an Object in the order c, b, a, 3, 2, 1, the stored order will be 1, 2, 3, c, b, a, for example.
Show code snippet
const obj = {};
obj.c = 1;
obj.b = 2;
obj.a = 3;
obj[3] = 4;
obj[2] = 5;
obj[1] = 6;
console.log(Object.keys(obj));
Run code snippetEdit code snippet Hide Results Copy to answer Expand
A Map will keep the order of the inserted keys regardless of their type.
Show code snippet
const map = new Map();
map.set('c', 1);
map.set('b', 2);
map.set('a', 3);
map.set(3, 4);
map.set(2, 5);
map.set(1, 6);
console.log(Array.from(map.keys()));
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Oftentimes this won't matter, especially in casual web dev, but there are numerous notorious cases when the order of the keys does matter.
Map is iterable
While an Array, as brought up in the question, is also iterable, a Map offers a better use-case for key-value mappings than an Object would. Being iterable has a number of benefits, just-in-time execution and guaranteed order being two examples.
Show code snippet
const map = new Map()
map.set(document.body, 'blue')
map.set(document.documentElement, 'green')
for (let pair of map) {
console.log(pair);
}
Run code snippetEdit code snippet Hide Results Copy to answer Expand
Other benefits
There are numerous other benefits why you might pick a Map over other alternatives, like performance as covered by Asthmatic, avoiding Object prototype issues, convenience etc.
Hopefully this generic overview has covered most questions you had.
The V8 engine heap isn't very good at managing arrays of objects. If you have an array of 100,000 objects, loop through each object and delete one parameter in the middle then re-add it, the heap size will spike rather than stay constant.
// Spike the memory heap
arr.forEach(obj => {let temp = obj[0]; delete obj[0]; obj[0] = temp})
The reason the heap will spike is because V8 creates intermediate arrays during the forEach loop over the array. Once the array you're looping over becomes massive (eg. 100,000 elements) the memory spike will be too large to handle and could crash the program.
Iterables like Map and Set aren't handled by the engine this way, and you can use a library like iter-tools to get array-like transformation methods for them.
Javascript performance of Array.map - Stack Overflow
Performance of array includes vs mapping to an Object and accessing it in JavaScript - Stack Overflow
javascript - What is faster? Map get() or Array find()? - Stack Overflow
Are maps faster than arrays?
Well first of all, it's not a fair comparison. As you said, the proper javascript map is able to use objects, not just arrays. So you are basically comparing two completely different functions with different algorithms/results/inner workings.
Of course the proper javascript map is slower - it is designed to work over a larger domain than a simple for over an array.
Just want to share some findings that I thought was relevant here. I had some issues with .map taking forever. Switching to hash map cut the speed tremendously.
We had a map function across 200k small objects, converting that map to a hashed object and recreating the array took it from 20min to 0.4s.
First approach (20min):
const newArr = arr1.map((obj) => {
const context1 = arr2.find(o => o.id === obj.id)
const context2 = arr3.find(o => o.id === obj.id)
return { ...obj, context1, context2 }
})
New Approach
const newArrObj = {}
arr1.forEach(o => newArrObj[o.id] = o)
arr2.forEach(o => newArrObj[o.id].context1 = o)
arr3.forEach(o => newArrObj[o.id].context2 = o)
const users = []
arr1.forEach(o => users[users.length] = newArrObj[o.id])
It's true that object lookup occurs in constant time - O(1) - so using object properties instead of an array is one option, but if you're just trying to check whether a value is included in a collection, it would be more appropriate to use a Set, which is a (generally unordered) collection of values, which can also be looked up in linear time. (Using a plain object instead would require you to have values in addition to your keys, which you don't care about - so, use a Set instead.)
const set = new Set(['foo', 'bar']);
console.log(set.has('foo'));
console.log(set.has('baz'));
This will be useful when you have to look up multiple values for the same Set. But, adding items to the Set (just like adding properties to an object) is O(N), so if you're just going to look up a single value, once, there's no benefit to this nor the object technique, and you may as well just use an array includes test.
Updated 04/29/2020
As the commenter rightly pointed out it would seem V8 was optimizing out the array includes calls. An updated version that assigns to a var and uses it produces more expected results. In that case Object address is fastest, followed by Set has and in a distant third is Array includes (on my system / browser).
All the same, I do stand by my original point, that if making micro-optimizations it is worth testing assumptions. Just make sure your tests are valid ;)
Original
Well. Despite the obvious expectation that Object address and Set has should outperform Array includes, benchmarks against Chrome indicate that implementation trumps expectation.
In the benches I ran against Chrome Array includes was far and away the best performer.
I also tested locally with Node and got more expected results. In that Object address wins, followed closely by Set has, then Array includes was marginally slower than both.
Bottom line is, if you're making micro-optimizations (not recommending that) it's worth benchmarking rather than assuming which might be best for your particular case. Ultimately it comes down to the implementation, as your question implies. So optimizing for the target platform is key.
Here's the results I got:
Node (12.6.0):
ops for Object address 7804199
ops for Array includes 5200197
ops for Set has 7178483
Chrome (75.0):
https://jsbench.me/myjyq4ixs1/1

For the purpose of finding an item.
Is it better to get an array element at x index.
Or is it better to get a map element at x key.
For example let's say you have characters each with health and mana.
Would it be better to have 2 maps, each with their names and values. Or to have 2 arrays with just the values?
Ignore structs and datatables, this is just for the purpose of the question.
TLDR: are maps faster and more efficient than arrays for their purpose, or are they just more practical.
edit: reason why I'd think they are not is cuz maps store a key and a value. Whereas arrays store only list of values. But maybe how they work behind the scenes makes maps somehow better at what they are supposed to be used for.