TypeScript: Complex Data Types, JSON, Map, Filter, Reduce

Solutions to Practical Session Exercises - Week #1

Exercise 1: Implementing map with reduce

const mapExercise = (f, elements) =>
    elements.reduce((acc, curr) => { // reducer
        acc.push(f(curr));
        return acc;
    }, []);

console.log(mapExercise(x => x * x, [0, 1, 2, 3, 4, 5, 6])); // ==> [ 0, 1, 4, 9, 16, 25, 36 ]

Note: this version uses mutation in the reducer (acc.push(...)).

Rewrite this to avoid mutation.

Exercise 2: Implementing filter with reduce

function filterExercise(pred, elements) {
    return elements.reduce((acc, curr) => {
        if (pred(curr)) {
            acc.push(curr);
        }
        return acc;
    }, []);
}

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

// Using the more compact => notation and the ternary ( test ? then : else ) expression
const filter2 = (pred, elements) =>
    elements.reduce((acc, cur) => (pred(cur) ? acc.concat(cur) : acc), []);

filter2(x => x % 2 === 0, [0, 1, 2, 3, 4]); // ==> [ 0, 2, 4 ]

// Using the Ramda reduce operator instead of the array method elements.reduce()
// With TypeScript type declarations - note the usage of Array<T>() instead of [] to denote an empty array of type T[]
import { reduce } from "ramda";
const filter3 = <T>(pred: (x: T) => boolean, elements: T[]) =>
    reduce(
        (acc, cur) => (pred(cur) ? acc.concat(cur) : acc),
        Array<T>(),
        elements
    );

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

Note: Implement filter using chain and without mutation (no push()).

Exercise 3: Implementing some and every with map and reduce

const someExercise = (pred,arr) => arr.map(pred).reduce((acc,curr) => acc || curr, false);
const even = (x) => x % 2 === 0;
const arr1 = [0, 1, 2, 3];
const arr2 = [1, 3];

console.log(`arr1HasEvenNumbers = ${someExercise(even, arr1)}`); // ==> arr1HasEvenNumbers = true
console.log(`arr2HasEvenNumbers = ${someExercise(even, arr2)}`); // ==> arr2HasEvenNumbers = false

function everyExercise(pred, arr) {
    return arr.map(pred).reduce((acc, curr) => {
        return acc && curr;
    }, true);
}

console.log(`allInArr1AreEven = ${everyExercise(even, arr1)}`); // ==> allInArr1AreEven = false
console.log(`allInArr3AreEven = ${everyExercise(even, arr3)}`); // ==> allInArr3AreEven = true

// Every with ramda map and reduce and TypeScript types.
import { map, reduce } from "ramda";

const even = (x: number) => x % 2 === 0;
const arr1 = [0, 1, 2, 3];
const arr2 = [0, 2];

// Note the requirement to cast 'true' as boolean
const everyExercise2 = <T>(pred: (x: T) => boolean, arr: T[]) =>
    reduce(
        (acc: boolean, cur: boolean) => acc && cur,
        <boolean>true,
        map(pred, arr)
    );

console.log(`allInArr1AreEven = ${everyExercise2(even, arr1)}`); // ==> allInArr1AreEven = false
console.log(`allInArr2AreEven = ${everyExercise2(even, arr2)}`); // ==> allInArr2AreEven = true