Categories
Modern JavaScript

Best of Modern JavaScript — Maps and Arrays

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at how to use maps by doing array operations with them.

Converting Maps to Arrays

We can use the spread operator to convert iterable objects to arrays.

This includes maps.

For example, we can write:

const map = new Map()
  .set('foo', 'one')
  .set('bar', 'two')
  .set('baz', 'three');

const arr = [...map];

to create a map and convert it to an array.

Then we get an array with the following for arr :

[
  [
    "foo",
    "one"
  ],
  [
    "bar",
    "two"
  ],
  [
    "baz",
    "three"
  ]
]

Looping Over Map Entries

We can loop over map entries with the forEach method.

The method takes a callback with the value and key as parameters.

For instance, we can write:

const map = new Map()
  .set('foo', 'one')
  .set('bar', 'two')
  .set('baz', 'three');

map.forEach((value, key) => {
  console.log(key, value);
});

Then we get:

foo one
bar two
baz three

from the console log.

Mapping and Filtering Maps

To map and filter maps, we’ve to convert the map to an array first.

There’re no methods to do this within the Map constructor.

Therefore, to create a map and then do filtering and mapping with it, we can write:

const map = new Map()
  .set('foo', 1)
  .set('bar', 2)
  .set('baz', 3);

const mappedMap = new Map(
  [...map]
  .map(([k, v]) => [k, v ** 2])
);

We created a map called map .

Then we spread map with the spread operator into an array.

Then we called map on the returned array instance and return a new array with the v , which has the value squared.

k is the key and that stayed the same.

We did the mapping in the Map constructor to get a map returned.

In the end, we get a map with the following:

{"foo" => 1, "bar" => 4, "baz" => 9}

Similarly, we can call filter to filter the map entries.

For example, we can write:

const map = new Map()
  .set('foo', 1)
  .set('bar', 2)
  .set('baz', 3);

const mappedMap = new Map(
  [...map]
  .filter(([k, v]) => v < 3)
);

We called the filter method with the same callback signature, but we return only the entries with the value less than 3.

We did the filtering in the Map constructor to get a map returned.

In the end, we get:

{"foo" => 1, "bar" => 2}

We can also use the spread operator to combine maps.

For instance, we can write:

const map = new Map()
  .set('foo', 1)
  .set('bar', 2)
  .set('baz', 3);

const map2 = new Map()
  .set('qux', 4)
  .set('quxx', 5);

const combinedMap = new Map([...map, ...map2])

We created 2 maps, map1 and map2 .

Then we spread them both into an array with the spread operator.

The Map constructor will turn all the entries into a map.

In the end, we get:

{"foo" => 1, "bar" => 2, "baz" => 3, "qux" => 4, "quxx" => 5}

Conclusion

Converting maps to arrays are useful for various operations.

It lets us use array methods on maps, which is useful since there’re no map equivalents of array methods.

Categories
Modern JavaScript

Best of Modern JavaScript — Maps

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at how to use maps in various ways.

Maps to JSON

Maps can be converted to JSON objects.

We can do the conversion to the spread operator to an array.

For instance, we can write:

const map = new Map()
  .set('foo', 'one')
  .set('bar', 'two')
  .set('baz', 'three');

const arr = [...map];

We can just spread the map into an array of key-value pair arrays since maps are iterable.

Once we did that, we can convert the array into a JSON string with JSON.stringify :

console.log(JSON.stringify(arr));

Then we get:

[["foo","one"],["bar","two"],["baz","three"]]

We can convert the stringified array of key-value pair arrays into a map with JSON.parse and the Map constructor.

For example, we can write:

const parsed = new Map(JSON.parse(str));

Then we get back the original map.

Map to Object

We can convert a map to an object by looping through it and then putting the keys as the properties and the values as the values.

For example, we can write:

const map = new Map()
  .set('foo', 'one')
  .set('bar', 'two')
  .set('baz', 'three');

const obj = Object.create(null);
for (const [k, v] of map) {
  obj[k] = v;
}

We create an object without a prototype by passing in null into Object.create .

Then we loop through our map to get the key and value and then set the key as the property name and the value as the value with the for-of loop.

We can convert the object to JSON with JSON.stringify .

For example, we can write:

console.log(JSON.stringify(obj));

And we get:

{"foo":"one","bar":"two","baz":"three"}

logged.

We can call JSON.parse to parse that back into an object.

For instance, we can write:

const map = new Map()
  .set('foo', 'one')
  .set('bar', 'two')
  .set('baz', 'three');

const obj = Object.create(null);
for (const [k, v] of map) {
  obj[k] = v;
}

const str = JSON.stringify(obj);
console.log(JSON.parse(str));

Map API

The Map API lets us create a map with an iterable object of key-value pair arrays.

This is optional.

If we don’t pass it in, then we create an empty map.

