Javascript Functional Programming: Producing Higher-Order Functions

I. Introduction

Functional programming is a paradigm of programming that emphasizes the use of functions as the primary building blocks of software. In contrast to imperative programming, which focuses on how to perform operations, functional programming focuses on what operations to perform. By using functions as the primary building blocks, functional programming allows for more modular, maintainable, and scalable code.

One of the key features of functional programming is higher-order functions. Higher-order functions are functions that take other functions as arguments or return functions as their results. They allow for powerful abstractions and expressive code that can be reused in many different contexts.

In this post, we’ll be exploring how to produce higher-order functions in Javascript. We’ll start by defining what higher-order functions are and why they are important in functional programming. Then, we’ll show you how to create your own higher-order functions in Javascript, using examples to illustrate the concepts. We’ll also demonstrate how to use higher-order functions in practical applications, including examples of how to use built-in higher-order functions like map, filter, and reduce to transform and manipulate data.

By the end of this post, you’ll have a better understanding of higher-order functions and how to use them in your own Javascript code. Whether you’re a beginner or an experienced developer, understanding functional programming and higher-order functions is an essential skill for building high-quality, maintainable software. So, let’s dive in and start exploring!

II. Higher-Order Functions

Higher-order functions are a key concept in functional programming, and they are an essential tool for building modular, reusable, and maintainable code. In this section, we’ll be exploring what higher-order functions are, why they are important in functional programming, and how to use them in Javascript.

In Javascript, a higher-order function is a function that takes one or more functions as arguments, and/or returns a function as its result. This may sound a bit abstract at first, but it’s a powerful concept that allows us to create more expressive and flexible code.

One of the primary benefits of higher-order functions is that they enable code reuse. By defining a function that takes in another function as an argument, we can create a function that performs a specific operation on any input that matches a given criteria. This means that we can write a function once, and then use it in many different contexts without having to repeat the code.

Another benefit of higher-order functions is that they enable us to create abstractions. We can define a function that takes in a complex function as an argument, and then use that function in our own code to simplify complex operations. This means that we can write code that is more expressive and easier to understand, even if we don’t fully understand the details of the underlying operations.

In the next section of this post, we’ll explore how to create higher-order functions in Javascript, and how to use them in practical applications. So, let’s dive in and start exploring the power of higher-order functions!

Examples of built-in higher-order functions in Javascript, like map, filter, and reduce

1. Map

The map method is a built-in higher-order function in Javascript that creates a new array with the results of calling a provided function on every element in the original array. Here’s an example: 

				
					const numbers = [1, 2, 3, 4, 5];
const squaredNumbers = numbers.map(num => num * num);
console.log(squaredNumbers); // Output: [1, 4, 9, 16, 25]
				
			

In this example, we define an array of numbers and then use the map method to create a new array called squaredNumbers, where each element is the result of squaring the corresponding element in the original numbers array.

2. Filter

The filter method is another built-in higher-order function in Javascript that creates a new array with all elements that pass the test implemented by the provided function. Here’s an example:

				
					const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter(num => num % 2 === 0);
console.log(evenNumbers); // Output: [2, 4]
				
			

In this example, we define an array of numbers and then use the filter method to create a new array called evenNumbers, where each element is an even number from the original numbers array.

3. Reduce

The reduce method is a built-in higher-order function in Javascript that applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value. Here’s an example:

				
					const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // Output: 15
				
			

In this example, we define an array of numbers and then use the reduce method to calculate the sum of all the elements in the numbers array. The reduce method takes two arguments: a callback function that defines the operation to perform on each element, and an initial value for the accumulator (in this case, 0).

These are just a few examples of the built-in higher-order functions in Javascript. In the next section, we’ll explore how to create our own higher-order functions in Javascript.

III. Producing Higher-Order Functions

In the previous section, we explored what higher-order functions are and why they are important in functional programming. Now, we’ll dive deeper and explore how to create your own higher-order functions in Javascript.

Creating your own higher-order functions can be incredibly useful in Javascript, as it allows you to create code that is more modular, maintainable, and reusable. By creating functions that take in other functions as arguments, or return functions as their results, you can create code that is more expressive and flexible, and that can be used in many different contexts.

In this section, we’ll explore how to create higher-order functions in Javascript. We’ll start by looking at examples of functions that take other functions as arguments, and show you how to write them step-by-step. Then, we’ll move on to functions that return other functions as their results, and explore the concepts involved in creating these functions.

By the end of this section, you’ll have a better understanding of how to create your own higher-order functions in Javascript, and how to use them in practical applications. So, let’s dive in and start exploring how to produce higher-order functions in Javascript!

A. How to create higher-order functions in Javascript

Creating your own higher-order functions in Javascript is a powerful way to build modular, maintainable, and reusable code. In this section, we’ll explore how to create higher-order functions that take other functions as arguments, and functions that return other functions as their results.

