API Docs for:
Show:

File: development/js/utils/array.js

import type from './type';

/**
 * Provides array functions
 *
 * @module Utils
 * @class Utils.Array
 * @static
 */

function __slice (xs) {
    var _start = arguments[1] || 0,
        _end = arguments[2] || xs.length;

    return Array.prototype.slice.call(xs, _start, _end);
}

function flattenTCO (xs, acc) {
    if (xs.length < 1) {
        return acc;
    }

    if (type.isArray(xs[0])) {
        return flattenTCO(xs[0].concat(__slice(xs, 1)), acc);
    }

    return flattenTCO(__slice(xs, 1), acc.concat(xs[0]));
}



/**
 * Converts strings, objects and array-like values into real arrays.
 *
 * @method toArray
 * @for Utils.Array
 * @param {string|array-like|object} v Value to transform
 * @return {array} A real array
 *
 * @example
 *     import {toArray} from './utils/array';
 * 
 *     var nodes = toArray(document.querySelectorAll('div'));
 *     // -> [<div>, <div>, ...]
 */
export const toArray = (v, from, until) => {
    if (type.isString(v)) {
        return __slice(v.split(''), from, until);
    }

    if (type.isSeq(v)) {
        return __slice(v, from, until);
    }

    if (type.isObject(v)) {
        return Object.keys(v).map((k) => v[k]);
    }

    throw new Error('Unable to convert ' + v + ' to array');
}

/**
 * Given a array, string or otherwise sequencial object, returns the first item
 *
 * @method first
 * @for Utils.Array
 * @param {string|array|array-like} xs Sequencial object
 * @return {any|null} First item or null
 *
 * @example
 *     import {first} from './utils/array';
 * 
 *     first([1, 2, 3, 4]); // -> 1
 */
export const first = (xs) => {
    if (!type.isSeq(xs)) {
        return null;
    }
    return xs[0];
}

/**
 * Given a array, string or otherwise sequencial object, returns the last item
 *
 * @method last
 * @for Utils.Array
 * @param {string|array|array-like} xs Sequencial object
 * @return {any|null} Last item or null
 *
 * @example
 *     import {last} from './utils/array';
 * 
 *     last([1, 2, 3, 4]);
 *     // -> 4
 */
export const last = (xs) => {
    if (!type.isSeq(xs)) {
        return null;
    }
    return xs[xs.length - 1];
}


/**
 * Given a array, string or otherwise sequencial object, returns the head item
 *
 * @method head
 * @for Utils.Array
 * @param {string|array|array-like} xs Sequencial object
 * @return {array} List with the head item
 *
 * @example
 *     import {head} from './utils/array';
 * 
 *     head([1, 2, 3, 4]);
 *     // -> [1]
 */
export const head = (xs) => {
    if (!type.isSeq(xs)) {
        return [];
    }
    return [xs[0]];
}


/**
 * Given a array, string or otherwise sequencial object, returns the tail item
 *
 * @method tail
 * @for Utils.Array
 * @param {string|array|array-like} xs Sequencial object
 * @return {array} List with the tail item
 *
 * @example
 *     import {tail} from './utils/array';
 * 
 *     tail([1, 2, 3, 4]);
 *     // -> [4]
 */
export const tail = (xs) => {
    if (!type.isSeq(xs)) {
        return [];
    }
    return [xs[xs.length - 1]];
}


/**
 * Given a array, string or otherwise sequencial object, returns all items but
 *     the last
 *
 * @method initial
 * @for Utils.Array
 * @param {string|array|array-like} xs Sequencial object
 * @return {array} List with the initial items
 *
 * @example
 *     import {initial} from './utils/array';
 * 
 *     initial([1, 2, 3, 4]);
 *     // -> [1, 2, 3]
 */
export const initial = (xs) => {
    if (!type.isSeq(xs)) {
        return [];
    }
    return __slice(xs, 0, xs.length - 1);
}


/**
 * Given a array, string or otherwise sequencial object, returns all items but
 *     the first
 *
 * @method rest
 * @for Utils.Array
 * @param {string|array|array-like} xs Sequencial object
 * @return {array} List with the rest items
 *
 * @example
 *     import {rest} from './utils/array';
 * 
 *     rest([1, 2, 3, 4]);
 *     // -> [2, 3, 4]
 */
export const rest = (xs) => {
    if (!type.isSeq(xs)) {
        return [];
    }
    return __slice(xs, 1);
}

/**
 * Given a enumerable value, counts the number of items the enumerable contains
 *
 * @method sizeOf
 * @for Utils.Array
 * @param {string|array|array-like|object} xs Enumerable
 * @return {number} Number of items inside xs
 *
 * @example
 * import {sizeOf} from './utils/array';
 * 
 *     sizeOf('hello world'); // -> 11
 *     sizeOf([1, 2, 3]); // -> 3
 *     sizeOf({a: 1, b: 2, c: 3}); // -> 3
 */
export const sizeOf = (xs) => {
    if (!type.isEnum(xs)) {
        return 0;
    }

    return type.isNumber(xs.length) ? xs.length : Object.keys(xs).length;
}

export const fits = (nth, xs) => {
    if (!type.isSeq(xs)) {
        return false;
    }

    if (type.isObject(xs) && type.isString(nth)) {
        return xs.hasOwnProperty(nth);
    }

    return type.isNumber(nth) && nth >= 0 && nth < xs.length;
}

export const nthOf = (nth, xs) => {
    if (!fits(nth, xs)) {
        return null;
    }
    return xs[nth];
}

/**
 * If given a multidimensional array, flattens it into a single dimension and
 *     returns the result
 *
 * @method flatten
 * @for Utils.Array
 * @param {array} xs Multidimensional array
 * @return {array} Flattened array
 *
 * @example
 *     import {flatten} from './utils/array';
 * 
 *     flatten([1, 2, [3, [4]]]);
 *     // -> [1, 2, 3, 4]
 */
export const flatten = (xs) => {
    return flattenTCO(xs, []);
}

export const take = (n, xs) => {
    if (!type.isSeq(xs)) {
        return [];
    }

    if (!fits(n, xs)) {
        return head(xs);
    }

    return __slice(xs, 0, n);
}

export const drop = (n, xs) => {
    if (!type.isSeq(xs)) {
        return [];
    }

    if (!fits(n, xs)) {
        return tail(xs);
    }

    return __slice(xs, n);
}



export default {
    toArray, first, last, initial, rest, head, tail, nthOf, fits, flatten, take,
    drop, sizeOf
}