array/uniqArr.ts

/** @module Array */

import { isArr } from './isArr'

/**
 * Removes duplicates from an array, checking by reference-equality
 * @function
 * @example
 * uniqArr([1,1,2,3,3])
 * // Returns array with only unique values [ 1, 2, 3 ]
 * @param {array} arr - array to remove duplicates from
 * @return {array} copy of passed in array, with duplicates removed
 */
export const uniqArrByReference = <T = any>(arr: any[]): T[] => {
  return !isArr(arr) ? arr : arr.filter((e, i, arr) => arr.indexOf(e) == i)
}

/**
 *
 * @param {*} arr
 * @param {*} selector
 */
/**
 * Removes duplicates from an array.
 * @function
 * @example
 * uniqArr([1,1,2,3,3])
 * // Returns array with only unique values [ 1, 2, 3 ]
 * @example
 * uniqArr([ {a: 1} , { a: 1 }], element => element.a)
 * // Returns array [ { a: 1 } ]
 * @param {array} arr - array to remove duplicates from
 * @param {Function} [selector] - optional function to specify the property uniqArr should use to check if another element exists
 * @return {array} copy of passed in array, with duplicates removed
 */
export const uniqArr = <T = any>(
  arr: any[],
  selector?: (element: any, idx?: number) => any
): T[] => {
  if (!selector) return uniqArrByReference(arr)

  // loop over each element in one pass,
  // only including in the unique array elements
  // we haven't encountered before
  // by checking with `selector` and the set
  const { unique } = arr.reduce(
    (data, element, index) => {
      const id = selector(element, index)
      !data.set.has(id) && data.unique.push(element)
      data.set.add(id)
      return data
    },
    {
      unique: [],
      set: new Set(),
    }
  )

  return unique
}