Creating Functions that Take Other Functions as Arguments

To create a function that takes another function as an argument, you simply define the function with the desired argument name, and then pass in the function as an argument when the higher-order function is called.

Let’s start theapplyOperationfunction which is a very basic function.  This function allows you to pass in an operation function along with two numbers, and it will apply the operation on those numbers and return the result. If no operation function is provided, it defaults to an empty function, and if no numbers are provided, it defaults to 0 and 2.

Here is how you can implement this  applyOperation function: 

				
					const  applyOperation = (operation = () => {}, num1 = 0, num2 = 2) => operation(num1, num2);
const add = (num1 = 0, num2 = 1) => num1 + num2;
const multiply = (num1 = 0, num2 = 1) => num1 * num2;

const sum = applyOperation(add, 5, 3); // Output: 8
const product = applyOperation(multiply, 5, 3); // Output: 15
				
			

Next, let’s create apromisifyfunction.  Here is an implementation of ourpromisify function: 

				
					/**
* @name promisify
* @function
*
* @param {Function|Object} fn the function or object to be promisified
*  
* @description promisified functions or objects
* @return {Function|Object} fn, the promisified function
* 
*/

const promisify = (fn = () => {}) => (...args) => 
    new Promise((resolve, reject) =>
        fn(...args, (error, data) =>  error ? reject(error) : resolve(data)));
				
			
  • The promisify function takes a callback-style function fn as its input parameter.

  • It returns a new function that accepts any number of arguments (...args).

  • This new function creates a new Promise using the Promise constructor. This Promise resolves with the data object if the fn function executes without error, or rejects with the error object if the fn function encounters an error.

  • The fn function is called with the given arguments (...args) and an additional callback function that takes an error object and the data object as its two arguments.

  • If the error object is truthy, the Promise is rejected with the error object.

  • If the error object is falsy, the Promise is resolved with the data object.

Our promisify function allows you to convert a callback-style asynchronous function into a Promise-based asynchronous function. It does this by wrapping the original function in a new function that returns a Promise and then calling the original function with the given arguments along with a callback function that resolves or rejects the Promise based on the result of the original function.

Here is an example of how to use our promisify function:

Next, let’s create ademethodizefunction.  Here is an implementation of ourdemthodize function: 

				
					 /**
   * @name demethodize
   * @function
   *
   * @param {Function|Object} fn  the function to bind to object method
   *  
   * @description plucks off a method from ANY object and makes that method a completely independent standalone reusable  function.
   * 
   *  For instance, if I wanted to make Array.prototype.map method an independent standalone reusable function, I would do something like this: const myArrayMap = pluckOff(Array.prototype.map). Then I would use it like this:
   * 
   * const array = [1,2,3,4,5]; const result = myArrayMap(array, x => x * 2); result = [2,4,6,8,10]
   * 
   * @return {Function|Object} fn.bind(...args)(), the completely independent standalone reusable function
   * 
   */

const demethodize = (fn = () => {}) => (...args)=> fn.bind(...args)();
				
			
  • The demethodize function takes a method function fn as its input parameter.

  • It returns a new function that accepts any number of arguments (...args).

  • This new function creates a bound function by calling fn.bind(...args). The bind() method creates a new function with the same body as the original fn function, but with its this value bound to the object passed as the first argument. In this case, ...args represents the object that the method fn function would normally be called on.

  • The new bound function is immediately called with no arguments () using the parentheses at the end of the line, and its return value is returned by the outer function.

Our demethodize  function allows you to “demethodize” a method function so that it can be called separately from an object context. It does this by creating a bound function using fn.bind(...args) and immediately calling it with no arguments (). The return value of the bound function is then returned by the outer function.

Next, let’s create a promisify function.  Here is an implementation of ourpromisify function: 

				
					  /**
   * @name demethodizeConstruct
   * @function
   *
   * @param {Function|Object} Source  The object to demethodize
   * @param {Function|Object} Destination  The object to methodify
   *  
   * @description Methodifies a destination object with all the methods on the prototype of the source object
   * 
   * @return {Function|Object} The methodified object
   * 
   */

const demethodizeConstruct = (Source = {}, Destination =  {} ) => {
    
    for(let method of Object.getOwnPropertyNames(Source.prototype)){
        Destination[method] = demethodize(Source.prototype[method])
    }
    return Destination;
}
				
			
  • The demethodizeConstruct function takes two parameters: the Source constructor function whose prototype methods need to be demethodized, and the Destination object where the demethodized functions will be added.

  • It loops through all the prototype methods of the Source constructor function using a for..of loop and the Object.getOwnPropertyNames method.

  • For each prototype method, it creates a new function using the demethodize function, passing in the method as the fn parameter and an empty object {} as the thisArg parameter.

  • It adds the new demethodized function to the Destination object with the same method name.

  • The Destination object with the demethodized functions added to it is returned.

