Source: array.js

'use strict';

const _ = require('lodash');

const Wrapper = require('../utils/wrapper');

/**
 * Flatten an input Array
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {boolean} [flattenDeep] - whether to flatten recursively or not (default: false)
 * @return {Array} flattened Array
 *
 * @example
 * flatten(4) // => []
 * flatten(true) // => []
 * flatten(Infinity) // => []
 * flatten(null) // => []
 * flatten() // => []
 * flatten(NaN) // []
 * flatten(4.5) // => []
 * flatten([3, 4, 5]) // => [3, 4, 5]
 * flatten('hello') // => ['h', 'e', 'l', 'l', 'o']
 * flatten({answer: 42}) // => []
 * flatten(function () {}) // => []
 * flatten([[3], [[4], 5], [[[[[6]]]]]]) // => [3, [4], 5, [[[[6]]]]]
 * flatten([[3], [[4], 5], [[[[[6]]]]]], {}, true) // => [3, 4, 5, 6]
 */
function flatten(input, object, flattenDeep) {
    if (flattenDeep) {
        return _.flattenDeep(input);
    }
    return _.flatten(input);
}

/**
 * Retrive only unique values from an Array
 * @param  {*} input - current value
 * @return {Array} set of unique elements
 *
 * @example
 * unique(4) // => []
 * unique(true) // => []
 * unique(Infinity) // => []
 * unique(null) // => []
 * unique() // => []
 * unique(NaN) // []
 * unique(4.5) // => []
 * unique([3, 4, 5]) // => [3, 4, 5]
 * unique('hello') // => ['h', 'e', 'l', 'o']
 * unique({answer: 42}) // => []
 * unique(function () {}) // => []
 * unique([4, 3, 4, 2, [3]]) // => [4, 3, 2, [3]]
 */
function unique(input) {
    return _.uniq(input);
}

/**
 * Sum up elements in an Array
 * @param  {*} input - current value
 * @return {int|float} sum of elements in Array
 *
 * @example
 * sum(4) // => 0
 * sum(true) // => 0
 * sum(Infinity) // => 0
 * sum(null) // => 0
 * sum() // => 0
 * sum(NaN) // 0
 * sum(4.5) // => 0
 * sum([3, 4, 5]) // => 12
 * sum('hello') // => 'hello'
 * sum({answer: 42}) // => 0
 * sum(function () {}) // => 0
 */
function sum(input) {
    return _.sum(input);
}

/**
 * Retrieve the nth child from an Array
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {int} [index] - index to retrieve (default: 0)
 * @return {*} retrieved element
 *
 * @example
 * child(4) // =>
 * child(true) // =>
 * child(Infinity) // =>
 * child(null) // =>
 * child() // =>
 * child(NaN) // =>
 * child(4.5) // =>
 * child([3, 4, 5]) // => 3
 * child('hello') // => 'h'
 * child({answer: 42}) // =>
 * child(function () {}) // =>
 * child([4, 3, 4, 3], {}, 3) // => 3
 * child([4, 3, 4, 3], {}, 5) // =>
 * child([4, 3, 4, 3], {}, -2) // => 4
 */
function child(input, object, index) {
    return _.nth(input, index);
}

/**
 * Remove falsey values (ie. false, null, 0, "", undefined, NaN)
 * @param  {*} input - current value
 * @return {Array} Array without falsey values
 *
 * @example
 * compact(4) // => []
 * compact(true) // => []
 * compact(Infinity) // => []
 * compact(null) // => []
 * compact() // => []
 * compact(NaN) // => []
 * compact(4.5) // => []
 * compact([3, 4, 5]) // => [3, 4, 5]
 * compact('hello') // => ['h', 'e', 'l', 'l', 'o']
 * compact({answer: 42}) // => []
 * compact(function () {}) // => []
 * compact([false, 0, null, , '', NaN, 1]) // => [1]
 */
function compact(input) {
    return _.compact(input);
}

