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

Author: erretres

Created: 2022-04-12 Tue 13:38

Validate