Thursday, September 19, 2024

Iterables in JavaScript



Key Concepts

Iterable: it is a sequence of elements that we can iterate over. In JavaScript, iterable must implement the ‘Symbol.iterator’ method.


Iterator Protocol: an object is an iterator when it implements a ‘next()’ method which returns an object with the properties:

value: next value in the iteration sequence.
done: A boolean to specify if the iteration is done.


Iterable Protocol: an object is iterable when it implements the ‘Symbol.iterator’ method.

Symbol.iterator: a built-in symbol that defines the default iterator for an object. In other words, an object that implements the ‘Symbol.iterator’ method becomes an iterator and gets the ‘next()’ method.

Built-in Iterable: JavaScript objects implementing collections such as ‘String’, ‘Array’, ‘TypedArray’, ‘Map’, and ‘Set’ are iterable because their prototype objects implement the ‘Symbol.iterator’ method.

Custom Iterable: new iterable objects can be defined by implementing the ‘Symbol.iterator’ method.

Custom Data Structure as Iterable: new data structures like stacks or queues can be created and made iterable by implementing the ‘Symbol.iterator’ method.

Generator: it is a specific type of function. The generator does not return a single value as a result like normal functions but multiple values over time. The generator can be used to implement the iterator and iterable protocol.

Iterable and Spread Syntax: a spread operator can be applied to an iterable to expand its elements.

Loop: the ‘for...of’ loop and ‘while’ loop can be used to iterate over iterators. However, only ‘for...of’ can used to loop through an iterable.


Note: it is possible to create a custom object that adheres to the protocol iterator but is not iterable. I.e, it can implement the ‘next()’ method but not the ‘Symbol.iterator’ method. This object is not iterable.

Usage Examples: Iterables

Iterable

Code Snippet

Code Output

String

let str = "Hi!";

let strIterator = str[Symbol.iterator]();

console.log(strIterator.next());

console.log(strIterator.next());

console.log(strIterator.next());

console.log(strIterator.next());

{value: 'H', done: false}

{value: 'i', done: false}

{value: '!', done: false}

{value: undefined, done: true}

Array

let numArray = [16, 27, 43];

let iterator = numArray[Symbol.iterator]();

console.log(iterator.next());

console.log(iterator.next());

console.log(iterator.next());

console.log(iterator.next());

{value: 16, done: false}

{value: 27, done: false}

{value: 43, done: false}

{value: undefined, done: true}

TypedArray

const uint8Array = new Uint8Array([10, 123, 45]);

let uint8Iter = uint8Array[Symbol.iterator]();

console.log(uint8Iter.next());

console.log(uint8Iter.next());

console.log(uint8Iter.next());

console.log(uint8Iter.next());

{value: 10, done: false}

{value: 123, done: false}

{value: 45, done: false}

{value: undefined, done: true}

Set

const fruits = new Set(["Orange", "Banana", "Strawberry"]);

let fruitIter = fruits[Symbol.iterator]();

console.log(fruitIter.next());

console.log(fruitIter.next());

console.log(fruitIter.next());

console.log(fruitIter.next());


{value: 'Orange', done: false}

{value: 'Banana', done: false}

{value: 'Strawberry', done: false}

{value: undefined, done: true}

Map

const fruitPrice = new Map([

  ["Orange", 3.25],

  ["Banana", 4.75],

["Strawberry", 6.5]

]);


let fruitPrIter = fruitPrice[Symbol.iterator]();

console.log(fruitPrIter.next());

console.log(fruitPrIter.next());

console.log(fruitPrIter.next());

console.log(fruitPrIter.next());


{value: Array(2), done: false}

{value: Array(2), done: false}

{value: Array(2), done: false}

{value: undefined, done: true}

Custom Data Structure (Stack)

class Stack {

constructor() {

this.items = [];

}

push(item) {

this.items.push(item);

}

pop() {

if (this.isEmpty()) {

return "Stack is empty";

}

return this.items.pop();

}

peek() {

if (this.isEmpty()) {

return "Stack is empty";

}

return this.items[this.items.length - 1];

}

isEmpty() {

return this.items.length === 0;

}

size() {

return this.items.length;

}

print() {

console.log(this.items.toString());

}

// Define the iterator method

[Symbol.iterator]() {

let index = this.items.length - 1;

let items = this.items;

return {

next() {

if (index >= 0) {

return { value: items[index--], done: false };

} else {

return { value: undefined, done: true };

}

}

};

}

}


