Javascript map performance vs object

Ocean Fishing

The internet is a great place to find information, but there is one teeny-tiny problem. You are on a boat in the middle of a deep blue sea. Although most of us do not venture all the way down into the darkness, we still precisely like to get what we want. (Oh! The Internet is the deep blue sea I was talking about).

As frontend developers most of the times we do not rely on vanilla javascript for everything that we want. Most of the times we pledge our allegiance to our lords (frameworks) and libraries and follow them blindly because we think that they are capable of doing everything, sometimes losing out on javascript best practices.

Recently, I got the chance to work on a backbone application where I understood the primary importance of selectively using javascript built-in objects for our operations regardless of the framework.

Let’s say for example we have an array of n-objects [{id: 1, name: “one”}, {id: 2, name: “ two”} …. ], when someone asks you to find an object based on the ‘id’, ideally your first approach would be to use the array’s built-in iterative methods to find the first matching element? Right.

But let’s say when the same person tells you to do the same operation n-times for random id in your array, what would you? Try and writing an efficient algorithm is one answer, but a better answer is that you use an Object to do the same job.

by losing an extra O(n) iteration to loop through the array and generate an { id: {}, id: {} }, you would be getting access to Objects.id to directly and efficiently access the object you want. And for an n-number of operations, you would be saving a ton of time, rather than searching the entire array.

So with that introduction let’s jump into using choosing javascript’s built-in objects(Array, Object, Set and Map) based on operations we wish to use it for.

1. Search/Find

Let’s look at how we can find a particular element in all the four built-in javascript objects for different use-cases.

Array

// array of objects
array.find(object => object.id === 2); // returns object with id 2
//array of numbers starting from "zero"
array.indexOf("one"); // returns 1 as index

Object

// array of Objects
// eg: [{id: 1, name: "one"},...] can be converted to {1: {name: "one"}, ... }
object[2] // returns the value of key 2 (i.e {name: "two"}

Note: When using n-operations to find an object, its always best to use object key for retrieving the item rather than array find.

Set

Sets have no built-in function to retrieve or find the index of its items even-though its an iterable, so ideally we would have to convert it to an array before indexOf/find operation.

const mySet = new Set(['1', '2', '3']);
[...mySet].indexOf('2') // returns 1
const mySet = new Set([{1: 'one'}, {2: 'two'}, {3: 'three'}]);
[...mySet].find(object => object[2] === 'two'); // returns {2: 'two'}

Map

Maps are special objects per se, they are iterables with key value pair constructor that looks like a 2D array but acts like an object. They offer a better flexibility in terms of choosing our key values. A map can have a key value which can be a string, number, object or even NaN.

var map = new Map([[ 1, 'one' ],[ 2, 'two' ]]);
map.get(1)
// returns 'one'

Note: Maps can be very flexible in places where objects can be a bit annoying, and it strongly serves the purpose in some specific scenarios, like adding and deleting key-pairs frequently.

2. Sort

Sort operations can be interesting, and most of the times we assume that sort over an iterable entity works out of the box. Well, it doesn’t always.

Array

Array sorts are often misunderstood by both beginners and intermediate developers. Since array’s default sort sorts an array based on Unicode , we cannot expect to get same sort behaviour for all the datatypes. Hence, we often need to pass a comparator function into the sort.

// array of strings in a uniform case without special characters
const arr = [ "sex", "age", "job"];
arr.sort();
//returns ["age", "job", "sex"]
// array of numbers
const arr = [ 30, 4, 29 , 19];
arr.sort((a, b) => a-b);
// returns [4, 19, 29, 30]
// array of number strings
const arr = [ "30", "4", "29" , "19" ];
arr.sort((a, b) => a-b); // returns ["4", "19", "29", "30"]
// array of mixed numerics
const arr = [ 30, "4", 29 , "19" ];
arr.sort((a, b) => a-b); // returns ["4", "19", 29, 30]
// array of non-ASCII strings and also strings
const arr = ['réservé', 'cliché', 'adieu'];
arr.sort((a, b) => a.localeCompare(b));
// returns is ['adieu', 'cliché','réservé']
// array of objects
const arr = [
{ name: 'Sharpe', value: 37 },
{ name: 'And', value: 45 },
{ name: 'The', value: -12 }
];

