Javascript

Javascript Keyed and Indexed Collections Array, Map and Set

javascript collections

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 NaNs 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 are Strings, where they can be of any value for a Map.
  • You can get the size of a Map easily while you have to manually keep track of size for an Object.
  • 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 using map = 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 with indexOf 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

 

5 1 vote
Article Rating

What's your reaction?

Excited
4
Happy
4
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Rohit
Rohit
2 years ago

best