/**
 * Concatanate/append/push a new value onto input value to return an Array
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {*} [value] - value to concatenate/append/push
 * @return {Array} new Array with value concatanated, appended or pushed
 *
 * @example
 * concat(4) // => [4]
 * concat(true) // => [true]
 * concat(Infinity) // => [Infinity]
 * concat(null) // => [null]
 * concat() // => [undefined]
 * concat(NaN) // => [NaN]
 * concat(4.5) // => [4.5]
 * concat([3, 4, 5]) // => [3, 4, 5]
 * concat('hello') // => ['hello']
 * concat({answer: 42}) // => [{answer: 42}]
 * concat(function () {}) // => [function () {}]
 * concat([1], {}, 4) // => [1, 4]
 * concat(1, {}, 4) // => [1, 4]
 * concat('hello', {}, 4) // => ['hello', 4]
 * concat([1], {}, true) // => [1, true]
 * concat([1], {}, {answer: 42}) // => [1, {answer: 42}]
 */
function concat(input, object, value) {
    return value ? _.concat(input, value) : _.concat(input);
}

/**
 * Join an Array together given a joinToken
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {string} [joinToken] - a token to join values together
 * @return {string} joined value
 *
 * @example
 * join(4) // => ''
 * join(true) // => ''
 * join(Infinity) // => ''
 * join(null) // => ''
 * join() // => ''
 * join(NaN) // => ''
 * join(4.5) // => ''
 * join([3, 4, 5]) // => '345'
 * join('hello') // => 'hello'
 * join({answer: 42}) // => ''
 * join(function () {}) // => ''
 * join([1, 4, 5], {}, '-') // => '1-4-5'
 */
function join(input, object, joinToken) {
    return _.join(input, joinToken || '');
}

/**
 * Reverse the values in an Array
 * @param  {Array} input - current value
 * @return {Array} reversed Array
 *
 * @example
 * reverse([3, 4, 5]) // => [5, 4, 3]
 * reverse(['hello', 'world', '!']) // => ['!', 'world', 'hello']
 * reverse({answer: 42, me: 2}) // => {answer: 42, me: 2}
 */
function reverse(input) {
    return _.reverse(input);
}

/**
 * Slice values from an Array
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {int} [start] - starting point for slicing (default: 0)
 * @param  {int} [end] - endpoing for slicing
 * @return {Array} sliced Array
 *
 * @example
 * slice(4) // => []
 * slice(true) // => []
 * slice(Infinity) // => []
 * slice(null) // => []
 * slice() // => []
 * slice(NaN) // => []
 * slice(4.5) // => []
 * slice([3, 4, 5]) // => [3, 4, 5]
 * slice('hello') // => ['h', 'e', 'l', 'l', 'o']
 * slice({answer: 42}) // => []
 * slice(function () {}) // => []
 * slice([1, 4, 5], {}, 1, 1) // => []
 * slice([1, 4, 5], {}, 1, 2) // => [4]
 * slice([1, 4, 5], {}, 0, -1) // => [1, 4]
 */
function slice(input, object, start, end) {
    return _.slice(input, start, end);
}

/**
 * Get the length of an Array or string else return 0
 * @param  {*} input - current value
 * @return {int} length of the input Array or 0
 *
 * @example
 * length(4) // => 0
 * length(true) // => 0
 * length(Infinity) // => 0
 * length(null) // => 0
 * length() // => 0
 * length(NaN) // => 0
 * length(4.5) // => 0
 * length([3, 4, 5]) // => 3
 * length('hello') // => 5
 * length({answer: 42}) // => 0
 * length(function () {}) // => 0
 */
function length(input) {
    return Array.isArray(input) || typeof input === 'string' ? input.length : 0;
}

/**
 * Find a match in an Array of Objects
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {*} match - match to look for
 * @return {Object} matched element
 *
 * @example
 * find([{a: 3}, {a: 4}, {b: 5}], {}, 'a') // => {a: 3}
 * find([{a: 3}, {a: 4}, {b: 5}], {}, {b: 5}) // => {b: 5}
 * find([{a: 3}, {a: 4}, {b: 5}], {}, {b: 2}) // =>
 */