For instance, we can write:

const map = new Map([
  ['foo', 'one'],
  ['bar', 'two'],
  ['bar', 'three'],
]);

to create a map.

The Map.prototype.get method takes a string key and returns the value with the given key.

If there’s no such key in the map, then undefined is returned.

Map.prototype.set takes a key and value as arguments and then returns the map with the new entry added.

Map.prototype.has takes a key and returns a boolean indicating whether the key exists or not.

Map.prototype.delete takes a key and removes the item given the key.

If an item is removed then true is returned.

Otherwise, nothing happens and false is returned.

Map.prototype.size is a getter method and return how many entries are in the map.

Map.prototype.clear clears all entries from the map and returns nothing.

Conclusion

Maps can be converted to arrays and objects.

Also, it has many methods we can use to manipulate and get data about maps.

Categories
Modern JavaScript

Best of Modern JavaScript — Loops and Array.from

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the for-of loop and the Array.from method.

Iterating with Existing Variables, Object Properties, and Array Elements

We can iterate with variables that have already been defined.

For instance, we can write:

let x;
for (x of [1, 2, 3]) {
  console.log(x);
}

We assign the array entry to x , which is defined before the loop block.

We can also iterate with an object property.

For example, we can write:

const obj = {};
for (obj.foo of [1, 2, 3]) {
  console.log(obj.foo);
}

We defined obj outside the loop and attached the foo property to it.

We can also iterate with an array element:

const arr = [];
for (arr[0] of [1, 2, 3]) {
  console.log(arr[0]);
}

We set the first entry of arr as we loop through it.

Iterating with a Destructuring Pattern

We can iterate with the destructuring pattern.

For example, we can write:

const map = new Map().set(1, 'foo').set(2, 'bar');
for (const [key, value] of map) {
  console.log(key, value);
}

We create a Map instance with the key and value.

Then we can loop through it with the key and value destructured from each map entry.

And then we can use it anywhere in the loop body.

We can do the same with array.entries() it returns an array with entries with the [key, value] pattern.

For example, we can write:

const arr = ['foo', 'bar', 'baz'];
for (const [key, value] of arr.entries()) {
  console.log(key, value);
}

Then we destructured the return entries and log them.

New Array Features

The Array constructor gained various instance and static methods with ES6.

New Static Array Methods

One of the new static Array method includes the Array.from method.

It takes an array-like or iterable object and returns an array.

For example, we can write:

const arrayLike = {
  length: 2,
  0: 'foo',
  1: 'bar'
};

const arr = Array.from(arrayLike);
for (const a of arr) {
  console.log(a);
}

We convert the arrayLike object into an array with the Array.from method.

An array-like object is one with the length property and numeric indexes.

We can’t use these kinds of object with the for-of loop.

So if we write:

const arrayLike = {
  length: 2,
  0: 'foo',
  1: 'bar'
};

for (const a of arrayLike) {
  console.log(a);
}

We’ll get the error ‘Uncaught TypeError: arrayLike is not iterable’.

We can also convert iterable objects to arrays with Array.from .

For instance, given the following HTML:

<div>
  foo
</div>
<div>
  bar
</div>
<div>
  baz
</div>

We can get a NodeList with the div nodes, convert it to an array, and the loop through it.

For instance, we can write:

const divs = document.querySelectorAll('div');

for (const d of Array.from(divs)) {
  console.log(d.textContent);
}

We call Array.from to convert the NodeList to an array.

And then we can use it with the for-of loop.

This is different from the old way, where we call slice with the call method:

const divs = document.querySelectorAll('div');
const arr = Array.prototype.slice.call(divs);
for (const d of arr) {
  console.log(d.textContent);
}

Conclusion

We can use the for-of loop with various kinds of variables.

Also, the Array.from lets us convert an array-like or iterable object to an array.

Categories
Modern JavaScript

Best of Modern JavaScript — Import and Export Styles

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at how to use JavaScript modules.

Named Exporting Styles

We can export anything in our JavaScript modules.

Some things we can export include:

export var foo = "...";
export let bar = "...";
export const MY_CONST = "...";

export function qux() {
  //...
}

export function* gen() {
  //...
}

export class Baz {
  //...
}

We export var , let , const variables.

And we can also export various kinds of functions and classes.

We can also list all the exports at the end of the file.

For example, we can write:

var foo = "...";
let bar = "...";
const MY_CONST = "...";

function qux() {
  //...
}

function* gen() {
  //...
}

class Baz {
  //...
}

export { MY_CONST, foo, bar, qux, gen, Baz };

We have one export statement with all the items we export at the end.

Re-exporting

We can re-export module members by writing:

export { foo, bar } from "./foo";

Given that foo.js has:

export var foo = "...";
export let bar = "...";

This syntax is the shorthand for importing all the members from foo.js and export them with the same names all in one line.