// sort by name string
arr.sort((a,b) => a['name'].localeCompare(b['name']));
// sort by value number
arr.sort((a,b) => a['value']-b['value']);

Note: I usually prefer using localeCompare to sort my strings as they offer more flexibility of controlling case-sensitivity, accent and also give us the benefit of considering language during the sort.

Object

There is no built-in method for the sorting of the objects, but ES6 offers some interesting built-in key-sorting during the creation of the object. Object keys are sorted only based on numerics/numeric-strings, all the other keys are pushed right after the numeric keys unsorted.

// object with numeric/numeric-string keys are sorted
const obj = { 30: 'dad', '4': 'kid', 19: 'teen', '100': 'grams'};
console.log(obj)
// returns {4: "kid", 19: "teen", 30: "dad", 100: "grams"} with sorted keys
// object with key-values as alpha-strings are not sorted
const obj = { "b": "two", "a": "one", "c": "three" };
console.log(obj) // returns {b: "two", a: "one", c: "three"}
// object with numeric, numeric-string and alpha keys are partially sorted. (i.e only numeric keys are sorted)
const obj = { b: "one", 4: "kid", "30": "dad", 9: "son", a: "two" };
console.log(obj)
// returns {4: "kid", 9: "son", 30: "dad", b: "one", a: "two"}

Set

Sets do not have built-in sort functionality, however the easiest way to sort a set is to convert it to an array and implementing array’s sort method. Since, set is an iterable object, we can build our own sorting algorithm of our choice.

// set to array and array sort 
const set = new Set(['b', 'a', 'c']);
[...set].sort();
// returns ['a', 'b', 'c'] which is an array
// alternatively we can use entries to sort a set and create a new sorted set. The iterator gives us the ['a', 'a'] when spread over an array.
const set = new Set(['b', 'a', 'c']);
const sortedSet = new Set([...set.entries()].map((entry) => entry[0]).sort());

Note: Keep in mind that sort method is of array’s and you would have to use a comparator appropriately to get your desired sort.

Map

Similar to sets maps do not have a built-in method themselves, but we can still spread their entries over an array and build a new sorted map.

// entries spread over an array can be sorted like an array
const map = new Map([["c", 'three'],["a", 'one'], ["b", 'two']]);
const sortedMap = new Map([...map.entries()].sort())
// returns sorted Map(3) {"a" => "one", "b" => "three", "c" => "two"}

Note: In the map sorting, it is important to know that the two-dimensional array from the map gets sorted based on the first element in each sub-array. Here the sorting is based on “a”, “b” and “c” strings. If these were numbers, you would have to use a comparator.

3. Includes or Has

One of the most important features of the iterable objects is to check the presence of the desired item. Almost, all of the built-in standard and iterable javascript objects have their own implementation to achieve this. Let’s look at them below.

Array

// we are considering a linear array only
const arr = [1, 2, 3];
arr.includes(1); // returns true
arr.includes('1'); // returns false as types do not match

Object

// we are going to consider only the keys
const obj = { a: 1, b: 2, c: 3, 1: 'one' };
obj.hasOwnProperty('a'); // returns true
obj.hasOwnProperty('1'); // returns true because no type check
obj.hasOwnProperty(1); // returns true

Set

Set has a handy ‘has’ function which can be more efficient in accessing the values compared to an array.

const set = new Set([1, 2, 3, 4, 5]);
set.has(4); // returns true
set.has('4'); // returns false because of mismatch in type

Map

Map has a built-in ‘has’ function too.

const map = new Map([[3, 'three'],["a", 'one'], ["b", 'two']]);
map.has('a');
// returns true
map.has(3); // returns true
map.has('3'); // returns false because types don't match

Note: Compared to the array’s includes function, Object’s hasOwnProperty and Set/Map’s has functions seem to perform close to O(1) in different tests, clearly more efficient in terms of larger data sets.

4. Removing Duplicates

There is no straight forward way to remove duplicates in a collection, given that array or object is linear, we can use some of the built-in methods to remove duplicates.

Array

There are many online methods to remove duplicates from an array. That this stackoverflow thread covers. However in ES6 let’s look at the easiest ways of removing duplicates from an array.

