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
}