Categories
Modern JavaScript

Best of Modern JavaScript — Iterables

Since 2015, JavaScript has improved immensely.

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

In this article, we’ll look at JavaScript iterable objects.

Iterable Values

JavaScript has various kinds of iterable objects.

They include arrays, strings, maps, sets, and NodeLists.

Constructs Supporting Iteration

There’re various constructs that make working with iterable objects easier.

One of them is the destructuring syntax.

We can destructure any iterable object entries to variables.

For example, we can write:

const [a, b] = new Set(['a', 'b', 'c']);

Then we assigned 'a' to a and 'b' to b .

The for-of loop lets us iterate through entries in iterable objects easily.

For instance, we can write:

for (const x of ['foo', 'bar', 'baz']) {
  console.log(x);
}

to loop through an array.

The Array.from static method lets us create an array from iterable objects.

For example, we can convert a set to an array by writing:

const arr = Array.from(new Set(['a', 'b', 'c']));

Then arr would be [‘a’, ‘b’, ‘c’] .

The spread operator lets us do the same thing as Array.from

We can write:

const arr = [...new Set(['a', 'b', 'c'])];

to spread a set’s entries into an array.

The Map and Set constructor lets us create maps and sets from arrays.

The Map constructor takes an array with key-value arrays as entries and turn them into a set of key-value pairs in an object.

For example, we can write:

const map = new Map([
  ['foo', 'no'],
  ['bar', 'yes']
]);

To create a set, we can write:

const set = new Set(['a', 'b', 'c']);

to create it from an array.

Promise.all() lets us run multiple promises in parallel.

For instance, we can write:

Promise.all([
  Promise.resolve(1),
  Promise.resolve(2),
]).then(() => {
  //...
});

to run 2 promises in an array in parallel.

Promise.race resolves to the value of the first promise that finishes running.

For instance, we can use it by writing:

Promise.race([
  Promise.resolve(1),
  Promise.resolve(2),
]).then(() => {
  //...
});

And we get the first value resolved from the set of promises.

The yield* keyword lets us run generator functions from another generator function.

So we can use it by writing:

`yield*` `gen;`

Iterability

Any object with the Symbol.iterator method is an iterable object.

The method must be a generator function.

For example, we can get that method from the array by writing:

const arr = ['foo', 'bar', 'baz'];
const iter = arr[Symbol.iterator]();

Then we can get the entries one by one by calling the next method from the returned iterator:

iter.next()

We get:

{value: "foo", done: false}

when we run it the first time.

When we run it again:

iter.next()

We get:

{value: "bar", done: false}

Then when we run it again, we get:

{value: "baz", done: false}

Then finally, when we run it one last time, we get:

{value: undefined, done: true}

value is the value returned from the iterable object. And done indicates whether there’s any value to return.

next returns each item wrapped in an object.

Conclusion

JavaScript has various kinds of iterable objects.

They all have the Symbol.iterator property which is a method that returns the next value sequentially.

Categories
Modern JavaScript

Best of Modern JavaScript — WeakMaps and Sets

Since 2015, JavaScript has improved immensely.

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

In this article, we’ll look at weak maps.

WeakMap API

WeakMaps have 4 methods.

The WeakMap constructor takes an array of key-value pairs arrays.

The keys must be objects.

The WeakMap.prototype.get method takes the key and returns a value given the key.

WeakMap.prototype.set takes the key and value as arguments and returns the WeakMap with the new entry.

WeakMap.prototype.has takes the key as the argument and return true if the item with the given key exists and false otherwise.

WeakMap.prototype.delete takes the key with the item to delete and returns true if it’s deleted and false if it’s not found.

Set

ES5 doesn’t have a set data structure.

The alternatives are to use the keys of an object to store elements.

Or we can store the elements in an array.

We check whether it contains an element via indexOf or remove duplicates with filter etc.

indexOf can’t find the value NaN .

ES6 has the Set constructor to create a set.

It’s fast and handles NaN correctly.

We can create a set by writing:

const set = new Set();
set.add('foo');

We create a set with the Set construction and call add to add an entry.

The has instance method checks whether the item exists.

We can write:

const hasFoo = set.has('foo');

hasFoo is true since we have an entry with value 'foo' .

The delete instance lets us delete items.

For instance, we can write:

const deletedFoo = set.delete('foo');

It takes the key as the argument. It returns true if the item is deleted.

The size property has the number of items in the set.

So if we have:

const set = new Set();
set.add('foo');

const size = set.size;

And size is 1.

The clear instance method lets us remove all entries from a set.

For example, we can write:

set.clear();

Setting Up a Set

We can get the set by passing in an array into the Set constructor.

For example, we can write:

