In this article we will learn about dealing with collections in Javascript and will take a look at some advanced collections which are Maps and Sets.
In javascript there are a lot of methods to deal with collections. Like in languages such as PHP or Java there are collections that has numeric indices and these called indexed collections, collection that has associative indices and these called keyed collections that have a key of any type. In this tutorial we will take a look at each of these methods so let’s begin with arrays.
Indexed Collections (Arrays)
arrays is the indexed collection in javascript. Array is an ordered set of values that has a numeric index and value. For example, you could have an array called employees
that contains employees’ names indexed by their numerical employee number.
Creating an array
var arr = new Array(element0, element1, ...); // First method var arr = Array(element0, element1, ...); // Second method var arr = [element0, element1, ...]; // Third method
as shown above there are a lot of ways of creating arrays. When you initialize arrays in this way the length property is set to the number of arguments supplied.
Another way is to initialize the array with the number of items then add values like this:
var arr = new Array(5); // initialize array with fixed length var arr = Array(5); // initialize array with fixed length // This has exactly the same effect var arr = []; arr.length = 5; // initialize array with fixed length
If you need to create a variable length array and populate as you go use this approach:
// first method: initialize empty array then add elements var employees = []; employees[0] = 'Casey Jones'; employees[1] = 'Phil Lesh'; employees[2] = 'August West'; // second method: populate an array when you create it var colors = ['Red', 'Blue', 'Green'];
Â
Referring to array elements
To access array elements you use the index of the element as shown here:
var colors = ['Red', 'Blue', 'Green']; console.log(colors[0]); // Red console.log(colors[1]); // Blue
As shown above Arrays is zero based indices which means index of the elements begins with zero so to access the first element you do this with Array[0].
Â
Obtaining array length
To get the number of elements in array use Array.length like this:
console.log(colors.length) // 3
There are some caution when dealing with length property so consider this example:
var cats = []; cats[30] = ['Dusty']; console.log(cats.length); // 31
In the above code someone might guess that cats array has one element so it should have a length of 1. But here the first element has index of 30 so if you print the array it will show 31 elements starting with zero and this is because the length property always returns the index of the last element plus one (as above 30 + 1) = 31.
Iterating over arrays
There are multiple ways to iterate over arrays. First way using standard for loop:
var colors = ['red', 'green', 'blue']; for (var i = 0; i < colors.length; i++) { console.log(colors[i]); } // red // green // blue
The forEach()
method provides another way of iterating over an array:
var colors = ['red', 'green', 'blue']; colors.forEach(function(color) { console.log(color); }); // red // green // blue
Alternatively, You can shorten the code for the forEach parameter with ES2015 Arrow Functions:
var colors = ['red', 'green', 'blue']; colors.forEach(color => console.log(color)); // red // green // blue
Array methods
concat()
joins two arrays and returns a new array.
var myArray = new Array('1', '2', '3'); myArray = myArray.concat('a', 'b', 'c'); // myArray is now ["1", "2", "3", "a", "b", "c"]
join(delimiter = ',')
joins all elements of an array into a string.
var employees = new Array('Jonn', 'Adham', 'Amr'); var list = employees.join(' - '); // list is "Jonn - Adham - Amr"
push()
adds one or more elements to the end of an array and returns the resulting length of the array.
var numbers = new Array('1', '2'); numbers.push('3'); // numbers is now ["1", "2", "3"]
pop()
removes the last element from an array and returns that element.
var numbers = new Array('1', '2', '3'); var last = numbers.pop(); // numbers is now ["1", "2"], last = "3"
shift()
removes the first element from an array and returns that element.
var numbers = new Array('1', '2', '3'); var first = numbers.shift(); // numbers is now ["2", "3"], first is "1"
unshift()
adds one or more elements to the front of an array and returns the new length of the array.
var numbers = new Array('1', '2', '3'); numbers.unshift('4', '5'); // numbers becomes ["4", "5", "1", "2", "3"]
slice(start_index, upto_index)
extracts a section of an array and returns a new array.
var letters = new Array('a', 'b', 'c', 'd', 'e'); letters = letters.slice(1, 4); // starts at index 1 and extracts all elements // until index 3, returning [ "b", "c", "d"]
splice(index, count_to_remove, addElement1, addElement2, ...)
removes elements from an array and (optionally) replaces them. It returns the items which were removed from the array.
var numbers = new Array('1', '2', '3', '4', '5'); numbers.splice(1, 3, 'a', 'b', 'c', 'd'); // numbers is now ["1", "a", "b", "c", "d", "5"] // This code started at index one (or where the "2" was), // removed 3 elements there, and then inserted all consecutive // elements in its place.
reverse()
transposes the elements of an array, in place: the first array element becomes the last and the last becomes the first. It returns a reference to the array.
var numbers = new Array('1', '2', '3'); numbers.reverse(); // transposes the array so that numbers = ["3", "2", "1"]
sort()
sorts the elements of an array in place, and returns a reference to the array.
var myArray = new Array('Wind', 'Rain', 'Fire'); myArray.sort(); // sorts the array so that myArray = ["Fire", "Rain", "Wind"]
indexOf(searchElement[, fromIndex])
searches the array for searchElement
and returns the index of the first match.
var a = ['a', 'b', 'a', 'b', 'a']; console.log(a.indexOf('b')); // logs 1
map(callback[, thisObject])
returns a new array of the return value from executing callback
on every array item.
var a1 = ['a', 'b', 'c']; var a2 = a1.map(function(item) { return item.toUpperCase(); }); console.log(a2); // logs ['A', 'B', 'C']
filter(callback[, thisObject])
returns a new array containing the items for which callback returned true.
var a1 = ['a', 10, 'b', 20, 'c', 30]; var a2 = a1.filter(function(item) { return typeof item === 'number'; }); console.log(a2); // logs [10, 20, 30]
Keyed Collections
keyed collections or in some terms called associative. Collections of data which are ordered by a key, Map and Set objects contain elements which are iterable in the order of insertion.
Â
Maps
ECMAScript 2015 introduces a new data structure to map values to values. A Map
object is a simple key/value map and can iterate its elements in order of insertion.
Syntax
let myMap = new Map([iterable]); // create a new map object
Map take optional parameter iterable which is an Array
or other iterable object whose elements are key-value pairs.
Â
Adding elements
myMap.set('key1', 'value1'); myMap.set('key2', 'value2'); myMap.set('key3', 'value3');
As shown in the code above to add elements to the map use Map.set(key, value).
Â
Map properties and methods
Retrieve the size of map with Map.size property:
console.log(myMap.size); // 3
Get specific item by key with Map.get(key):
console.log(myMap.get('key1')) // value1 console.log(myMap.get('key7')) // undefined
Check if an item exists with Map.has(key):
console.log(myMap.has('key1')) // true console.log(myMap.has('key10')) // false
To delete an item from map use Map.delete(key):
myMap.delete('key1')
Clear all the map elements with Map.clear():
myMap.clear()
Iterating over map items:
Maps can be iterated using a for..of
loop as shown here:
for (var [key, value] of myMap) { console.log(key + ' -> ' + value); } // key1 -> value1 // key2 -> value2 // key3 -> value3
Maps also can be iterated using the forEach()
method:
myMap.forEach(function(value, key) { console.log(key + ' -> ' + value); }); // key1 -> value1 // key2 -> value2 // key3 -> value3
Let’s look at some examples:
var languages = new Map(); languages.set('php', 'web programming'); languages.set('c', 'system programming'); languages.set('opengl', 'game programming'); console.log(languages.size); // 3 console.log(languages.get('java')); // undefined console.log(languages.has('java')); // false languages.delete('opengl'); console.log(languages.has('opengl')); // false for (var [key, value] of languages) { console.log(key + ' used for ' + value); } // php used for web programming // c used for system programming // opengl used for game programming languages.clear(); console.log(languages.size); // 0
var myMap = new Map(); var keyString = 'a string', keyObj = {}, keyFunc = function() {}; // setting the values myMap.set(keyString, "value associated with 'a string'"); myMap.set(keyObj, 'value associated with keyObj'); myMap.set(keyFunc, 'value associated with keyFunc'); myMap.size; // 3 // getting the values myMap.get(keyString); // "value associated with 'a string'" myMap.get(keyObj); // "value associated with keyObj" myMap.get(keyFunc); // "value associated with keyFunc" myMap.get('a string'); // "value associated with 'a string'" // because keyString === 'a string' myMap.get({}); // undefined, because keyObj !== {} myMap.get(function() {}) // undefined, because keyFunc !== function () {}
As shown in the code above the Map keys can be anything (primitive values, objects or functions).
Using NaN
as Map
keys
var myMap = new Map(); myMap.set(NaN, 'not a number'); myMap.get(NaN); // "not a number" var otherNaN = Number('foo'); myMap.get(otherNaN); // "not a number"
NaN
can also be used as a key. Even though every NaN
is not equal to itself (NaN !== NaN
is true), the following example works because NaN
s are indistinguishable from each other.
Demonstrating keys(), values(), and entries():
var myMap = new Map(); myMap.set(0, 'zero'); myMap.set(1, 'one'); for (var [key, value] of myMap) { console.log(key + ' = ' + value); } // 0 = zero // 1 = one for (var key of myMap.keys()) { console.log(key); } // 0 // 1 for (var value of myMap.values()) { console.log(value); } // zero // one for (var [key, value] of myMap.entries()) { console.log(key + ' = ' + value); } // 0 = zero // 1 = one
Â
Object and Map Differences:
Traditionally, objects have been used to map strings to values. Objects allow you to set keys to values, retrieve those values, delete keys, and detect whether something is stored at a key. Map
objects, however, have a few more advantages that make them better maps.
- The keys of an
Object
areStrings
, where they can be of any value for aMap
. - You can get the size of a
Map
easily while you have to manually keep track of size for anObject
. - The iteration of maps is in insertion order of the elements.
- An
Object
has a prototype, so there are default keys in the map. (this can be bypassed usingmap = Object.create(null)
).
Converting Maps To Arrays and vice versa:
var kvArray = [['key1', 'value1'], ['key2', 'value2']]; // Use the regular Map constructor to transform a 2D key-value Array into a map var myMap = new Map(kvArray); myMap.get('key1'); // returns "value1" // Use the Array.from function to transform a map into a 2D key-value Array console.log(Array.from(myMap)); // Will show you exactly the same Array as kvArray // Or use the keys or values iterators and convert them to an array console.log(Array.from(myMap.keys())); // Will show ["key1", "key2"] console.log(Array.from(myMap.values())); // Will show ["value1", "value2"]
Sets
The Set
object lets you store unique values of any type, whether primitive values or object references. Set differs from Maps in that it does not contain explicit keys because it’s a collection of values and keys is the same as values.
Like Maps you can iterate Set elements in the order of insertion. But note that a value in a Set
may only occur once; it is unique in the Set
‘s collection.
Syntax
let mySet = new Set([iterable]); // create a new set object
If an iterable object is passed, all of its elements will be added to the new Set
. If you don’t specify this parameter, or its value is null
, the new Set
is empty.
Adding Elements
mySet.add(1); mySet.add(5); mySet.add(10); mySet.add(10); // will be deleted because its aleardy exist mySet.add(undefined); mySet.add(NaN); mySet.add('some text'); mySet.add({a: 1, b: 2}); mySet.add([1, 2, 3]);
As shown above you can add any arbitrary values in the Set. You can add primitive values, objects, arrays, also undefined and Nan.
Set properties and methods
Retrieve the size of set with Set.size property:
console.log(mySet.size); // 8
Check if a value exists with Set.has(value):
console.log(mySet.has(10)) // true console.log(mySet.has(1000)) // false
To remove an element from set use Set.delete(value):
mySet.delete(5);
To clear all Set values use Set.clear():
mySet.clear();
Iterating over set items:
using for .. of loop as shown in this code:
// iterate over items in set for (let item of mySet) { console.log(item); } // logs the items in the order: // 1 // 5 // 10 // undefined // NaN // "some text" // {"a": 1, "b": 2} // [1, 2, 3]
You can also iterate Sets using Set.forEach(callback):
// Iterate set entries with forEach mySet.forEach(function(value) { console.log(value); });
Let’s look at few examples:
var mySet = new Set(); mySet.add(1); mySet.add('some text'); mySet.add('foo'); mySet.has(1); // true mySet.delete('foo'); mySet.size; // 2 for (let item of mySet) console.log(item); // 1 // "some text"
var mySet = new Set(); mySet.add(1); // Set [ 1 ] mySet.add(5); // Set [ 1, 5 ] mySet.add(5); // Set [ 1, 5 ] mySet.add('some text'); // Set [ 1, 5, 'some text' ] var o = {a: 1, b: 2}; mySet.add(o); mySet.add({a: 1, b: 2}); // o is referencing a different object so this is okay mySet.has(1); // true mySet.has(3); // false, 3 has not been added to the set mySet.has(5); // true mySet.has(Math.sqrt(25)); // true mySet.has('Some Text'.toLowerCase()); // true mySet.has(o); // true mySet.size; // 5 mySet.delete(5); // removes 5 from the set mySet.has(5); // false, 5 has been removed mySet.size; // 4, we just removed one value console.log(mySet);// Set [ 1, "some text", Object {a: 1, b: 2}, Object {a: 1, b: 2} ]
Â
Set keys(), values(), and entries():
values() returns a new Iterator
object that contains the values for each element in the Set
object in insertion order.
keys() is the same function as the values()
function and returns a new Iterator
object that contains the values for each element in the Set
object in insertion order.
entries() returns a new Iterator
object that contains an array of [value, value]
for each element in the Set
object, in insertion order
for (let item of mySet.keys()) console.log(item); for (let item of mySet.values()) console.log(item); for (let [key, value] of mySet.entries()) console.log(key); var myArr = Array.from(mySet); // the following will also work if run in an HTML document mySet.add(document.body); mySet.has(document.querySelector('body')); // true // converting between Set and Array mySet2 = new Set([1, 2, 3, 4]); mySet2.size; // 4 [...mySet2]; // [1, 2, 3, 4] // intersect can be simulated via var intersection = new Set([...set1].filter(x => set2.has(x))); // difference can be simulated via var difference = new Set([...set1].filter(x => !set2.has(x))); // Iterate set entries with forEach mySet.forEach(function(value) { console.log(value); });
Â
Array and Set Differences:
Traditionally, a set of elements has been stored in arrays in JavaScript in a lot of situations. The new Set
object, however, has some advantages:
- Checking whether an element exists in a collection using
indexOf
for arrays is slow. Set
objects let you delete elements by their value. With an array you would have to splice based on an element’s index.- The value
NaN
cannot be found withindexOf
in an array. Set
objects store unique values; you don’t have to keep track of duplicates by yourself.
Â
Converting Sets To Arrays and vice versa:
var myArray = ['value1', 'value2', 'value3']; // Use the regular Set constructor to transform an Array into a Set var mySet = new Set(myArray); mySet.has('value1'); // returns true // Use the spread operator to transform a set into an Array. console.log([...mySet]); // Will show you exactly the same Array as myArray
best