Source: utils.js

// classes
import CrioArray from './CrioArray';
import CrioObject from './CrioObject';

// is
import {isArray, isCrio, isObject, isReactElement} from './is';

/**
 * @function createIterator
 *
 * @description
 * create a new iterator method
 *
 * @returns {function} the iterator method
 */
export const createIterator = () => {
  return function iterator() {
    const keys = this.keys();
    const length = keys.length;

    let index = 0,
      value;

    return {
      next: () => {
        if (index < length) {
          value = this[keys[index++]];

          return {
            done: false,
            value
          };
        }

        return {
          done: true
        };
      }
    };
  };
};

/**
 * @function every
 *
 * @description
 * does every result from calling fn match
 *
 * @param {CrioArray|CrioObject} object the object to test
 * @param {function} fn the function to perform the test on each item with
 * @returns {boolean} does every item match
 */
export const every = (object, fn) => {
  const objectKeys = object.keys();

  let key;

  for (let index = 0; index < objectKeys.length; index++) {
    key = objectKeys[index];

    if (!fn(object[key], key, object)) {
      return false;
    }
  }

  return true;
};

/**
 * @function find
 *
 * @description
 * find an item in the array if it exists
 *
 * @param {CrioArray|CrioObject} object the object to search
 * @param {function} fn function to test for finding the item
 * @param {boolean} [isKey] is the search for a key
 * @param {boolean} [isFromEnd] is the search for a key
 * @returns {*} found item or undefined
 */
export const find = (object, fn, isKey, isFromEnd) => {
  const objectKeys = isFromEnd ? object.keys().reverse() : object.keys();

  let key;

  for (let index = 0; index < objectKeys.length; index++) {
    key = objectKeys[index];

    if (fn(object[key], key, object)) {
      return isKey ? key : object[key];
    }
  }

  if (isKey) {
    return object.isArray() ? -1 : undefined;
  }

  return undefined;
};

/**
 * @function getCrioedObject
 *
 * @description
 * get the object converted to a CrioArray or CrioObject, if applicable
 *
 * @param {*} object the object to potentially crio
 * @returns {*} either the crioed object, or the object itself
 */
export const getCrioedObject = (object) => {
  if (!object || typeof object !== 'object') {
    return object;
  }

  if (isArray(object)) {
    return object instanceof CrioArray ? object : new CrioArray(object);
  }

  return isObject(object) ? new CrioObject(object) : object;
};

/**
 * @function getEntries
 *
 * @description
 * get the [key, value] pairs of the object
 *
 * @param {CrioArray|CrioObject} object the object to get the entries of
 * @returns {CrioArray} the entries of the object
 */
export const getEntries = (object) => {
  return object.keys().map((key) => {
    return [key, object[key]];
  });
};

/**
 * @function getRelativeValue
 *
 * @description
 * get the relative value used in copyWithin
 *
 * @param {number} value value used as baseline
 * @param {number} length the length of the crio
 * @returns {number} the relative number value
 */
export const getRelativeValue = (value, length) => {
  return value < 0 ? Math.max(length + value, 0) : Math.min(value, length);
};

/**
 * @function getValues
 *
 * @description
 * get the values of the object
 *
 * @param {CrioArray|CrioObject} object the object to get the values of
 * @returns {CrioArray} the values of the object
 */
export const getValues = (object) => {
  return object.keys().map((key) => {
    return object[key];
  });
};

/**
 * @function some
 *
 * @description
 * does any result from calling fn match
 *
 * @param {CrioArray|CrioObject} object the object to test
 * @param {function} fn the function to perform the test on each item with
 * @returns {boolean} does any item match
 */
export const some = (object, fn) => {
  const objectKeys = object.keys();

  let key;

  for (let index = 0; index < objectKeys.length; index++) {
    key = objectKeys[index];

    if (fn(object[key], key, object)) {
      return true;
    }
  }

  return false;
};

/**
 * @function thaw
 *
 * @description
 * convert the CrioArray or CrioObject passed to a plain JS object
 *
 * @param {CrioArray|CrioObject} object the object to convert
 * @returns {Array|Object} the plain JS version of the object passed
 */
export const thaw = (object) => {
  if (!isCrio(object)) {
    return object;
  }

  return object.isArray()
    ? [...object].map((item) => {
        return thaw(item);
      })
    : Object.keys(object).reduce((reducedObject, key) => {
        reducedObject[key] = thaw(object[key]);

        return reducedObject;
      }, {});
};