function find(input, object, match) {
    return _.find(input, match);
}

/**
 * Find all matches in an Array of Objects
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {*} match - match to look for
 * @return {Array} matched elements
 *
 * @example
 * findWhere([{a: 3}, {a: 4}, {b: 5}], {}, 'a') // => [{a: 3}, {a: 4}]
 * findWhere([{a: 3}, {a: 4}, {b: 5}], {}, {a: 3}) // => [{a: 3}]
 * findWhere([{a: 3}, {a: 4}, {b: 5}], {}, {a: 1}) // => []
 */
function findWhere(input, object, match) {
    return _.filter(input, match);
}

/**
 * Loop through all the elements of an Array
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {Function} func - function to execute on each element
 * @return {Array} Array with each element executed with input function
 *
 * @example
 * map([3, 4, 5], {}, function (a) { return a + 2; }) // => [5, 6, 7]
 * map('hello', {}, function (a) { return a + '^'; }) // => ['h^', 'e^', 'l^', 'l^', 'o^']
 */
function map(input, object, func) {
    return _.map(input, func);
}

/**
 * Loop through elements of an Array and filter out values
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {Function} func - function to execute on each element
 * @return {Array} Array with elements filtered out
 *
 * @example
 * filter([3, -4, -5], {}, function (a) { return a > 0; }) // => [3]
 * filter('hello', {}, function (a) { return a === 'l'; }) // => ['l', 'l']
 */
function filter(input, object, func) {
    return _.filter(input, func);
}

/**
 * Loop through elements of an Array and reduce to a single value
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {Function} func - function to execute on each element
 * @param  {*} initial - initial value into reducer
 * @return {*} reduced value
 *
 * @example
 * reduce([3, 4, 5], {}, function (p, v) { return p + v; }, 0) // => 12
 * reduce([3, 4, 5], {}, function (p, v) { return p + v; }, 10) // => 22
 * reduce('hello', {}, function (p, v) { return p + v; }, 'world ') // => 'world hello'
 */
function reduce(input, object, func, initial) {
    return _.reduce(input, func, initial);
}

/**
 * Loop through elements to sort them
 * @param  {*} input - current value
 * @param  {Object} object - input object into mapper
 * @param  {Function} func - function to execute on each element
 * @return {Array} sorted Array
 *
 * @example
 * sort(4) // => 4
 * sort(true) // => true
 * sort(Infinity) // => Infinity
 * sort(null) // =>
 * sort() // =>
 * sort(NaN) // => NaN
 * sort(4.5) // => 4.5
 * sort('hello') // => 'hello'
 * sort([300, 4, 15]) // => [15, 300, 4]
 * sort([300, 4, 15], {}, function (a, b) { return a - b; }) // => [4, 15, 300]
 * sort([300, 4, 15], {}, function (a, b) { return b - a; }) // => [300, 15, 4]
 * sort([300, 4, 15], {}, function (a, b) { return b - a; }) // => [300, 15, 4]
 * sort(['h', 'e', 'l', 'l', 'o']) // => ['e', 'h', 'l', 'l', 'o']
 */
function sort(input, object, func) {
    return Array.isArray(input) ? input.sort(func) : input;
}

module.exports = {
    flatten: Wrapper.valueHandler(flatten),
    unique: Wrapper.valueHandler(unique),
    sum: Wrapper.valueHandler(sum),
    child: Wrapper.valueHandler(child),
    compact: Wrapper.valueHandler(compact),
    concat: Wrapper.valueHandler(concat),
    join: Wrapper.valueHandler(join),
    reverse: Wrapper.valueHandler(reverse),
    slice: Wrapper.valueHandler(slice),
    length: Wrapper.valueHandler(length),
    find: Wrapper.valueHandler(find),
    findWhere: Wrapper.valueHandler(findWhere),
    map: Wrapper.valueHandler(map),
    filter: Wrapper.valueHandler(filter),
    reduce: Wrapper.valueHandler(reduce),
    sort: Wrapper.valueHandler(sort)
};