array/flatMap.ts

/** @module Array */

import { isArr } from './isArr'
import { isFunc } from '@method/isFunc'
import { validate } from '@validation/validate'

/**
 * Maps each element using mapping function `mapFn`, but returns the result as a flattened array.
 * It is equivalent to map() followed by flattening to depth 1, but flatMap is a useful shortcut,
 * and merging both steps into one method (with one pass over the array) is slightly more efficient.
 * @function
 * @example
 * [1, 2].map(x => [x * 2]) // returns [[2], [4]]
 * flatMap([1, 2], x => [x * 2]) // returns [2, 4]
 * @param {Array} arr - array to map across
 * @param {Function} mapFn - function for mapping
 */
export const flatMap = <T = any>(
  arr: any[],
  mapFn: (current: any) => any
): T[] => {
  const [inputIsValid] = validate({ arr, mapFn }, { arr: isArr, mapFn: isFunc })
  if (!inputIsValid) return arr

  // iterate across the array, calling mapFn on each element, then flattening into final array
  return arr.reduce((finalArr, current) => {
    const result = mapFn(current)
    isArr(result)
      ? result.map((el: any) => finalArr.push(el))
      : finalArr.push(result)
    return finalArr
  }, [])
}