/** @module Array */
import { isFunction } from './type'
/**
* 获取数组的最后一个值
* @function lastItem
* @param {array} arr - 源数组
* @return {*}
* @example
* let value = U.lastItem([1, 1, 2, 3])
* // => 3
*
* let value = U.lastItem([])
* // => undefined
*/
export const lastItem = arr => arr[arr.length -1]
/**
* 数组去重,返回无重复值的新数组。
* @function uniqueItems
* @param {array} arr - 需要去重的源数组
* @return {array}
* @example
* let arr = [1, 1, 2, 3, 3, 4, 5]
* arr = U.uniqueItems(arr)
* // => [1, 2, 3, 4, 5]
*/
export const uniqueItems = arr => [...new Set(arr)]
/**
* 根据提供的比较器函数返回数组的所有唯一值。
* @function uniqueItemsBy
* @param {array} arr - 数组
* @param {function} fn - 比较器函数
* @param {*} fn.a - 比较元素
* @param {*} fn.b - 比较元素
* @param {boolean} [isRight=false] - 可选,默认false,是否从数组最后一个元素开始比较
* @return {array}
* @example
* U.uniqueItemsBy([
* { id: 0, value: 'a' },
* { id: 1, value: 'b' },
* { id: 2, value: 'c' },
* { id: 0, value: 'd' }
* ],
* (a, b) => a.id == b.id)
* // => [{ id: 0, value: 'a' }, { id: 1, value: 'b' }, { id: 2, value: 'c' }]
*
* U.uniqueItemsBy([
* { id: 0, value: 'a' },
* { id: 1, value: 'b' },
* { id: 2, value: 'c' },
* { id: 0, value: 'd' }
* ],
* (a, b) => a.id == b.id,
* true)
* // => [{ id: 0, value: 'd' }, { id: 2, value: 'c' }, { id: 1, value: 'b' }]
*/
export const uniqueItemsBy = (arr, fn, isRight) => arr[isRight ? 'reduceRight' : 'reduce']((acc, x) => {
if (!acc.some(y => fn(x, y))) acc.push(x)
return acc
}, [])
/**
* 检索数组重复元素,返回新数组。
* @function repeatItems
* @param {array} arr - 数组
* @return {array}
* @example
* U.repeatItems([1, 1, 2, 3, 3, 4, 5])
* // => [1, 3]
*/
export const repeatItems = arr => arr.filter(
(item, i) => (
arr.indexOf(item) === i && arr.indexOf(item) !== arr.lastIndexOf(item)
)
)
/**
* 初始化一个给定长度以及值的数组。当映射是一个函数时提供迭代的i和数组长度len两个参数。
* @function initArray
* @param {number} len - 数组长度
* @param {*|function} [val|fn=null] - 可选,数组元素的映射值,默认为null;当映射是一个函数时,该函数参数如下表:
* @param {number} fn.index - 可选,数组中正在处理的当前元素的索引
* @param {number} fn.length - 可选,数组的长度
* @return {array}
* @example
* console.log(U.initArray(3))
* // => [null, null, null]
*
* const arr = U.initArray(3, {a: 1, b: 2})
* // => [ { a: 1, b: 2 }, { a: 1, b: 2 }, { a: 1, b: 2 } ]
*
* const arr = U.initArray(3, (i) => i * 2)
* // => [ 0, 2, 4 ]
*/
export const initArray = (len, val = null) => (
isFunction(val) ? Array.from({length: len}, (item, i) => val(i, len)) : Array.from({length: len}).fill(val)
)
/**
* 使用函数将数组的值映射到对象,其中键 - 值对由数组原始值作为键和映射值组成。
* @function mapObject
* @param {array} arr - 对象键名的数组
* @param {function(currentValue, index, array)} fn - 生成对象值的映射函数
* @param {*} fn.currentValue - 数组中正在处理的当前元素
* @param {number} fn.index - 可选,数组中正在处理的当前元素的索引
* @param {array} fn.array - 可选,当前正在处理的数组
* @return {object}
* @example
* const obj = U.mapObject([1, 2, 3], i => i * 2)
* // => {1: 2, 2: 4, 3: 6}
*/
export const mapObject = (arr, fn) => {
arr = [arr, arr.map(fn)]
return arr[0].reduce((acc, val, i) => {
acc[val] = arr[1][i]
return acc
}, {})
}
/**
* 求数组内元素特定键或键映射的平均值
* @function averageBy
* @param {array} arr - 求值数组
* @param {function|string} fn - 键值运算映射函数或键名
* @return {number}
* @example
* const arr = [{a: 1, b: 2}, {a: 2, b: 4}]
*
* U.averageBy(arr, 'a')
* // => 1.5
*
* U.averageBy(arr, o => o.a * o.b)
* // => 5
*/
export const averageBy = (arr, fn) => (
arr.map(isFunction(fn) ? fn : val => val[fn]).reduce((acc, v) => acc + v, 0) / arr.length
)
/**
* 求数组内元素特定键或键映射的最大值
* @function maxBy
* @param {array} arr - 求值数组
* @param {function|string} fn - 键值运算映射函数或键名
* @return {number}
* @example
* const arr = [{a: 1, b: 2}, {a: 2, b: 4}]
*
* U.max(arr, 'a')
* // => 2
*
* U.maxBy(arr, o => o.a * o.b)
* // => 8
*/
export const maxBy = (arr, fn) => Math.max(...arr.map(isFunction(fn) ? fn : v => v[fn]))
/**
* 求数组内元素特定键或键映射的最小值
* @function minBy
* @param {array} arr - 求值数组
* @param {function|string} fn - 键值运算映射函数或键名
* @return {number}
* @example
* const arr = [{a: 1, b: 2}, {a: 2, b: 4}]
*
* U.minBy(arr, 'a')
* // => 1
*
* U.minBy(arr, o => o.a * o.b)
* // => 2
*/
export const minBy = (arr, fn) => Math.min(...arr.map(isFunction(fn) ? fn : v => v[fn]))
/**
* 将数组切割分组函数
* @function chunk
* @param {array} arr - 切割的数组
* @param {number} size - 切割数组的长度
* @return {array}
* @example
* chunk([1, 2, 3, 4, 5], 2)
* => [[1,2],[3,4],[5]]
*/
export const chunk = (arr, size) => (
Array.from({ length: Math.ceil(arr.length / size) }, (v, i) =>
arr.slice(i * size, i * size + size)
)
)