javascript
examples
examples.js⚡javascript
// ============================================
// 17.4 Iterators and Iterables - Examples
// ============================================
// --------------------------------------------
// 1. Built-in Iterables
// --------------------------------------------
// String iteration
const str = 'Hello';
for (const char of str) {
console.log(char); // 'H', 'e', 'l', 'l', 'o'
}
// Array iteration
const arr = [1, 2, 3];
for (const item of arr) {
console.log(item); // 1, 2, 3
}
// Map iteration
const map = new Map([
['a', 1],
['b', 2],
]);
for (const [key, value] of map) {
console.log(key, value); // 'a' 1, 'b' 2
}
// Set iteration
const set = new Set([1, 2, 3]);
for (const item of set) {
console.log(item); // 1, 2, 3
}
// --------------------------------------------
// 2. Manual Iterator Usage
// --------------------------------------------
const numbers = [10, 20, 30];
// Get the iterator
const iterator = numbers[Symbol.iterator]();
// Call next() manually
console.log(iterator.next()); // { value: 10, done: false }
console.log(iterator.next()); // { value: 20, done: false }
console.log(iterator.next()); // { value: 30, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
// --------------------------------------------
// 3. Custom Iterable Object
// --------------------------------------------
const range = {
start: 1,
end: 5,
[Symbol.iterator]() {
let current = this.start;
const last = this.end;
return {
next() {
if (current <= last) {
return { value: current++, done: false };
}
return { done: true };
},
};
},
};
console.log('Range iteration:');
for (const num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
// Works with spread
console.log([...range]); // [1, 2, 3, 4, 5]
// Works with destructuring
const [first, second] = range;
console.log(first, second); // 1, 2
// Works with Array.from
console.log(Array.from(range)); // [1, 2, 3, 4, 5]
// --------------------------------------------
// 4. Iterable Class
// --------------------------------------------
class Range {
constructor(start, end, step = 1) {
this.start = start;
this.end = end;
this.step = step;
}
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
const step = this.step;
return {
next() {
if (current <= end) {
const value = current;
current += step;
return { value, done: false };
}
return { done: true };
},
};
}
}
const evens = new Range(0, 10, 2);
console.log([...evens]); // [0, 2, 4, 6, 8, 10]
// --------------------------------------------
// 5. Iterator with Return Method
// --------------------------------------------
function createResourceIterator(data) {
let index = 0;
console.log('Resource opened');
return {
[Symbol.iterator]() {
return this;
},
next() {
if (index < data.length) {
return { value: data[index++], done: false };
}
return { done: true };
},
return() {
console.log('Resource closed (cleanup)');
return { done: true };
},
};
}
const resource = createResourceIterator([1, 2, 3, 4, 5]);
for (const item of resource) {
console.log(item);
if (item === 3) break; // Triggers return()
}
// Output: Resource opened, 1, 2, 3, Resource closed (cleanup)
// --------------------------------------------
// 6. Infinite Iterator
// --------------------------------------------
const fibonacci = {
[Symbol.iterator]() {
let prev = 0,
curr = 1;
return {
next() {
const value = curr;
[prev, curr] = [curr, prev + curr];
return { value, done: false };
},
};
},
};
// Take first 10 fibonacci numbers
const fibArray = [];
for (const num of fibonacci) {
if (fibArray.length >= 10) break;
fibArray.push(num);
}
console.log(fibArray); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
// --------------------------------------------
// 7. Iterator Helper Functions
// --------------------------------------------
// Take first N items from any iterable
function* take(iterable, n) {
let count = 0;
for (const item of iterable) {
if (count >= n) return;
yield item;
count++;
}
}
console.log([...take([1, 2, 3, 4, 5], 3)]); // [1, 2, 3]
// Filter iterable
function* filter(iterable, predicate) {
for (const item of iterable) {
if (predicate(item)) {
yield item;
}
}
}
console.log([...filter([1, 2, 3, 4, 5], (x) => x % 2 === 0)]); // [2, 4]
// Map iterable
function* map(iterable, fn) {
for (const item of iterable) {
yield fn(item);
}
}
console.log([...map([1, 2, 3], (x) => x * 2)]); // [2, 4, 6]
// --------------------------------------------
// 8. Combining Iterators
// --------------------------------------------
function* concat(...iterables) {
for (const iterable of iterables) {
yield* iterable;
}
}
const combined = concat([1, 2], [3, 4], [5, 6]);
console.log([...combined]); // [1, 2, 3, 4, 5, 6]
function* zip(...iterables) {
const iterators = iterables.map((it) => it[Symbol.iterator]());
while (true) {
const results = iterators.map((it) => it.next());
if (results.some((r) => r.done)) return;
yield results.map((r) => r.value);
}
}
const zipped = zip([1, 2, 3], ['a', 'b', 'c']);
console.log([...zipped]); // [[1, 'a'], [2, 'b'], [3, 'c']]
// --------------------------------------------
// 9. Two-Way Iterator (Iterable Iterator)
// --------------------------------------------
class Counter {
constructor(max) {
this.max = max;
this.current = 0;
}
[Symbol.iterator]() {
return this;
}
next() {
if (this.current < this.max) {
return { value: this.current++, done: false };
}
return { done: true };
}
// Reset for re-iteration
reset() {
this.current = 0;
return this;
}
}
const counter = new Counter(3);
console.log([...counter]); // [0, 1, 2]
console.log([...counter]); // [] - exhausted!
counter.reset();
console.log([...counter]); // [0, 1, 2] - works again
// --------------------------------------------
// 10. Linked List Iterator
// --------------------------------------------
class LinkedList {
constructor() {
this.head = null;
this.tail = null;
}
append(value) {
const node = { value, next: null };
if (!this.head) {
this.head = this.tail = node;
} else {
this.tail.next = node;
this.tail = node;
}
return this;
}
[Symbol.iterator]() {
let current = this.head;
return {
next() {
if (current) {
const value = current.value;
current = current.next;
return { value, done: false };
}
return { done: true };
},
};
}
}
const list = new LinkedList().append('a').append('b').append('c');
console.log([...list]); // ['a', 'b', 'c']
for (const item of list) {
console.log(item); // 'a', 'b', 'c'
}
// --------------------------------------------
// 11. Tree Iterator (DFS)
// --------------------------------------------
class TreeNode {
constructor(value, children = []) {
this.value = value;
this.children = children;
}
*[Symbol.iterator]() {
yield this.value;
for (const child of this.children) {
yield* child;
}
}
}
const tree = new TreeNode('root', [
new TreeNode('child1', [
new TreeNode('grandchild1'),
new TreeNode('grandchild2'),
]),
new TreeNode('child2'),
]);
console.log([...tree]);
// ['root', 'child1', 'grandchild1', 'grandchild2', 'child2']
// --------------------------------------------
// 12. Lazy Evaluation with Iterators
// --------------------------------------------
function* numbersFrom(start) {
let n = start;
while (true) {
yield n++;
}
}
function* takeWhile(iterable, predicate) {
for (const item of iterable) {
if (!predicate(item)) return;
yield item;
}
}
// Get numbers from 1 while less than 10
const nums = takeWhile(numbersFrom(1), (n) => n < 10);
console.log([...nums]); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// --------------------------------------------
// 13. Object.entries/keys/values Iteration
// --------------------------------------------
const person = { name: 'Alice', age: 30, city: 'NYC' };
// Object.entries returns iterable
for (const [key, value] of Object.entries(person)) {
console.log(`${key}: ${value}`);
}
// Object.keys returns iterable
for (const key of Object.keys(person)) {
console.log(key);
}
// Object.values returns iterable
for (const value of Object.values(person)) {
console.log(value);
}
// --------------------------------------------
// 14. Making Any Object Iterable
// --------------------------------------------
const obj = {
data: ['a', 'b', 'c'],
[Symbol.iterator]() {
return this.data[Symbol.iterator]();
},
};
console.log([...obj]); // ['a', 'b', 'c']
// --------------------------------------------
// 15. Checking if Something is Iterable
// --------------------------------------------
function isIterable(obj) {
return obj != null && typeof obj[Symbol.iterator] === 'function';
}
console.log(isIterable([1, 2, 3])); // true
console.log(isIterable('hello')); // true
console.log(isIterable(new Map())); // true
console.log(isIterable({ a: 1 })); // false
console.log(isIterable(123)); // false
console.log(isIterable(null)); // false
// Make plain object iterable
function makeIterable(obj) {
obj[Symbol.iterator] = function* () {
for (const key of Object.keys(this)) {
yield [key, this[key]];
}
};
return obj;
}
const iterableObj = makeIterable({ x: 1, y: 2 });
console.log([...iterableObj]); // [['x', 1], ['y', 2]]