Our demethodizeConstruct  function allows you to demethodize all the prototype methods of a constructor function and add them to a target object. It does this by looping through all the prototype methods of the Source constructor function, using the demethodize function to create a new demethodized function for each method, and adding the new demethodized function to the Destination object. The Destination object with the demethodized functions added to it is then returned.

Next, let’s create afindOptimum function.  Here is an implementation of our findOptimum function: 

				
					/**
* @name findOptimum
* @function
*
* @param {Function|Object} fn the function 
*  
* @description creates an optimization function

* @return {Function|Object} The optimization function
* 
*/
const findOptimum = (fn = () => {}) => (array = []) => Array.isArray(array) ? array.reduce(fn): null;
				
			
  • The findOptimum function takes a function fn as its input parameter.

  • It returns a new function that takes one parameter array.

  • If array is an array, the new function calls the reduce() method on array, passing in the fn function as the first argument. The reduce() method applies the fn function to each element of the array and returns a single value that represents the optimum value. The reduce() method can be used to find the maximum or minimum value in an array, for example.

  • If array is not an array, the new function returns null.

Our  findOptimum function allows you to find the optimum value in an array by applying a comparison function to all the elements of the array. It does this by returning a new function that takes an array as its parameter and uses the reduce() method to apply the fn function to all the elements of the array and return the optimum value. If the input parameter is not an array, it returns null.

Next, let’s create a callFirstOnlyNTimes function.  Here is an implementation of our callFirstOnlyNTimes function: 

				
					    /**
     * @name callFirstOnlyNTimes
     * @function
     *
     * @param {Function|Object} f the function to be called only n times
     * @param {Function|Object} g  the function to be called as many times as left after f() is called n times
     * @param {Number} n number of time the function f() should be called
     *  
     * @description creates a function that calls and runs the first argument function f() n times and only n times no matter how many times the function is called or used in the loop. It calls f() exactly n times and the rest of the times it calls g(). For instance if n = 1 and the function is called 200 times, it would call or execute f() only once and g() 199 times. If n = 5 and the function is called 200 times, it would call or execute f() exactly 5 times and g() 195 times.
     * 
     * @return {Function|Object} a function that calls fn() only n times and g() afterward
     * 
     */
    const callFirstOnlyNTimes = (f = () => {}, g = () => {}, n = 1) => {
        let done = false
        return (...args) => {
          if (!done) {
            done = true
            if (typeof n !== 'number' || n % 1 !== 0) {
              f(...args)
            } else {
              for (let i = 1; i <= Math.abs(n); i++) {
                f(...args)
              }
            }
          } else {
            g(...args)
          }
        }
      }
    
				
			
  • The callFirstOnlyNTimes function takes three parameters: f, g, and n.

  • It initializes a variable done to false.

  • It returns a new function that takes any number of arguments (...args).

  • The returned function checks if done is false. If done is false, the returned function sets done to true and checks the value of n.

  • If n is not a whole number, the returned function calls f with the given arguments (...args).

  • If n is a whole number, the returned function calls f with the given arguments (...args) n times using a for loop.

  • If done is true, the returned function calls g with the given arguments (...args).

  • The f function will be called only the first n times the returned function is called, after which the g function will be called every time the returned function is called.

OurcallFirstOnlyNTimes function allows you to specify a function f that will be called only the first n times the returned function is called, and a function g that will be called every time after that. It does this by returning a new function that uses a flag variable done to determine whether f or g should be called. If done is false, the returned function calls f the first n times it is called, and then sets done to true and calls g every time after that.

Next, let’s create a callOnlyNTimes function.  Here is an implementation of ourcallOnlyNTimes function: 

				
					   /**
   * @name callOnlyNTimes
   * @function
   *
   * @param {Function|Object} fn the function to be called only n times

   * @param {Number} n number of time the function f() should be called
   *  
   * @description creates a function that calls and runs the function f() n times and only n times no matter how many times the function is called or used in the loop. It calls f() exactly n times. For instance if n = 1 and the function is called 200 times, it would call or execute f() only once (no more than once). If n = 5 and the function is called 200 times, it would call or execute f() exactly 5 times and no more than 5 times.
   * 
   * @return {Function|Object} a function that calls fn() only n times
   * 
   */
   const callOnlyNTimes = (fn = () => {}, n = 1) => {
    let done = false
    return (...args) => {
        if (!done) {
            done = true
            for (let i = 0; i < Math.abs(n); i++) {
                fn(...args)
            }
        }
    }
}

				
			
  • The callOnlyNTimes function takes two parameters: fn and n.

  • It initializes a variable done to false.

  • It returns a new function that takes any number of arguments (...args).

  • The returned function checks if done is false. If done is false, the returned function sets done to true and calls fn n times using a for loop.

  • The fn function will be called only the first n times the returned function is called.

