javascript

exercises

exercises.js
// ============================================
// 17.4 Iterators and Iterables - Exercises
// ============================================

// Exercise 1: Manual Iterator Usage
// Get the iterator from the array and call next() three times

const colors = ['red', 'green', 'blue'];

// Your code here:
// const iterator = colors[Symbol.iterator]();
// Call next() and log the results

/*
Solution:
const iterator = colors[Symbol.iterator]();
console.log(iterator.next());  // { value: 'red', done: false }
console.log(iterator.next());  // { value: 'green', done: false }
console.log(iterator.next());  // { value: 'blue', done: false }
console.log(iterator.next());  // { value: undefined, done: true }
*/

// --------------------------------------------

// Exercise 2: Simple Custom Iterable
// Create an iterable object that yields numbers 1 to 3

// Your code here:
// const simpleIterable = {
//     [Symbol.iterator]() {
//         ...
//     }
// };

// for (const num of simpleIterable) {
//     console.log(num);  // 1, 2, 3
// }

/*
Solution:
const simpleIterable = {
    [Symbol.iterator]() {
        let current = 1;
        return {
            next() {
                if (current <= 3) {
                    return { value: current++, done: false };
                }
                return { done: true };
            }
        };
    }
};

for (const num of simpleIterable) {
    console.log(num);  // 1, 2, 3
}
*/

// --------------------------------------------

// Exercise 3: Range Class
// Create a Range class that's iterable from start to end

// Your code here:
// class Range {
//     constructor(start, end) {
//         ...
//     }
//     [Symbol.iterator]() {
//         ...
//     }
// }

// const range = new Range(5, 10);
// console.log([...range]);  // [5, 6, 7, 8, 9, 10]

/*
Solution:
class Range {
    constructor(start, end) {
        this.start = start;
        this.end = end;
    }
    
    [Symbol.iterator]() {
        let current = this.start;
        const end = this.end;
        
        return {
            next() {
                if (current <= end) {
                    return { value: current++, done: false };
                }
                return { done: true };
            }
        };
    }
}

const range = new Range(5, 10);
console.log([...range]);  // [5, 6, 7, 8, 9, 10]
*/

// --------------------------------------------

// Exercise 4: Countdown Iterator
// Create an iterable that counts down from a number to 1

// Your code here:
// function countdown(start) {
//     return {
//         [Symbol.iterator]() {
//             ...
//         }
//     };
// }

// console.log([...countdown(5)]);  // [5, 4, 3, 2, 1]

/*
Solution:
function countdown(start) {
    return {
        [Symbol.iterator]() {
            let current = start;
            return {
                next() {
                    if (current >= 1) {
                        return { value: current--, done: false };
                    }
                    return { done: true };
                }
            };
        }
    };
}

console.log([...countdown(5)]);  // [5, 4, 3, 2, 1]
*/

// --------------------------------------------

// Exercise 5: Step Range
// Create an iterable that goes from start to end with a step

// Your code here:
// function stepRange(start, end, step) {
//     return {
//         ...
//     };
// }

// console.log([...stepRange(0, 10, 2)]);  // [0, 2, 4, 6, 8, 10]
// console.log([...stepRange(1, 10, 3)]);  // [1, 4, 7, 10]

/*
Solution:
function stepRange(start, end, step = 1) {
    return {
        [Symbol.iterator]() {
            let current = start;
            return {
                next() {
                    if (current <= end) {
                        const value = current;
                        current += step;
                        return { value, done: false };
                    }
                    return { done: true };
                }
            };
        }
    };
}

console.log([...stepRange(0, 10, 2)]);  // [0, 2, 4, 6, 8, 10]
console.log([...stepRange(1, 10, 3)]);  // [1, 4, 7, 10]
*/

// --------------------------------------------

// Exercise 6: Character Iterator
// Create an iterable that yields characters in a range (e.g., 'a' to 'e')

// Your code here:
// function charRange(startChar, endChar) {
//     ...
// }

// console.log([...charRange('a', 'e')]);  // ['a', 'b', 'c', 'd', 'e']

/*
Solution:
function charRange(startChar, endChar) {
    const start = startChar.charCodeAt(0);
    const end = endChar.charCodeAt(0);
    
    return {
        [Symbol.iterator]() {
            let current = start;
            return {
                next() {
                    if (current <= end) {
                        return { 
                            value: String.fromCharCode(current++), 
                            done: false 
                        };
                    }
                    return { done: true };
                }
            };
        }
    };
}

console.log([...charRange('a', 'e')]);  // ['a', 'b', 'c', 'd', 'e']
console.log([...charRange('A', 'F')]);  // ['A', 'B', 'C', 'D', 'E', 'F']
*/

// --------------------------------------------

// Exercise 7: Take Function
// Create a take function that returns first N items from an iterable

// Your code here:
// function take(iterable, n) {
//     ...
// }

// console.log(take([1, 2, 3, 4, 5], 3));  // [1, 2, 3]

/*
Solution:
function take(iterable, n) {
    const result = [];
    const iterator = iterable[Symbol.iterator]();
    
    for (let i = 0; i < n; i++) {
        const { value, done } = iterator.next();
        if (done) break;
        result.push(value);
    }
    
    return result;
}

console.log(take([1, 2, 3, 4, 5], 3));  // [1, 2, 3]
console.log(take([1, 2], 5));  // [1, 2]
*/

// --------------------------------------------

// Exercise 8: Drop Function
// Create a drop function that skips first N items from an iterable

// Your code here:
// function drop(iterable, n) {
//     ...
// }

// console.log([...drop([1, 2, 3, 4, 5], 2)]);  // [3, 4, 5]

