javascript

exercises

exercises.js
// ============================================
// 17.5 Generators - Exercises
// ============================================

// Exercise 1: Basic Generator
// Create a generator that yields 'a', 'b', 'c'

// Your code here:
// function* abc() {
//     ...
// }

// console.log([...abc()]);  // ['a', 'b', 'c']

/*
Solution:
function* abc() {
    yield 'a';
    yield 'b';
    yield 'c';
}

console.log([...abc()]);  // ['a', 'b', 'c']
*/

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

// Exercise 2: Range Generator
// Create a generator that yields numbers from start to end

// Your code here:
// function* range(start, end) {
//     ...
// }

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

/*
Solution:
function* range(start, end) {
    for (let i = start; i <= end; i++) {
        yield i;
    }
}

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

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

// Exercise 3: Countdown Generator
// Create a generator that counts down from n to 1

// Your code here:
// function* countdown(n) {
//     ...
// }

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

/*
Solution:
function* countdown(n) {
    while (n >= 1) {
        yield n--;
    }
}

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

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

// Exercise 4: Infinite Counter
// Create an infinite generator that counts from 0
// Use it with a take function to get first 5 values

// Your code here:
// function* infiniteCounter() {
//     ...
// }

// function take(gen, n) {
//     ...
// }

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

/*
Solution:
function* infiniteCounter() {
    let n = 0;
    while (true) {
        yield n++;
    }
}

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

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

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

// Exercise 5: Fibonacci Generator
// Create a generator that yields Fibonacci numbers

// Your code here:
// function* fibonacci() {
//     ...
// }

// Using take: first 8 fibonacci numbers should be [1, 1, 2, 3, 5, 8, 13, 21]

/*
Solution:
function* fibonacci() {
    let prev = 0, curr = 1;
    while (true) {
        yield curr;
        [prev, curr] = [curr, prev + curr];
    }
}

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

console.log(take(fibonacci(), 8));  // [1, 1, 2, 3, 5, 8, 13, 21]
*/

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

// Exercise 6: Powers Generator
// Create a generator that yields powers of a base (base^0, base^1, base^2, ...)

// Your code here:
// function* powers(base) {
//     ...
// }

// console.log(take(powers(2), 5));  // [1, 2, 4, 8, 16]

/*
Solution:
function* powers(base) {
    let exp = 0;
    while (true) {
        yield base ** exp;
        exp++;
    }
}

console.log(take(powers(2), 5));  // [1, 2, 4, 8, 16]
console.log(take(powers(3), 4));  // [1, 3, 9, 27]
*/

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

// Exercise 7: yield* Delegation
// Create a generator that yields 1, 2, then delegates to [3, 4, 5], then yields 6

// Your code here:
// function* combined() {
//     ...
// }

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

/*
Solution:
function* combined() {
    yield 1;
    yield 2;
    yield* [3, 4, 5];
    yield 6;
}

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

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

// Exercise 8: Flatten Generator
// Create a generator that flattens nested arrays using yield*

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

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

/*
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, 6]]])]);  // [1, 2, 3, 4, [5, 6]]
*/

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

// Exercise 9: Deep Flatten Generator
// Create a generator that deeply flattens nested arrays

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

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

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

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

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

// Exercise 10: Two-way Communication
// Create a generator that takes input and yields processed output

// Your code here:
// function* echo() {
//     let input = yield 'Ready';
//     while (input !== 'quit') {
//         input = yield `You said: ${input}`;
//     }
//     return 'Goodbye';
// }

// Test it by calling next() with different values

/*
Solution:
function* echo() {
    let input = yield 'Ready';
    while (input !== 'quit') {
        input = yield `You said: ${input}`;
    }
    return 'Goodbye';
}

const gen = echo();
console.log(gen.next());          // { value: 'Ready', done: false }
console.log(gen.next('Hello'));   // { value: 'You said: Hello', done: false }
console.log(gen.next('World'));   // { value: 'You said: World', done: false }
console.log(gen.next('quit'));    // { value: 'Goodbye', done: true }
*/

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

// Exercise 11: ID Generator
// Create a generator that produces unique IDs with a prefix

// Your code here:
// function* idGenerator(prefix) {
//     ...
// }

// const userIds = idGenerator('user');
// console.log(userIds.next().value);  // 'user_1'
// console.log(userIds.next().value);  // 'user_2'

/*
Solution:
function* idGenerator(prefix) {
    let id = 1;
    while (true) {
        yield `${prefix}_${id++}`;
    }
}

const userIds = idGenerator('user');
console.log(userIds.next().value);  // 'user_1'
console.log(userIds.next().value);  // 'user_2'
console.log(userIds.next().value);  // 'user_3'
*/

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

// Exercise 12: Filter Generator
// Create a generator that filters values from another iterable

// 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]
*/

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

// Exercise 13: Map Generator
// Create a generator that maps values from another iterable

// Your code here:
// function* map(iterable, fn) {
//     ...
// }

// console.log([...map([1, 2, 3], x => x * 2)]);  // [2, 4, 6]

/*
Solution:
function* map(iterable, fn) {
    for (const item of iterable) {
        yield fn(item);
    }
}

console.log([...map([1, 2, 3], x => x * 2)]);  // [2, 4, 6]
console.log([...map(['a', 'b'], s => s.toUpperCase())]);  // ['A', 'B']
*/

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

// Exercise 14: Zip Generator
// Create a generator that zips two iterables together

// Your code here:
// function* zip(iter1, iter2) {
//     ...
// }

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

/*
Solution:
function* zip(iter1, iter2) {
    const it1 = iter1[Symbol.iterator]();
    const it2 = iter2[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 15: Cycle Generator
// Create a generator that cycles through an array infinitely

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

// const colors = cycle(['red', 'green', 'blue']);
// console.log(colors.next().value);  // 'red'
// console.log(colors.next().value);  // 'green'
// console.log(colors.next().value);  // 'blue'
// console.log(colors.next().value);  // 'red' (cycles back)

/*
Solution:
function* cycle(arr) {
    while (true) {
        yield* arr;
    }
}

const colors = cycle(['red', 'green', 'blue']);
console.log(colors.next().value);  // 'red'
console.log(colors.next().value);  // 'green'
console.log(colors.next().value);  // 'blue'
console.log(colors.next().value);  // 'red'
console.log(colors.next().value);  // 'green'
*/

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

// Bonus Exercise: Paginated Data Generator
// Create a generator that simulates paginated API responses

// Your code here:
// function* paginate(data, pageSize) {
//     ...
// }

// const pages = paginate([1,2,3,4,5,6,7,8,9,10], 3);
// Each next() should return { page: N, items: [...], hasMore: bool }

/*
Solution:
function* paginate(data, pageSize) {
    const totalPages = Math.ceil(data.length / pageSize);
    
    for (let page = 1; page <= totalPages; page++) {
        const start = (page - 1) * pageSize;
        const end = start + pageSize;
        
        yield {
            page,
            items: data.slice(start, end),
            hasMore: page < totalPages
        };
    }
}

const pages = paginate([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 3);
console.log(pages.next().value);  // { page: 1, items: [1, 2, 3], hasMore: true }
console.log(pages.next().value);  // { page: 2, items: [4, 5, 6], hasMore: true }
console.log(pages.next().value);  // { page: 3, items: [7, 8, 9], hasMore: true }
console.log(pages.next().value);  // { page: 4, items: [10], hasMore: false }
*/
Exercises - JavaScript Tutorial | DeepML