OurcallOnlyNTimes function allows you to specify a function fn that will be called only the first n times the returned function is called. It does this by returning a new function that uses a flag variable done to determine whether fn should be called. If done is false, the returned function sets done to true and calls fn n times using a for loop. After the first n times the returned function is called, the fn function will not be called again.

Next, let’s create a billOnceAndOnlyOnce  function.  Here is an implementation of ourbillOnceAndOnlyOncefunction: 

				
					
   /**
   * @name billOnceAndOnlyOnce
   * @function
   *
   * @param {Function|Object} bill the function to call for billing

   * @param {Function|Object} dontBill the function to call to avoid billing
   *  
   * @description creates a function that is called and runs only onces no matter how many times the function is called or used in the loop. For instance if the function is called 200 times, it would be called or executed only the first round (no more than once); that is it would 1 time and not run the rest of 199 times.
   * 
   * @return {Function|Object} a function that bills only once not matter what
   * 
   */

   const billOnceAndOnlyOnce = (bill = () => {}, dontBill = () => {})  => {
    let timeToBill = bill
    return (...args) => {
      let result = timeToBill(...args)
      timeToBill = dontBill
      return result
    }
  }

				
			
  • The billOnceAndOnlyOnce function takes two parameters: bill and dontBill.

  • It initializes a variable timeToBill to bill.

  • It returns a new function that takes any number of arguments (...args).

  • The returned function calls timeToBill with the given arguments (...args) and stores the result in a variable result.

  • The returned function then sets timeToBill to dontBill.

  • Finally, the returned function returns the result.

  • The bill function will be called only the first time the returned function is called, and the dontBill function will be called every time after that.

OurbillOnceAndOnlyOnce function allows you to specify a function bill that will be called only the first time the returned function is called, and a function dontBill that will be called every time after that. It does this by returning a new function that uses a variable timeToBill to keep track of which function should be called, and then sets timeToBill to dontBill after the first call to the returned function.

Next, let’s create a inputsValidfunction.  We will use this mostly as a helper function to help perform some validations for our upcoming function. Here is an implementation of ourinputsValid function: 

				
					   /**
   * @name inputsValid
   * @function
   *
   * @param {Function} array  the array to validate
   * @param {Function} fn  the call back function to validate
   * @param {Number} flat arr flattening depth to validate
   *  
   * @description validates inputs
   * 
   * @return {Boolean} true if inputs are valid and false if inputs are invalid
   * 
   */
   const inputsValid = (array = [], fn = () => {}, flat = 1) => {
    if (!Array.isArray(array)) return false
    if (typeof fn !== 'function') return false;
    if (typeof flat !== 'number' || flat < 0 || (flat % 1 !== 0 && flat !== Infinity)) return false;
    return true
  }

				
			
  • The inputsValid function takes three parameters: array, fn, and flat.

  • It checks if array is an array by using the Array.isArray() method. If array is not an array, the function returns false.

  • It checks if fn is a function by using the typeof operator. If fn is not a function, the function returns false.

  • It checks if flat is a whole number or Infinity by using the typeof operator and the modulo operator (%). If flat is not a whole number or Infinity, the function returns false.

  • If all three input parameters pass the validation checks, the function returns true. Otherwise, it returns false.

Our inputsValid function validates whether the input parameters array, fn, and flat are valid or not. It does this by checking whether array is an array, whether fn is a function, and whether flat is a whole number or Infinity. If all input parameters pass the validation checks, the function returns true. Otherwise, it returns false.

Next, let’s create a none function.  Here is an implementation of ournone function: 

				
					   /**
   * @name none
   * @function
   *
   * @param {Array|Object} array the array to filter
   * @param {Function|Object} fn the predicate
   * @param {Number} flat  the array to filter flattening depth
   *  
   * @description Checks if none of the elements of the array satisfies the predicate
   * 
   * @return {Boolean} True if none of the elements of the array satisfies the predicate. False otherwise
   * 
   */
    const  none  = (array = [], fn = () => false, flat = 0) => inputsValid(array, fn, flat) ? array.flat(flat).every(v => !fn(v)) : false;
				
			
  • The none function takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns false.

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the every() method to check whether none of the elements in the flattened array pass the test implemented by the fn function.

  • If none of the elements pass the test, the function returns true. Otherwise, it returns false.

Ournonefunction checks whether none of the elements in array pass the test implemented by the fn function. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the every() method to check whether none of the elements in the flattened array pass the test implemented by the fn function. If none of the elements pass the test, the function returns true. Otherwise, it returns false.