// considering a linear array Set gives us the answer we need.
const arr = [1, 2, 2, 4, 5, 5];
[...new Set(arr)];
// returns [1, 2, 4, 5]
// however set doesn't remove duplicates from array of objects
const arr = [{a:1},{b:2},{a:1}];
[...new Set(arr)];
// returns [{a:1},{b:2},{a:1}]
// hence we can use ES6 filter and map functions to achieve the same
arr.filter((obj, index) => {
return arr.map(obj => obj['a']).indexOf(obj['a']) === index;
});
// removes duplicate based on the key 'a'

Note: Set will remove the duplicate objects if they are stringified and the strings match, but we still have to parse then back to the object once set is converted back to an array. The whole process might not be performant.

Object

Objects do not allow duplicate key values, old values are overwritten by the new values.

const obj = { b: "one", a: "two", a: "three" };
console.log(obj); // returns {b: "one", a: "three"}

Set

Sets inherently do not allow duplicate values when they are passed a linear iterable object like an array, but when they are passed an array of object they do allow duplicate objects.

// a linear array iterable
const set = new Set([1, 2, 2, 4, 5, 5]);
console.log(set);
// returns Set {1, 2, 4, 5}
// array of objects
const set = new Set([{a:1},{b:2},{a:1}]);
console.log(set);
// returns Set {{a:1},{b:2},{a:1}} with duplicate objects

Map

Maps also do not allow duplicate keys during the creation.

const map = new Map([[3, 'three'], [2, 'two'], [2, 'four']]);
console.log(map);
// returns {3 => "three", 2 => "four"}

5. Delete

Array

Array has no built-in method to delete its items. However we can use couple of methods to do it. Splice, indexOf or filter.

// I personally prefer this method.
const arr = [ 'a', 'b', 'c' ];
arr.filter(e => e !== 'c');
// returns [ 'a', 'b' ] removing 'c'

Object

Objects do not have a built-in delete method, but according to the docs we can use the delete keyword to delete a key. However, this is widely discouraged in the javascript community and even libraries like underscore and lodash use a different method.

// The infamous delete method
const obj = { b: "one", a: "two" };
delete obj.a;
// deletes a and returns true

Set

Set offers a built-in delete method making our lives easier

const set = new Set([1, 2, 4, 5]);
set.delete(4);
// deletes 4 and returns true
set.delete('5');
// returns false as types do not match

Map

Map has its own built-in delete method to remove keys from a given map object.

const map = new Map([[3, 'three'], [2, 'two']);
map.delete(3);
// deletes [3, 'three'] and returns true.
map.delete('2'); // returns false as types do not match

6. Length and Size

// arrays have a built-in property for length which is different from collection size.
['1', '2', '3'].length // returns 3
// objects have no built-in property to check length or size, so we need to resort to using keys array to check length.
Object.keys({ b: 'one', a: 'two', c: 'three' }).length // returns 3
// set has a size property built-in
new Set([{a:1},{b:2},{a:1}]).size // returns 3
// map has a size property built-in
new Map([[3, 'three'],['a', 'one'], ['b', 'two']]).size // returns 3

Conclusion

These are some of the key pointers to remember when choosing the standard built-in objects of your choice. Though javascript offers us the flexibility of using more than one built-in objects, its always better to choose the one that suits the best. Feel free to leave your comments below.

✉️ Subscribe to CodeBurst’s once-weekly Email Blast, 🐦 Follow CodeBurst on Twitter, view 🗺️ The 2018 Web Developer Roadmap, and 🕸️ Learn Full Stack Web Development.

Which is faster Map or object JavaScript?

However, if you're only using string-based keys and need maximum read performance, then objects might be a better choice. This is because JavaScript engines compile objects down to C++ classes in the background, and the access path for properties is much faster than a function call for Map().

Which is faster Map or object?

The results are similar to those string-key cases: Map s start off as much faster than objects (2 times faster for insertion and deletion, 4-5 times faster for iteration), but the delta is getting smaller as we increase the size.

How fast is JavaScript Map?

map() takes about 2,000ms, whereas a for loop takes about 250ms.

Is Map faster than for loop JavaScript?

Comparing performance , map() wins! map() works way faster than for loop.