/** @module Array */
import { validate } from '@validation/validate'
import { isArr } from './isArr'
/**
* Builds a map of elements mapped to their frequency counts
* @function
* @param {Array<*>} arr
* @return {Map<*, number>}
*/
export const buildElementCountMap = (arr: any[]): Map<any, number> => {
const counts = new Map()
for (let i = 0; i < arr.length; i++) {
const element = arr[i]
const count = counts.get(element) ?? 0
counts.set(element, count + 1)
}
return counts
}
/**
* Returns true if the maps
* @function
* @param {Map<*, number>} mapA
* @param {Map<*, number>} mapB
* @returns {Boolean} - True if the item count it equal between mapA and mapB
*/
export const areCountMapsEqual = (
mapA: Map<any, number>,
mapB: Map<any, number>
): boolean => {
if (mapA.size !== mapB.size) return false
for (let [key, count] of mapA) {
const otherCount = mapB.get(key)
if (otherCount !== count) return false
}
return true
}
/**
* Checks if arrays are frequency equal. Does this
* by making only one pass over each array and using an auxillary map.
* @function
* @param {Array<*>} arr
* @param {Array<*>} otherArr
* @returns {Boolean} - True if otherArr contains exactly the same elements as arr, where order does not matter, but frequency does
*/
export const areFrequencyEqual = (arr: any[], otherArr: any[]): boolean => {
const [valid] = validate({ arr, otherArr }, { $default: isArr })
if (!valid) return null
if (arr === otherArr) return true
if (arr.length !== otherArr.length) return false
const arrCounts = buildElementCountMap(arr)
const otherCounts = buildElementCountMap(otherArr)
return areCountMapsEqual(arrCounts, otherCounts)
}