Next, let’s create a forEachAsync function.  Here is an implementation of ourforEachAsync function: 

				
					    /**
   * @name forEachAsync
   * @function
   *
   * @param {Array|Object} array the array to loop over
   * @param {Function|Object} fn the callback function
   * @param {Number} flat  the array to loop over flattening depth
   *  
   * @description asynchronously loops an array
   * 
   * @return {Promise} A promise if promise is fulfilled and successfull
   * 
   */
  const forEachAsync  = (array = [], fn = () => false, flat = 0) =>  inputsValid(array, fn, flat) ? array.flat(flat).reduce((promise, value) => promise.then(() => fn(value)), Promise.resolve()): undefined
				
			
  • The forEachAsync function takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns undefined.

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the reduce() method to iterate over each element of the flattened array asynchronously.

  • For each element of the flattened array, the fn function is called using Promise.then(). This ensures that the elements are processed in the correct order.

  • The reduce() method returns a Promise that resolves when all elements of array have been processed.

OurforEachAsync function iterates over each element of array asynchronously and calls the fn function for each element. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the reduce() method to iterate over each element of the flattened array asynchronously. The function returns a Promise that resolves when all elements of array have been processed.

Next, let’s create a mapAsyncfunction.  Here is an implementation of ourmapAsyncfunction: 

				
					/**
 * @name mapAsync
 * @function
 *
 * @param {Array|Object} array the array to map 
 * @param {Function|Object} fn the callback function
 * @param {Number} flat  the array to map flattening depth
 *  
 * @description asynchronously  maps an array
 * 
 * @return {Promise}  A promise if promise is fulfilled and successfull
 * 
 */
const mapAsync = (array = [],fn = () => [], flat = 0) => inputsValid(array, fn, flat)? Promise.all(array.flat(flat).map(fn)): [];
				
			
  • The mapAsync function takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns an empty array [].

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the map() method to apply the fn function to each element of the flattened array.

  • The map() method returns an array of Promises, which resolve to the results of applying the fn function to each element of array.

  • The Promise.all() method is used to wait for all Promises to resolve before returning a single Promise that resolves to an array of the results of applying the fn function to each element of array.

OurmapAsync function applies the fn function to each element of array and returns a Promise that resolves to an array of the results. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the map() method to apply the fn function to each element of the flattened array. Finally, it returns a Promise that resolves to an array of the results. If any of the input parameters fail the validation checks, the function returns an empty array [].

Next, let’s create a filterAsync function.  Here is an implementation of ourfilterAsync function: 

				
					/**
 * @name filterAsync
 * @function
 *
 * @param {Array|Object} array the array to filter
 * @param {Function|Object} fn the callback function
 * @param {Number} flat  the array to filter flattening depth
 *  
 * @description asynchronously filters an array
 * 
 * @return {Promise}  A promise if promise is fulfilled and successfull
 * 
 */

const filterAsync  = (array = [], fn = () => [], flat = 0) => inputsValid(array, fn, flat) ? mapAsync(fn, flat).then(array => array.flat(flat).filter((v, i) => Boolean(array[i]))): []
				
			
  • The filterAsync function takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns an empty array [].

  • If all input parameters pass the validation checks, the function uses the mapAsync() function to apply the fn function to each element of the flattened array.

  • The mapAsync() function returns a Promise that resolves to an array of the results of applying the fn function to each element of the flattened array.

  • The Promise returned by mapAsync() is then used to filter the original array. If the Promise resolves to an array of values that are truthy, then the corresponding element in the original array is included in the result array. Otherwise, the element is excluded.

  • Finally, the Promise is resolved to the filtered array.

OurfilterAsync function filters  array using the fn function and returns a Promise that resolves to an array of the elements that pass the test. It does this by validating the input parameters array, fn, and flat, using the mapAsync() function to apply the fn function to each element of the flattened array, and then filtering the original array using the Promise returned by mapAsync(). If any of the input parameters fail the validation checks, the function returns an empty array [].

Next, let’s create a reduceAsyncfunction.  Here is an implementation of ourreduceAsyncfunction: 

				
					/**
 * @name reduceAsync
 * @function
 *
 * @param {Array|Object} array the array to reduce
 * @param {Function|Object} fn the callback function
 * @param {Number} flat  the array to reduce flattening depth
 *  
 * @description asynchronously reduces an array
 * 
 * @return {Promise} A promise if promise is fulfilled and successfull
 * 
 */

const reduceAsync  = (array =[], fn = () => {}, init, flat = 0) =>  inputsValid(array, fn, flat)
    ? Promise.resolve(init).then(accumulator => this.forEachAsync(array.flat(flat), async (v, i) => {
        accumulator = fn(accumulator, v, i)
        }).then(() => accumulator))
    : 0;
				
			
  • The reduceAsync function takes four parameters: array, fn, init, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns 0.

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the forEachAsync() function to iterate over each element of the flattened array asynchronously.

  • For each element of the flattened array, the fn function is called using the Promise.then() method to update the accumulator value asynchronously.

  • The reduceAsync() function returns a Promise that resolves to the final value of the accumulator after all elements of array have been processed by the fn function.

