Description

ArraySuperClass is a custom implementation of JavaScript's native Array: we reimplemented lightweight versions of some popular array methods: map, some, find, push. However, we chose our implementation of map to MUTATE the instance of ArraySuperClass it is being invoked to.

Testing with Cucumber.js

Below one might find the step definitions we use. We stress again that our customized map MUTATES the array it is invoked to!


#does_it_contain_array_API.feature 

Feature: Does the ArraySuperClass class contain the properties of an object instantiated from the native JavaScript Array class?
  We implemented ArraySuperClass to have the native JavaScript API, as well as the array value, under the property called array.

  Scenario: ArraySuperClass class instances contains the properties of instances of the native JavaScript Array class
    Given an instance of the ArraySuperClass class 
    When compared with an instance of the native JavaScript Array class, not necessarily with the same array value
    Then it should contain all the properties of the instance of the native JavaScript Array class i.e. the array API

  Scenario: ArraySuperClass class instances' length property is the same as the length property of instances of the native JavaScript Array class
    Given the length property of an instance of the ArraySuperClass class 
    When compared with an instance of the native JavaScript Array class, with the same number of elements
    Then both length properties should point to the same number value

#does_it_extend_array_API.feature

Feature: Does the ArraySuperClass class contain properties that are functionally similar to the ones of JavaScript's native Array class?
  We reimplemented lightweight versions of some popular array methods: map, some, find, push.
  We added some modifications from the originals, though: map, for example, does not create a new array, but instead
  mutates the original array.

  Scenario: ArraySuperClass class instances' map method MUTATES the original array with the provided callback
    Given the map method of an instance of the ArraySuperClass class
    When we invoke map with a provided callback
    Then it should mutate the original instance element-wise with the callback

  Scenario: ArraySuperClass class instances' some method returns true when a condition is met
    Given the some method of an instance of the ArraySuperClass class
    When we invoke some with a provided callback
    Then it should return true if any element meets the condition

  Scenario: ArraySuperClass class instances' find method returns the first element that satisfies the provided condition
    Given the find method of an instance of the ArraySuperClass class
    When we invoke find with a provided callback
    Then it should return the first element that satisfies the condition

  Scenario: ArraySuperClass class instances' push method adds a new element to the end of the array
    Given the push method of an instance of the ArraySuperClass class
    When we invoke push with a provided new element
    Then it should add the new element to the end of the array
    And it should update the length of the array
  

ArraySuperClass Implementation


/**
 * ArraySuperClass is a custom implementation of JavaScript's native Array.
 */
class ArraySuperClass {
  array;
  length;

  /**
   * Constructs an ArraySuperClass instance with the given elements.
   * @param {...any} array - The elements to initialize the array with.
   */
  constructor(...array) {
    this.array = array;
    this.#updateLength();
  }

  /**
   * Updates the 'length' property of the ArraySuperClass instance.
   * @private
   */
  #updateLength() {
    this.length = 0;
    for (const _ of this.array) {
      this.length++;
    }
  }

  /**
   * Maps each element using a mapping function. As opposed to the native API, this is !!! MUTABLE !!!.
   * @param {Function} mappingFunction - The function to apply to each element.
   */
  map(mappingFunction) {
    for (let index = 0; index < this.length; index++) {
      this.array[index] = mappingFunction(this.array[index]);
    }
  }

  /**
   * Tests whether at least one element passes the test implemented by the provided function.
   * @param {Function} testFunction - The function to test for each element.
   * @returns {boolean} True if at least one element passes the test, otherwise false.
   */
  some(testFunction) {
    for (let index = 0; index < this.length; index++) {
      if (testFunction(this.array[index])) {
        return true;
      }
    }
    return false;
  }

  /**
   * Returns the value of the first element that satisfies the provided test function.
   * @param {Function} testFunction - The function to test for each element.
   * @returns {any} The first element that satisfies the test function; otherwise undefined.
   */
  find(testFunction) {
    for (let index = 0; index < this.length; index++) {
      if (testFunction(this.array[index])) {
        return this.array[index];
      }
    }
    return undefined;
  }

  /**
   * Adds one element to the end of the array and updates the length property.
   * @param {any} newLastElement - The element to add to the end of the array.
   * @returns {number} The new length property of the array.
   */
  push(newLastElement) {
    this.array[this.length] = newLastElement;
    this.#updateLength();
    return this.length;
  }

}
export default ArraySuperClass;