Recursion Practice using first and rest (Solutions)
Table of Contents
1 How To
- Create a file with extension .js
- Copy a code snippet to the file and then execute in the command line:
node filename.js
2 first and rest
Using the destructuring assignment syntax we can get the "first element of an array" and "the rest of the array". I think that using "first and rest" makes it easier to think recursively.
let arr = [1,2,3,4] const [first, ...rest] = arr; console.log('first: ', first); console.log('rest: ', rest);
first: 1 rest: [ 2, 3, 4 ]
3 check
The check function is only a wrapper for assert (Node.js). I wrote it to make more readable the tests and outputs.
'use strict'; const assert = require("assert"); function check(message) { return { fn(fnToCheck) { return { toBe: { msg : message != undefined ? message : '', actual: fnToCheck, fnExeAssert(fnAssert,actual, expected) { try { fnAssert(actual, expected); console.log('PASS: ' + this.msg + '\n' ); } catch(e) { console.log('FAIL: ' + this.msg ); console.log(' expected: ', e.expected, '\n actual: ', e.actual, '\n'); } }, deepStrictEqual (expected) { this.fnExeAssert(assert.deepStrictEqual, this.actual, expected) }, strictEqual (expected) { this.fnExeAssert(assert.strictEqual, this.actual, expected) } } } } } }
4 remove first ocurrence
Write a function that removes the first occurrence of an number m from an array of numbers.
If m does not occur in the array, the array remains unchanged.
'use strict'; const assert = require("assert"); function check(message) { return { fn(fnToCheck) { return { toBe: { msg : message != undefined ? message : '', actual: fnToCheck, fnExeAssert(fnAssert,actual, expected) { try { fnAssert(actual, expected); console.log('PASS: ' + this.msg + '\n' ); } catch(e) { console.log('FAIL: ' + this.msg ); console.log(' expected: ', e.expected, '\n actual: ', e.actual, '\n'); } }, deepStrictEqual (expected) { this.fnExeAssert(assert.deepStrictEqual, this.actual, expected) }, strictEqual (expected) { this.fnExeAssert(assert.strictEqual, this.actual, expected) } } } } } } check("removeFirstOcurrence(5, []) should return an empty array") .fn(removeFirstOcurrence(5, [])) .toBe .deepStrictEqual([]); check("removeFirstOcurrence(5, [5]) should return an empty array") .fn(removeFirstOcurrence(5, [5])) .toBe .deepStrictEqual([]); check("removeFirstOcurrence(5, [1]) should return the original array without changes") .fn(removeFirstOcurrence(5, [1])) .toBe .deepStrictEqual([1]); check("removeFirstOcurrence(5, [5,5]) should return an array with only one 5") .fn(removeFirstOcurrence(5, [5,5])) .toBe .deepStrictEqual([5]); check("removeFirstOcurrence(5, [1,5,5,2,3]) should return an array without the first ocurrence of 5") .fn(removeFirstOcurrence(5, [1,5,5,2,3])) .toBe .deepStrictEqual([1,5,2,3]); check( "removeFirstOcurrence(5, [5,1,2,3]) should return an array without 5") .fn(removeFirstOcurrence(5, [5,1,2,3])) .toBe .deepStrictEqual([1,2,3]); check("removeFirstOcurrence(5, [1,2,5,3]) should return an array without 5") .fn(removeFirstOcurrence(5, [1,2,5,3])) .toBe .deepStrictEqual([1,2,3]); check("removeFirstOcurrence(5, [1,2,3,5]) should return an array without 5") .fn(removeFirstOcurrence(5, [1,2,3,5])) .toBe .deepStrictEqual([1,2,3]); /** * Removes the first occurrence of an number m from an array of numbers. * If m does not occur in the array, the array remains unchanged. * @param {number} m - the number to remove * @param {array} arr - the array of numbers * @returns {array} - the array without the first ocurrence of m or the original array */ function removeFirstOcurrence(m, arr) { if(arr.length === 0 ) return [] const [first, ...rest] = arr; if(m === first) { return rest; } else { let a = removeFirstOcurrence(m, rest); a.unshift(first); return a; } }
PASS: removeFirstOcurrence(5, []) should return an empty array PASS: removeFirstOcurrence(5, [5]) should return an empty array PASS: removeFirstOcurrence(5, [1]) should return the original array without changes PASS: removeFirstOcurrence(5, [5,5]) should return an array with only one 5 PASS: removeFirstOcurrence(5, [1,5,5,2,3]) should return an array without the first ocurrence of 5 PASS: removeFirstOcurrence(5, [5,1,2,3]) should return an array without 5 PASS: removeFirstOcurrence(5, [1,2,5,3]) should return an array without 5 PASS: removeFirstOcurrence(5, [1,2,3,5]) should return an array without 5
5 sum
Write a function that sums every number in the array.
If the array is empty, return 0.
'use strict'; const assert = require("assert"); function check(message) { return { fn(fnToCheck) { return { toBe: { msg : message != undefined ? message : '', actual: fnToCheck, fnExeAssert(fnAssert,actual, expected) { try { fnAssert(actual, expected); console.log('PASS: ' + this.msg + '\n' ); } catch(e) { console.log('FAIL: ' + this.msg ); console.log(' expected: ', e.expected, '\n actual: ', e.actual, '\n'); } }, deepStrictEqual (expected) { this.fnExeAssert(assert.deepStrictEqual, this.actual, expected) }, strictEqual (expected) { this.fnExeAssert(assert.strictEqual, this.actual, expected) } } } } } } check("sum([0] should return 0") .fn(sum([0])) .toBe .strictEqual(0); check("sum([1] should return 1") .fn(sum([1])) .toBe .strictEqual(1); check("sum([0,1] should return 1") .fn(sum([0,1])) .toBe .strictEqual(1); check("sum([1,2] should return 3") .fn(sum([1,2])) .toBe .strictEqual(3); check("sum([1,-1] should return 0") .fn(sum([1,-1])) .toBe .strictEqual(0); /** * Returns the sum of every number in the array * If the array is empty, return 0; * @param {array} arr - the array of numbers * @returns {number} - the sum of every number in the array or zero */ function sum(arr) { if(arr.length === 0) return 0; const [first, ...rest] = arr; return first + sum(rest); }
PASS: sum([0] should return 0 PASS: sum([1] should return 1 PASS: sum([0,1] should return 1 PASS: sum([1,2] should return 3 PASS: sum([1,-1] should return 0
6 countChar
Write a function that counts the ocurrences of a character in a string.
If the character does not occur in the string return 0.
'use strict'; const assert = require("assert"); function check(message) { return { fn(fnToCheck) { return { toBe: { msg : message != undefined ? message : '', actual: fnToCheck, fnExeAssert(fnAssert,actual, expected) { try { fnAssert(actual, expected); console.log('PASS: ' + this.msg + '\n' ); } catch(e) { console.log('FAIL: ' + this.msg ); console.log(' expected: ', e.expected, '\n actual: ', e.actual, '\n'); } }, deepStrictEqual (expected) { this.fnExeAssert(assert.deepStrictEqual, this.actual, expected) }, strictEqual (expected) { this.fnExeAssert(assert.strictEqual, this.actual, expected) } } } } } } check("countChar('h', 'ayzayzzz') should return 0") .fn(countChar('h', 'ayzayzzz')).toBe.strictEqual(0); check("countChar('a', 'ayzayzzz') should return 2") .fn(countChar('a', 'ayzayzzz')).toBe.strictEqual(2); check("countChar('y', 'ayzayzzz') should return 2") .fn(countChar('y', 'ayzayzzz')).toBe.strictEqual(2); check("countChar('z', 'ayzayzzz') should return 4") .fn(countChar('z', 'ayzayzzz')).toBe.strictEqual(4); /** * Returns the number of ocurrences of a character in a string. * If the character does not occur in the string return 0. * @param {string} c - the character to count * @param {string|array} str - string in the first call, array in the recursive calls * @returns {number} the number of ocurrences of the character in the string or zero */ function countChar(c, str) { if(str.length === 0 ) return 0 const [first, ...rest] = str; return (c === first ) ? 1 + countChar(c, rest) : countChar(c, rest) }
PASS: countChar('h', 'ayzayzzz') should return 0 PASS: countChar('a', 'ayzayzzz') should return 2 PASS: countChar('y', 'ayzayzzz') should return 2 PASS: countChar('z', 'ayzayzzz') should return 4
7 len
Write a function that returns the number of elements in an array.
If the array is empty, return 0.
'use strict'; const assert = require("assert"); function check(message) { return { fn(fnToCheck) { return { toBe: { msg : message != undefined ? message : '', actual: fnToCheck, fnExeAssert(fnAssert,actual, expected) { try { fnAssert(actual, expected); console.log('PASS: ' + this.msg + '\n' ); } catch(e) { console.log('FAIL: ' + this.msg ); console.log(' expected: ', e.expected, '\n actual: ', e.actual, '\n'); } }, deepStrictEqual (expected) { this.fnExeAssert(assert.deepStrictEqual, this.actual, expected) }, strictEqual (expected) { this.fnExeAssert(assert.strictEqual, this.actual, expected) } } } } } } check("len([]) should return 0") .fn(len([])) .toBe .strictEqual(0) check("len([0]) should return 1") .fn(len([0])) .toBe .strictEqual(1) check("len([1,2]) should return 2") .fn(len([1,2])) .toBe .strictEqual(2) /** * Returns the number of elements in an array. * If the array is empty, return 0. * @param {arr} arr - the array * @returns {number} - the number of elements in the array or zero */ function len(arr){ if(arr.length === 0) return 0; const [first, ...rest] = arr; return 1 + len(rest); }
PASS: len([]) should return 0 PASS: len([0]) should return 1 PASS: len([1,2]) should return 2
8 max
Write a function that returns the largest number of an array of numbers.
If the array is empty, returns 0;
'use strict'; const assert = require("assert"); function check(message) { return { fn(fnToCheck) { return { toBe: { msg : message != undefined ? message : '', actual: fnToCheck, fnExeAssert(fnAssert,actual, expected) { try { fnAssert(actual, expected); console.log('PASS: ' + this.msg + '\n' ); } catch(e) { console.log('FAIL: ' + this.msg ); console.log(' expected: ', e.expected, '\n actual: ', e.actual, '\n'); } }, deepStrictEqual (expected) { this.fnExeAssert(assert.deepStrictEqual, this.actual, expected) }, strictEqual (expected) { this.fnExeAssert(assert.strictEqual, this.actual, expected) } } } } } } check("max[15,8,3,9] should return 15") .fn(max([15,8,3,9])) .toBe .strictEqual(15) check("max[8,15,9] should return 15") .fn(max([8,15,9])) .toBe .strictEqual(15) check("max[8,9,15] should return 15") .fn(max([8,9,15])) .toBe .strictEqual(15) check("max[14,16, 8,3,9,14] should return 16") .fn(max([14,16, 8,3,9,14])) .toBe .strictEqual(16) /** * Return the largest number of an array of numbers. * If the array is empty, returns 0; * @param {array} arr - the array of numbers * @returns {number} - the larget number in the array or 0 */ function max(arr) { if(arr.length === 1 ) { return arr[0]; } else { const [first, ...rest] = arr; if (first > rest[0]) { rest.shift(); rest.push(first) return max(rest) } else { return max(rest) } } }
PASS: max[15,8,3,9] should return 15 PASS: max[8,15,9] should return 15 PASS: max[8,9,15] should return 15 PASS: max[14,16, 8,3,9,14] should return 16