OurreduceAsyncfunction reduces the array using the fn function and returns a Promise that resolves to the final value of the accumulator. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the forEachAsync() function to iterate over each element of the flattened array asynchronously. Finally, it returns a Promise that resolves to the final value of the accumulator. If any of the input parameters fail the validation checks, the function returns 0.

Next, let’s create afilteredfunction.  Here is an implementation of ourfilteredfunction: 

				
					/**
 * @name filter
 * @function
 *
 * @param {Array|Object} array the array to filter
 * @param {Function|Object} fn the call back function
 * @param {Number} flat  the array to filter flattening depth
 *  
 * @description filters an array
 * 
 * @return {Array|Object} The filtered array
 * 
 */
const filtered  = (array = [], fn = () => [], flat = 1) => inputsValid(array, fn, flat) ? array.flat(flat).filter(x => fn(x)) : [];

				
			
  • The filtered function takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns an empty array [].

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the filter() method to filter the flattened array based on the fn function.

  • The filter() method returns a new array containing the elements of array that pass the test implemented by the fn function.

  • The filtered() function returns the new filtered array.

Ourfilteredfunction filters the array using the fn function and returns a new array containing the elements that pass the test. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the filter() method to filter the flattened array. If any of the input parameters fail the validation checks, the function returns an empty array [].

Next, let’s create a filterItems function.  Here is an implementation of ourfilterItems function: 

				
					/**
 * @name filterItems
 * @function
 * 
 * @param {Array|Object} array the array to filter
 * @param {String} query any fitlering query
 *  
 * @description reads a query and filter arrays according to the query
 * 
 * @return {Array}  The query filtered array
 * 
 */
const filterItems = (query, array = []) => Array.isArray(array) ? array.filter(el => el.toLowerCase().indexOf(query.toLowerCase()) !== -1): [];
				
			
  • The filterItems function takes two parameters: query and array.

  • It checks whether the input parameter array is an array by calling the Array.isArray() method. If array is not an array, the function returns an empty array [].

  • If array is an array, the function uses the filter() method to create a new array containing the elements of array that contain the query string.

  • The filter() method iterates over each element of array and calls a callback function for each element. The callback function tests whether the element contains the query string using the indexOf() method. If the query string is found in the element, the callback function returns true, which includes the element in the new array. Otherwise, the callback function returns false, which excludes the element from the new array.

  • The filterItems() function returns the new array containing the elements of array that contain the query string.

OurfilterItemsfunction filters the array to create a new array containing the elements that contain the query string. It does this by using the filter() method to create a new array containing the elements of array that contain the query string. If array is not an array, the function returns an empty array [].

Next, let’s create asomefunction.  Here is an implementation of oursomefunction: 

				
					 /**
 * @name some
 * @function
 *
 * @param {Array} array The input array to check
 * @param {Function} fn The predicate function to call when   
 * @param {Number} flat  the array to check flattening depth
 *  
 * @description checks an array according to the thruthiness of the predicate
 * 
 * @return {Boolean} true if at least one of the array items for which the predicate is true if found. false otherwise
 * 
 */
const some = (array = [], fn = () => false, flat = 0) => inputsValid(array, fn, flat) ? array.flat(flat).reduce((x, y) => x || fn(y), false) : false;
				
			
  • The some function takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns false.

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the reduce() method to iterate over each element of the flattened array and test it using the fn function.

  • For each element of the flattened array, the fn function is called to test whether the element passes the test.

  • If at least one element in the array passes the test implemented by the fn function, the reduce() method returns true. Otherwise, it returns false.

  • The some() function returns the boolean value returned by the reduce() method.

Oursomefunction tests thearrayusing thefn function and returns a boolean value that indicates whether at least one element in the array passes the test. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the reduce() method to iterate over each element of the flattened array and test it using the fn function. If any of the input parameters fail the validation checks, the function returns false.

Next, let’s create aeveryfunction.  Here is an implementation of oureveryfunction: 

				
					/**
 * @name every
 * @function
 *
 * @param {Array} array the array to check
 * @param {Function} fn the predicate function 
 * @param {Number} flat  the array to filter flattening depth
 *  
 * @description checks an array according to the thruthiness of the predicate
 * 
 * @return {Boolean} true if each one of the array items for which the predicate is true if found. false otherwise
 * 
 */
const every = (array = [], fn = () => false, flat = 0, result = []) => {
   if(inputsValid(arr, fn, falt)){
    array.flat(flat).reduce((x, y) => (x === false && fn(y) ? result.push(y) : result.pop()), false);
    return result.length === array.flat(flat).length ? true : false;
   }else{
       return false;
   }
}
				
			
  • Theeveryfunction takes four parameters: array, fn, flat, and result.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns false.

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the reduce() method to iterate over each element of the flattened array and test it using the fn function.

  • For each element of the flattened array, the fn function is called to test whether the element passes the test.

  • If an element passes the test, it is added to the result array using the push() method.

  • If an element does not pass the test, it is removed from the result array using the pop() method.

  • If all elements in thearraypass the test implemented by the fn function, the result array will be equal to the flattened array, and the function returns true. Otherwise, it returns false.

  • The every() function returns the boolean value returned by the reduce() method.