const set = new Set(['foo', 'bar', 'baz']);

We can also use the add method to add the items:

const set = new Set().add('foo').add('bar').add('baz');

The Set constructor has zero or one argument.

If no arguments are passed in, then an empty set is created.

If one argument is passed in, then the argument has to be an iterable object.

Comparing Set Elements

We can compare set elements with the has method.

The algorithm works like === but NaN is equal to itself

For instance, if we write:

const set = new Set([NaN]);

Then we can check if with:

consrt hasNaN = set.has(NaN);

Add the same element a second has no effect.

For example, we can write:

const set = new Set();
set.add('bar');
set.add('bar');

const size = set.size;

The size would still be one after we added 'bar' twice.

2 objects are never considered equal.

This behavior can’t be changed.

So if we write:

const set = new Set();
set.add({});
set.add({});

const size = set.size;

Then size is 2.

Conclusion

WeakMaps have a much simpler API than Maps.

Sets can’t have duplicate values, but objects are never considered the same even if they have the same content.

Categories
Modern JavaScript

Best of Modern JavaScript — Set Operations

Since 2015, JavaScript has improved immensely.

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

In this article, we’ll look at sets.

Iterating Through Sets

We can iterate through sets with the for-of loop since sets are iterable objects.

For example, we can write:

const set = new Set(['foo', 'bar', 'baz']);
for (const x of set) {
  console.log(x);
}

Then we can write:

foo
bar
baz

logged.

The spread operator works with iterables to convert them to arrays.

This includes sets, so we can write:

const set = new Set(['foo', 'bar', 'baz']);
const arr = [...set];

and arr is [“foo”, “bar”, “baz”] .

Mapping and Filtering

To do map and filter operations, we can convert sets to arrays with the spread operator.

Then we can call the map and filter methods on those.

For example, we can map set entries to new values by writing:

const set = new Set([1, 2, 3]);
const squares = new Set([...set].map(x => x ** 2));

Then we get:

{1, 4, 9}

instead of:

{1, 2, 3}

To filter items, we can call the filter method on the array:

const set = new Set([1, 2, 3]);
const filtered = new Set([...set].filter(x => (x % 3) == 0));

We filtered out anything that isn’t evenly divisible by 3, so we get:

{3}

Union, Intersection, and Difference

We can compute union, intersection, and difference of 2 sets with various array operations.

Unions

A union is a set that has elements of both set a and b .

We can use the spread operator to create a union.

So we can write:

const a = new Set([1, 2, 3]);
const b = new Set([1, 3, 5]);
const union = new Set([...a, ...b]);

And union is {1, 2, 3, 5} .

The spread operator combines both sets into one array.

[...a, ...b] is equivalent to [...a].concat([...b]) .

The Set constructor converts that back into a set, and remove the duplicates in the process.

Intersection

A set intersection is a set that has elements in set a that’s also in set b .

To create an intersection from 2 sets, we can write:

const a = new Set([1, 2, 3]);
const b = new Set([1, 3, 5]);
const intersection = new Set(
  [...a].filter(x => b.has(x)));

We create 2 sets a and b .

Then we can get all the items in a that’s also in b with the filter method.

The has method checks if b also has the same item.

The Set constructor converts the array back to a set.

Therefore, we get:

{1, 3}

fot intersection .

Set Difference

The set difference lets us create a set from 2 sets a and b where the items in set a isn’t in set b .

To create the set difference, we can write:

const a = new Set([1, 2, 3]);
const b = new Set([1, 3, 5]);
const difference = new Set(
  [...a].filter(x => !b.has(x)));

We call filter with a callback that negates what’s returned in has to return all the items that aren’t in b but it’s in a .

So we get:

{2}

as the value of difference .

Conclusion

We can iterate through sets and computer the union, difference, and intersections with array operations.

Categories
Modern JavaScript

Best of Modern JavaScript — Set Operations

Since 2015, JavaScript has improved immensely.

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

In this article, we’ll look at sets.

Iterating Through Sets

We can iterate through sets with the for-of loop since sets are iterable objects.

For example, we can write:

const set = new Set(['foo', 'bar', 'baz']);
for (const x of set) {
  console.log(x);
}

Then we can write:

foo
bar
baz

logged.

The spread operator works with iterables to convert them to arrays.

This includes sets, so we can write:

const set = new Set(['foo', 'bar', 'baz']);
const arr = [...set];

and arr is [“foo”, “bar”, “baz”] .

Mapping and Filtering

To do map and filter operations, we can convert sets to arrays with the spread operator.

Then we can call the map and filter methods on those.

For example, we can map set entries to new values by writing:

const set = new Set([1, 2, 3]);
const squares = new Set([...set].map(x => x ** 2));