/*
Solution:
function* drop(iterable, n) {
    let count = 0;
    for (const item of iterable) {
        if (count >= n) {
            yield item;
        }
        count++;
    }
}

console.log([...drop([1, 2, 3, 4, 5], 2)]);  // [3, 4, 5]
*/

// --------------------------------------------

// Exercise 9: Repeat Iterator
// Create an iterable that repeats a value N times

// Your code here:
// function repeat(value, times) {
//     ...
// }

// console.log([...repeat('hello', 3)]);  // ['hello', 'hello', 'hello']

/*
Solution:
function repeat(value, times) {
    return {
        [Symbol.iterator]() {
            let count = 0;
            return {
                next() {
                    if (count < times) {
                        count++;
                        return { value, done: false };
                    }
                    return { done: true };
                }
            };
        }
    };
}

console.log([...repeat('hello', 3)]);  // ['hello', 'hello', 'hello']
*/

// --------------------------------------------

// Exercise 10: Cycle Iterator (Infinite)
// Create an iterator that cycles through an array infinitely
// Use with take() to avoid infinite loop

// Your code here:
// function cycle(arr) {
//     ...
// }

// const cycled = cycle([1, 2, 3]);
// console.log(take(cycled, 7));  // [1, 2, 3, 1, 2, 3, 1]

/*
Solution:
function cycle(arr) {
    return {
        [Symbol.iterator]() {
            let index = 0;
            return {
                next() {
                    const value = arr[index % arr.length];
                    index++;
                    return { value, done: false };
                }
            };
        }
    };
}

function take(iterable, n) {
    const result = [];
    for (const item of iterable) {
        if (result.length >= n) break;
        result.push(item);
    }
    return result;
}

const cycled = cycle([1, 2, 3]);
console.log(take(cycled, 7));  // [1, 2, 3, 1, 2, 3, 1]
*/

// --------------------------------------------

// Exercise 11: Flatten Iterator
// Create an iterator that flattens nested arrays one level

// Your code here:
// function flatten(arr) {
//     ...
// }

// console.log([...flatten([[1, 2], [3, 4], [5]])]);  // [1, 2, 3, 4, 5]

/*
Solution:
function* flatten(arr) {
    for (const item of arr) {
        if (Array.isArray(item)) {
            yield* item;
        } else {
            yield item;
        }
    }
}

console.log([...flatten([[1, 2], [3, 4], [5]])]);  // [1, 2, 3, 4, 5]
console.log([...flatten([1, [2, 3], 4, [5, 6]])]);  // [1, 2, 3, 4, 5, 6]
*/

// --------------------------------------------

// Exercise 12: Zip Iterator
// Create an iterator that combines two arrays into pairs

// Your code here:
// function zip(arr1, arr2) {
//     ...
// }

// console.log([...zip([1, 2, 3], ['a', 'b', 'c'])]);  
// // [[1, 'a'], [2, 'b'], [3, 'c']]

/*
Solution:
function* zip(arr1, arr2) {
    const it1 = arr1[Symbol.iterator]();
    const it2 = arr2[Symbol.iterator]();
    
    while (true) {
        const r1 = it1.next();
        const r2 = it2.next();
        
        if (r1.done || r2.done) return;
        
        yield [r1.value, r2.value];
    }
}

console.log([...zip([1, 2, 3], ['a', 'b', 'c'])]);  
// [[1, 'a'], [2, 'b'], [3, 'c']]
*/

// --------------------------------------------

// Exercise 13: Filter Iterator
// Create an iterator that filters based on a predicate

// Your code here:
// function* filter(iterable, predicate) {
//     ...
// }

// console.log([...filter([1, 2, 3, 4, 5], x => x % 2 === 0)]);  // [2, 4]

/*
Solution:
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]
console.log([...filter(['a', 'bb', 'ccc'], s => s.length > 1)]);  // ['bb', 'ccc']
*/

// --------------------------------------------

// Exercise 14: Check if Iterable
// Create a function that checks if a value is iterable

// Your code here:
// function isIterable(value) {
//     ...
// }

// console.log(isIterable([1, 2, 3]));  // true
// console.log(isIterable('hello'));   // true
// console.log(isIterable(123));       // false
// console.log(isIterable({ a: 1 }));  // false

/*
Solution:
function isIterable(value) {
    return value != null && typeof value[Symbol.iterator] === 'function';
}

console.log(isIterable([1, 2, 3]));  // true
console.log(isIterable('hello'));   // true
console.log(isIterable(new Map())); // true
console.log(isIterable(new Set())); // true
console.log(isIterable(123));       // false
console.log(isIterable({ a: 1 }));  // false
console.log(isIterable(null));      // false
*/

// --------------------------------------------

// Exercise 15: Make Object Iterable
// Make a plain object iterable over its entries

// Your code here:
// function makeIterable(obj) {
//     ...
//     return obj;
// }

// const person = makeIterable({ name: 'Alice', age: 30 });
// for (const [key, value] of person) {
//     console.log(`${key}: ${value}`);
// }

/*
Solution:
function makeIterable(obj) {
    obj[Symbol.iterator] = function() {
        const entries = Object.entries(this);
        let index = 0;
        
        return {
            next() {
                if (index < entries.length) {
                    return { value: entries[index++], done: false };
                }
                return { done: true };
            }
        };
    };
    return obj;
}

const person = makeIterable({ name: 'Alice', age: 30, city: 'NYC' });
for (const [key, value] of person) {
    console.log(`${key}: ${value}`);
}
// name: Alice
// age: 30
// city: NYC
*/
Exercises - JavaScript Tutorial | DeepML