50

Plain object keys must be strings, whereas a Map can have keys of any type.

But I have little use for this in practice. In nearly all cases, I find myself using strings as keys anyway. And presumably new Map() is slower than {}. So is there any other reason why it might be better to use a Map instead of a plain object?

callum
  • 10,377
  • 9
  • 30
  • 33
  • 7
    [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Objects_and_maps_compared), as usual, has a good comparison. – Chris Hayes Jun 05 '15 at 02:18
  • 2
    FYI, Map [appears to be faster](https://jsperf.com/es6-map-vs-object-properties/2) for both setting and getting. – mpen Feb 02 '16 at 17:46
  • 1
    @mpen – jsperf is down now. Are you sure `map.set('foo', 123)` performed quicker than `obj.foo = 123`? If so that's very surprising – callum Sep 22 '16 at 16:14
  • @callum Uhh..no, not positive. You might want to write some new performance tests. – mpen Sep 22 '16 at 16:38

3 Answers3

56

There are some reasons why I prefer using Maps over plain objects ({}) for storing runtime data (caches, etc):

  1. The .size property lets me know how many entries exist in this Map;
  2. The various utility methods - .clear(), .forEach(), etc;
  3. They provide me iterators by default!

Every other case, like passing function arguments, storing configurations and etc, are all written using plain objects.

Also, remember: Don't try to optimize your code too early. Don't waste your time doing benchmarks of plain object vs Maps unless your project is suffering performance problems.

gustavohenke
  • 845
  • 9
  • 16
5

I'm not sure about this, but I think that performance is NOT a reason to use Maps. Take a look at this updated jsperf page:

http://jsperf.com/es6-map-vs-object-properties/73

It looks like (when dealing with strings at least) objects are much faster than maps for basic setting and getting.

EDIT: this answer is now outdated and wrong. See comment stream below.

EDIT2: Oddly, after all of the comments, it seems to return to Object being faster than Map (at least when storing numeric values). https://jsperf.app/es6-map-vs-object-properties/243

EDIT 3: It turns out JS optimizes everything when using a key that looks like a number, even if it was created as a string. When I use key_${i} rather than just ${i} for the key, things turn out much differently. Compare the flawed test above to this corrected version: https://jsperf.app/es6-map-vs-object-properties/251

  • 2
    That's not how you write performance tests. – Qix - MONICA WAS MISTREATED Aug 05 '17 at 22:00
  • 15
    That's not how you write useful comments. Please feel free to elaborate if you have an alternate methodology to suggest. What, specifically, is wrong with how those tests were written? Are they in any way invalid or unhelpful? – starlogodaniel Aug 07 '17 at 18:38
  • 11
    Language semantics/constructs being tested via microbenchmarks must only differ by one variable. Your tests vary across number of iterations, and a few of them will have their inner loop contents optimized out since the result is unused. Some tests pre-declare variables while others have the variable declaration inline with the for loop - which may incur different performance abnormalities. – Qix - MONICA WAS MISTREATED Aug 08 '17 at 05:38
  • 3
    Ouch, you're absolutely right. In my defense, the my version was an improvement on the one before it, but I missed both the pre-declare and the inner loop contents being optimized out. I was working with a colleague who improved upon my draft and I think resolved those issues: https://jsperf.com/es6-map-vs-object-properties/88. However, I think that it's valid to have different loop styles for the different data structures; in real usage, people will choose the loop structure with the best performance, and Map and Object have different "optimal" loop structures. Anyway, thanks for the catch. – starlogodaniel Aug 09 '17 at 15:12
  • Ok I see now - they used to be slower than plain objects, but have been heavily optimized in recent browsers. – jayarjo Jun 08 '18 at 17:38
  • 2
    Objects can never be as fast as Map. The runtime has to support prototypal inheritance & key mapping (e.g. numbers are converted to strings) that make optimizations complicated and bring overhead. Where as Map is just a straight forward key, value store that can be optimized to the extreme – sod Nov 29 '19 at 16:11
  • 1
    Unfortunately jsPerf seems to not be editable anymore, so I can't revise the tests to do a fair comparison, but I think at this point everyone would have to agree with you. When I originally wrote this answer, maps were actually slower than objects because maps were newer and not optimized. But since then they do seem to have caught up to and surpassed objects, based on tests I've run in real-world settings. – starlogodaniel Dec 02 '19 at 06:24
  • It appears that jsperf has been updated: [https://jsperf.com/es6-map-vs-object-properties/241](https://jsperf.com/es6-map-vs-object-properties/241) – pcormier Apr 18 '20 at 15:52
  • And interestingly, people are still making the same mistakes I did initially. I just updated the test to actually use the result so the body of the loop isn't optimized away, and amazingly... object is still faster than Map! https://jsperf.com/es6-map-vs-object-properties/243 – starlogodaniel Apr 20 '20 at 06:52
  • Map is way faster than object, both in Firefox and Chrome/Node https://jsben.ch/GdMb7 – user0103 Feb 10 '21 at 14:35
  • Your jsben.ch link takes me to malware sites, even though it used to work. But anyway, your test tests both writing and reading in a single test, which is only useful if you will do both operations frequently. For reading only, it seems Object is still faster then Map, unless you can see something wrong with my test here (note update from jsperf.com to jsperf.app) https://jsperf.app/es6-map-vs-object-properties/243 – starlogodaniel Mar 27 '23 at 18:58
  • Oh! My benchmark was very broken in a subtle way - I thought `${i}` would give me a string representation of i, but JS was being too smart and converting back to a number when I used it as a key, and optimizing as if I were using an array instead. Here is an updated benchmark that better shows the difference between using string keys or numeric keys for both Map and Object: https://jsperf.app/es6-map-vs-object-properties/251 – starlogodaniel Mar 27 '23 at 19:15
4

The other answers don't mention one last difference between objects and Maps:

The Map object holds key-value pairs and remembers the original insertion order of the keys.

Thus, when iterating over it, a Map object returns keys in order of insertion.

Quote from MDN, emphasis mine


This was the main reason I decided to use Map for the first time in a recent project. I had a normal object that I needed to display in a <table>, with each property going in a specific row.

let productPropertyOrder = [ "name", "weight", "price", "stocked" ];

let product =
{
    name: "Lasagne",
    weight: "1kg",
    price: 10,
    stocked: true
}

I wrote a function to transform an object into a Map according to a desired key order:

Note: this function discards all object properties not found in order

function objectToMap( obj, order )
{
    let map = new Map();

    for ( const key of order )
    {
        if ( obj.hasOwnProperty( key ) )
        {
            map.set( key, obj[ key ] );
        }
    }

    return map;
}

Then the map could be iterated over in the desired order:

let productMap = objectToMap( product, productPropertyOrder );

for ( const value of productMap.values() )
{
    let cell = document.createElement( "td" );
    cell.innerText = value;
    row.appendChild( cell );
}

Of course this is a bit contrived because one could just as well display when iterating over the property order without creating a Map in the process:

for ( const key of productPropertyOrder )
{
    if ( product.hasOwnProperty( key ) )
    {
        let value = product[ key ];
        // create cell
    }
}

But if you have an array of such objects and are going to be displaying them many places, then converting them all to maps first makes sense.

WD40
  • 149
  • 4
  • 1
    Your function `objectToMap` is reducing; every entry in the object whose key isn't in `order`, gets omitted from the resulting map. You might wanna include this information in your answer. – Gust van de Wal Jan 17 '20 at 15:11
  • 3
    Objects _also_ preserve the original insertion order of keys... [mostly, but with some caveats](https://tc39.es/ecma262/#sec-ordinaryownpropertykeys): "array index" keys (which are those satisfying `\`${key>>>0}\` === key`) are always listed first, in numerical order, then all string keys in original insertion order, and then all symbol keys in original insertion order. – Matthijs Oct 08 '20 at 00:22