Oureveryfunction tests the array using the fn function and returns a boolean value that indicates whether all elements in the array pass the test. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the reduce() method to iterate over each element of the flattened array and test it using the fn function. If any of the input parameters fail the validation checks, the function returns false.

Next, let’s create aforEachfunction.  Here is an implementation of ourforEachfunction: 

				
					/**
 * @name forEach
 * @function
 *
 * @param {Array} array the input array
 * @param {Function} fn the predicate function
 * @param {Number} flat  the array to loop over flattening depth
 *  
 * @description performs fn() operation for each of the array elements
 * 
 * @return {Function|Object} the resulting object or array or element from the fn() operation 
 * 
 */

const forEach = (array = [], fn = () => false, flat = 0)  => {
    if(inputsValid(array, fn, flat)){
        for (let i = 0; i < array.flat(flat).length; i++) {
            fn(array.flat(flat)[i],i,array);
        }
    }else{
        return undefined;
    }
};
				
			
  • TheforEachfunction takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns undefined.

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses a for loop to iterate over each element of the flattened array.

  • For each element of the flattened array, the fn function is called with the current element as its argument.

  • The function does not return a value.

OurforEachfunction iterates over the array using a for loop and calls the fn function for each element of the array. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using a for loop to iterate over each element of the flattened array and call the fn function with the current element as its argument. If any of the input parameters fail the validation checks, the function returns undefined.

Next, let’s create afilterfunction.  Here is an implementation of ourfilterfunction: 

				
					/**
 * @name filter
 * @function
 *
 * @param {Array} array the array to filter
 * @param {Function} fn the predicate function
 * @param {Number} flat  the array to filter flattening depth
 *  
 * @description filters an array according to the thruthiness of the predicate
 * 
 * @return {Array} the resulting array
 * 
 */

const filter = (array = [], fn = () => false, flat = 0, result = []) => {
   if(inputsValid(array, fn, flat)){
    for (let i = 0; i < this.flat(flat).length; i++) {
        fn(array.flat(flat)[i],i,array) ? result.push(array.flat(flat)[i]) : [];
    }
    return result.length > 0 ? result : [];
   }else{
       return [];
   }
};
				
			
  • The filter function takes four parameters: array, fn, flat, and result.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns an empty array [].

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses a for loop to iterate over each element of the flattened array.

  • For each element of the flattened array, the fn function is called with the current element, index and the entire array as arguments, to test whether the element passes the test.

  • If an element passes the test implemented by the fn function, it is added to the result array using the push() method.

  • If an element does not pass the test, it is not added to the result array.

  • The filter() function returns the result array if it contains any elements that passed the test. Otherwise, it returns an empty array [].

Ourfilterfunction filters the array using the fn function and returns an array that contains the elements of array that pass the test. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using a for loop to iterate over each element of the flattened array and call the fn function with the current element, index and the entire array as arguments. If an element passes the test, it is added to the result array, which is then returned by the function if it contains any elements that passed the test. If any of the input parameters fail the validation checks, the function returns an empty array [].

Next, let’s create a flattenfunction.  Here is an implementation of ourflattenfunction: 

				
					/**
 * @name flatten
 * @function
 *
 * @param {Array} array the array to flatten
 *  
 * @description filten an array to whatsover depth or level it has
 * 
 * @return {Array} the resulting flattened array
 * 
 */

const flatten =  (array =[], result = [])  => {
    array.forEach(el => (Array.isArray(el) ? result.push(...flatten(el)) : result.push(el)));
    return result;
};
				
			
  • Theflattenfunction takes two parameters: array and result.

  • The function first checks whether the input parameter array is an array using the Array.isArray() method. If array is not an array, it returns the result array.

  • If array is an array, the function uses the forEach() method to iterate over each element of array.

  • For each element of array, the flatten function checks whether it is an array using the Array.isArray() method.

  • If the element is an array, the flatten function calls itself recursively, passing the element as the array parameter and the result array as the result parameter.

  • If the element is not an array, the flatten function adds it to the result array using the push() method.

  • The flatten() function returns the result array after all elements of array have been flattened.

Ourflattenfunction flattens a multi-dimensional array to a single depth by recursively calling itself on each element of the array that is an array, and adding all non-array elements to the result array using the push() method. It does this by checking whether the input parameter array is an array, and then iterating over each element of array using the forEach() method. If an element is an array, the flatten function calls itself recursively on that element, passing the result array as the result parameter. If an element is not an array, it is added to the result array using the push() method. Finally, the flatten() function returns the result array after all elements of array have been flattened.