// Create a new stack

let stack = new Stack();

stack.push(15);

stack.push(25);

stack.push(35);


let stackIter = stack[Symbol.iterator]();

console.log(stackIter.next());

console.log(stackIter.next());

console.log(stackIter.next());

console.log(stackIter.next());

{value: 35, done: false}

{value: 25, done: false}

{value: 15, done: false}

{value: undefined, done: true}

Generator

function* numGenerator() {

yield 100;

yield 200;

yield 300;

}


let numGen = numGenerator();

console.log(numGen.next());

console.log(numGen.next());

console.log(numGen.next());

console.log(numGen.next());

{value: 100, done: false}

{value: 200, done: false}

{value: 300, done: false}

{value: undefined, done: true}

Custom Iterable

let customIterable = {

data: [10, 15, 20],

[Symbol.iterator]() {

let index = 0;

let data = this.data;

return {

next() {

if (index < data.length) {

return { value: data[index++], done: false };

} else {

return { value: undefined, done: true };

}

}

};

}

};


let customIter = customIterable[Symbol.iterator]();

console.log(customIter.next());

console.log(customIter.next());

console.log(customIter.next());

console.log(customIter.next());

{value: 10, done: false}

{value: 15, done: false}

{value: 20, done: false}

{value: undefined, done: true}


Usage Examples: Action on Iterables

Action

Code Snippet

Code Output

Iterable with Spread Syntax

const spreadArray = [...customIterable];
console.log(spreadArray); 

[10, 15, 20]



Usage Examples: Loop through Iterables

Loop

Iterates over

Code Snippet

Code Output

for..of

String

const str = "Hi!";

for (const s of str) {

  console.log(s);

}

H

i

!


Array

const nums = [16, 27, 43];

for (const v of nums) {

  console.log(v);

}


16

27

43


TypedArray

const uint8Array = new Uint8Array([10, 123, 45]);

for (const v of uint8Array) {

  console.log(v);

}


10

123

45


Set

const fruits = new Set(["Orange", "Banana", "Strawberry"]);

for (const f of fruits) {

   console.log(f);

}


Orange

Banana

Strawberry


Map

const fruitPrice = new Map([

  ["Orange", 3.25],

  ["Banana", 4.75],

["Strawberry", 6.5]

]);

for (const f of fruitPrice) {

   console.log(f);

}


['Orange', 3.25]

['Banana', 4.75]

['Strawberry', 6.5]


Custom Data Structure (Stack)

for (let v of stack) {

console.log(v);

}


35

25

15


Generator

for (let v of numGen){

console.log(v);

}


100

200

300

while

Any Iterable (Built-in, Custom or Generator)

let iter = customIterable[Symbol.iterator]();

while (true) {

  const result = iter.next();

console.log('value:', result.value, 'done: ', result.done);

  if (result.done) break;

}

value: 10 done: false

value: 15 done: false

value: 20 done: false

value: undefined done: true



Usage Examples: Iterators

Iterator (not Iterable)

Code Snippet

Code

Custom Iterator

function digitsIteration() {

let digit = 0;

return {

next: function() {

digit += 1;

if (digit < 10) {

return { value: digit, done: false };

} else {

return { value: undefined, done: true };

}

}

};

}


const digit = digitsIteration();

while (true) {

  const result = digit.next();

console.log('value:', result.value, 'done: ', result.done);

  if (result.done) break;

}

value: 1 done: false

value: 2 done: false

value: 3 done: false

value: 4 done: false

value: 5 done: false

value: 6 done: false

value: 7 done: false

value: 8 done: false

value: 9 done: false

value: undefined done: true

No comments:

Post a Comment

Blog Posts

Enhancing Performance of Java-Web Applications

Applications built with a Java back-end, a relational database (such as Oracle or MySQL), and a JavaScript-based front-end form a common and...