Then we get:

{1, 4, 9}

instead of:

{1, 2, 3}

To filter items, we can call the filter method on the array:

const set = new Set([1, 2, 3]);
const filtered = new Set([...set].filter(x => (x % 3) == 0));

We filtered out anything that isn’t evenly divisible by 3, so we get:

{3}

Union, Intersection, and Difference

We can compute union, intersection, and difference of 2 sets with various array operations.

Unions

A union is a set that has elements of both set a and b .

We can use the spread operator to create a union.

So we can write:

const a = new Set([1, 2, 3]);
const b = new Set([1, 3, 5]);
const union = new Set([...a, ...b]);

And union is {1, 2, 3, 5} .

The spread operator combines both sets into one array.

[...a, ...b] is equivalent to [...a].concat([...b]) .

The Set constructor converts that back into a set, and remove the duplicates in the process.

Intersection

A set intersection is a set that has elements in set a that’s also in set b .

To create an intersection from 2 sets, we can write:

const a = new Set([1, 2, 3]);
const b = new Set([1, 3, 5]);
const intersection = new Set(
  [...a].filter(x => b.has(x)));

We create 2 sets a and b .

Then we can get all the items in a that’s also in b with the filter method.

The has method checks if b also has the same item.

The Set constructor converts the array back to a set.

Therefore, we get:

{1, 3}

fot intersection .

Set Difference

The set difference lets us create a set from 2 sets a and b where the items in set a isn’t in set b .

To create the set difference, we can write:

const a = new Set([1, 2, 3]);
const b = new Set([1, 3, 5]);
const difference = new Set(
  [...a].filter(x => !b.has(x)));

We call filter with a callback that negates what’s returned in has to return all the items that aren’t in b but it’s in a .

So we get:

{2}

as the value of difference .

Conclusion

We can iterate through sets and computer the union, difference, and intersections with array operations.

Categories
Modern JavaScript

Best of Modern JavaScript — New Data Structures

Since 2015, JavaScript has improved immensely.

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

In this article, we’ll look at controlling the spreadability of arrays, maps, and sets.

Symbol.isConcatSpreadable

The Symbol.isConcatSpreadable isn’t part of any object in the ES6 standard library.

The mechanism exists purely for browser APIs and user code.

This means the subclasses of Array are spread by default.

Subclasses of Array can prevent instances from being spread by setting Symbol.isConcatSpreadable to fale .

This property can be an instance or a prototype property.

Other array-like objects are spread by concat if Symbol.isConcatSpreadable is true .

This means we can turn on spreading for some array-like objects.

Typed arrays aren’t spread and they don’t have the concat instance method.

Range of Array Indexes

ES6 follows the same rules as ES5 for the array index range.

Lengths are between 0 and 2 ** 32 — 1 .

And indexes are in the range between 0 and 2 ** 32 — 1 .

Strings and typed arrays have a larger range of indexes.

The upper bound of the range is because 2 ** 53 — 1 is the largest integer that JavaScript gloating point numbers can represent safely.

Maps and Sets

Maps and sets are new data structures introduced with ES6.

Maps can have arbitrary values. They’re stored as key-value pairs.

We can use an Array with entries having [key, value] pairs to set up the initial data.

For example, we can write:

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

We pass in an array of key-value pair arrays to the Map constructor to create our map.

A set is a collection of unique elements.

We can create a set with the Set constructor.

For example, we can write:

const arr = [2, 3, 5, 6, 6, 6, 2];
const unique = [...new Set(arr)];

then we get that unique is the array [2, 3, 5, 6] .

It’s useful for removing duplicate elements in an array.

Weak map is a map that doesn’t prevent its keys from being garbage collected.

This means that we won’t have to worry about memory leaks when using it.

Map

There’s no good way to create an object of key-value pairs with ES5 or earlier.

The only way is to create an object literal and use that as a map.

With ES6, we have the Map constructor.

To create a map, we can write:

const map = new Map();
map.set('foo', 'bar');

We create the map with the Map constructor.

Then we call the set method with the key and value as arguments to add or update the map with the entry.

If the entry with the key already exists, it’ll be overwritten.

The get method lets us get the item given the key.

So we can write:

const foo = map.get('foo');

and we get 'bar' .

The has method lets us check if an entry with the given key exists.

For example, we can write:

const hasFoo = map.has('foo');

then hasFoo would be true .

The delete method lets us delete the entry with the given key,

For example, we can write:

map.delete('foo')

to delete the entry with the key 'foo' .

Conclusion

We can control if an object if spreadable with the Array.prototype.concat method with the Symbol.isConcatSpreadable property.

Maps and sets are useful data structures that are introduced with ES6.