We can also use the as keyword to export the members.

For example, we can write;

export { foo as qux, bar } from "./foo";

to rename the foo variable to qux .

We can do the same thing as default export.

For example, we can write:

export { default } from "./foo";

given that we have:

export default "...";

We can also re-export named exports as default exports.

For example, we can write:

export { baz as default } from "./foo";

given that we have:

export const baz = "...";

in foo.js .

We imported baz from foo.js , which is a named export as a default export.

Having Both Named Exports and a Default Export in a Module

We can have both named and default exports inside a module.

For example, we can write:

var foo = "...";
let bar = "...";
const MY_CONST = "...";

function qux() {
  //...
}

function* gen() {
  //...
}

class Baz {
  //...
}

export { MY_CONST, foo, bar, qux, gen, Baz };

export default function quux() {}

all in one module.

Then we can write:

import quux, { MY_CONST, foo, bar, qux, gen, Baz } from "./foo";

to import them.

This is similar to what we had with CommonJS.

It’s probably a good idea to avoid mixing named and default exports to avoid confusion.

The default export is just another named export.

However, its name is default unless we rename it with as or import it with the name we want.

For example, we can import default exports by writing:

import { default as foo } from 'lib';
import foo from 'lib';

We can use default as an export name, but not as a variable name.

Conclusion

There’re many ways to export something with our code.

We can also re-export imports all in one line.

Categories
Modern JavaScript

Best of Modern JavaScript — for-of

Since 2015, JavaScript has improved immensely.

It’s much more pleasant to use it now than ever.

In this article, we’ll look at the for-of loop.

The for-of Loop

The for-of loop lets us replace the for-in loop and forEach with one loop.

It can also be used to iterate over any iterable object.

For example, we can use the for-of loop to loop through an array by writing:

const arr = [1, 2, 3];
for (const x of arr) {
  console.log(x);
}

We have an arr array and we loop through it with the for-of loop.

x is the array entry we’re iterating through.

break and continue also work inside for-of loops.

For example, we can write:

const arr = [1, 2, 3];
for (const x of arr) {
  if (x === 2) {
    break;
  }
  console.log(x);
}

or:

const arr = [1, 2, 3];
for (const x of arr) {
  if (x === 2) {
    continue;
  }
  console.log(x);
}

We can still use break to stop the loop.

And we can use continue to skip to the next iteration.

Also, it works with iterable object entry destructuring.

For example, we can write:

const arr = [1, 2, 3];
for (const [index, element] of arr.entries()) {
  console.log(index, element);
}

We call the arr.entries() method which returns an array with arrays of the index and the element for the index as the entries.

The for-of loop also works with other kinds of iterable objects.

For example, we can use it to iterate through maps.

We can write:

const map = new Map([
  [1, 'foo'],
  [2, 'bar'],
]);

for (const [key, value] of map) {
  console.log(key, value);
}

We create the Map instance and loop through it with the for-of loop.

We destructure each entry with the array with key and value .

And run console.log method to log the item.

The for-of loop only works with iterable values.

This means that we need to convert plain objects to iterable objects to make it work with the for-of loop.

For example, if we have an object with the length property and the indexes as keys, we’ve to convert it to an array with Array.from .

This means if we write:

const arrayLike = {
  length: 2,
  0: 'foo',
  1: 'bar'
};

for (const x of arrayLike) {
  console.log(x);
}

We’ll get the ‘Uncaught TypeError: arrayLike is not iterable’ error.

To fix this, we’ve to use the Array.from method:

const arrayLike = {
  length: 2,
  0: 'foo',
  1: 'bar'
};

for (const x of Array.from(arrayLike)) {
  console.log(x);
}

Array.from converts the object to an array so we can use the returned array with the for-of loop.

const vs var in Iterable Variables

Since for-of loop creates a fresh binding in each iteration, we can use const to declare our loop variable in the loop head.

This works as long as we don’t reassign the value in the loop body.

For instance, we can write:

const arr = [];
const orig = ['foo', 'bar', 'baz'];
for (const elem of orig) {
  arr.push(() => elem);
}
console.log(arr.map(f => f()));

The console log gives us [“foo”, “bar”, “baz”] since we const variables are confined within the block.

let works the same way as const , but we can reassign it to a value in the loop block.

On the other hand, if we use var , we’ll get different results.

For instance, if we have:

const arr = [];
const orig = ['foo', 'bar', 'baz'];
for (var elem of orig) {
  arr.push(() => elem);
}
console.log(arr.map(f => f()));

Then we get [“baz”, “baz”, “baz”] This is because a var variable is also available outside the block.

So when we run the functions we pushed to arr , elem will be 'baz' .

Therefore, we eliminate confusion by not using var .

Conclusion

for-of loop provides us with many conveniences with iteration.