Next, let’s create afindIndexfunction.  Here is an implementation of ourfindIndexfunction: 

				
					 /**
 * @name findIndex
 * @function
 *
 * @param {Array} array the input array
 * @param {Function} fn the predicate function
 * @param {Number} flat The input array flattening depth
 *  
 * @description find the index of an array element
 * 
 * @return {Array} the resulting array element
 * 
 */
const findIndex =  (array = [], fn = () => false, flat = 0) => inputsValid(array, fn, flat) ? array.flat(flat).reduce((x, y, z) => (x === -1 && fn(y) ? z : x), -1): undefined;
				
			
  • The findIndex function takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns undefined.

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the reduce() method to iterate over each element of the flattened array.

  • For each element of the flattened array, the fn function is called with the current element, index and the entire array as arguments, to test whether the element passes the test.

  • If an element passes the test implemented by the fn function, the reduce() method returns the index of that element, which is then returned by the findIndex() function.

  • If no element passes the test, the reduce() method returns -1, which is then returned by the findIndex() function.

  • The findIndex() function returns undefined if any of the input parameters fail the validation checks.

OurfindIndex function searches the array using the fn function and returns the index of the first element in array that passes the test. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the reduce() method to iterate over each element of the flattened array and call the fn function with the current element, index and the entire array as arguments. If an element passes the test, the index of that element is returned by the reduce() method, which is then returned by the findIndex() function. If no element passes the test, the reduce() method returns -1, which is then returned by the findIndex() function. If any of the input parameters fail the validation checks, the function returns undefined.

Next, let’s create a mapfunction.  Here is an implementation of ourmapfunction: 

				
					/**
 * @name map
 * @function
 *
 * @param {Array} array the array to map
 * @param {Function} fn The predicate function
 * @param {Number} flat The inpurt array flattening depth
 *  
 * @description maps each element with the resulting operation of the callback function
 * 
 * @return {Array} The resulting array 
 * 
 */
const map =  (array = [], fn = () => [], flat = 0) => inputsValid(array, fn, flat) ? array.flat(flat).reduce((x, y) => x.concat(fn(y)), []) : [];
				
			
  • The map function takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns an empty array [].

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the reduce() method to iterate over each element of the flattened array.

  • For each element of the flattened array, the fn function is called with the current element as an argument, and the result is concatenated to the accumulating x array.

  • The reduce() method returns the accumulated x array after all elements of the flattened array have been mapped.

  • The map() function returns the result of the reduce() method.

  • The map() function returns an empty array [] if any of the input parameters fail the validation checks.

Ourmap function maps the array using the fn function and returns a new array that contains the result of applying the fn function to each element of array. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the reduce() method to iterate over each element of the flattened array and apply the fn function to it. The result of each application of fn function is concatenated to the accumulating x array, which is then returned by the reduce() method. The map() function returns the result of the reduce() method. If any of the input parameters fail the validation checks, the function returns an empty array [].

Next, let’s create afindfunction.  Here is an implementation of ourfindfunction: 

				
					/**
 * @name find
 * @function
 *
 * @param {Array} array The input array
 * @param {Function} fn The predicate function
 * @param {Number} flat The input array flattening depth
 *  
 * @description Finds the first array element for which the predicate is true
 * 
 * @return {Array} The resulting array element
 * 
 */

const find  = (array = [], fn = () => false, flat = 0) => inputsValid(array,fn,flat) ? array.flat(flat).reduce((x, y) => (x === undefined && fn(y) ? y : x), undefined): undefined;

				
			
  • The find function takes three parameters: array, fn, and flat.

  • It checks whether the input parameters array, fn, and flat are valid by calling the inputsValid function. If any of the input parameters fail the validation checks, the function returns undefined.

  • If all input parameters pass the validation checks, the function flattens the array to the specified depth using the flat() method.

  • It then uses the reduce() method to iterate over each element of the flattened array.

  • For each element of the flattened array, the fn function is called with the current element as an argument.

  • If an element passes the test implemented by the fn function, the reduce() method returns that element, which is then returned by the find() function.

  • If no element passes the test, the reduce() method returns undefined, which is then returned by the find() function.

  • The find() function returns undefined if any of the input parameters fail the validation checks.

Ourfindfunction searches the array using the fn function and returns the first element in array that passes the test. It does this by validating the input parameters array, fn, and flat, flattening the array to the specified depth, and then using the reduce() method to iterate over each element of the flattened array and call the fn function with the current element as an argument. If an element passes the test, that element is returned by the reduce() method, which is then returned by the find() function. If no element passes the test, the reduce() method returns undefined, which is then returned by the find() function. If any of the input parameters fail the validation checks, the function returns undefined.

Related Articles

Responses