array.js

/**
 * @module array
 */


/**
 * Tests an array agains another array, returns true 
 * if the array has all items in the required array
 * @param {Array} array Array to test on
 * @param {Array} required Elements that should be present in the array
 * @returns {Boolean}
 */
export function arrayHas(array, required) {
  return required.every(val => array.includes(val));
}

/**
 * Adds properties to an array of objects
 * @param {Array} array Array of Objects
 * @param {Object} props Properties to add to each object in array
 */
export function arrayAssign(array, props) {
  array.forEach(obj => Object.assign(obj, props));
  return array;
}


/**
 * Creates a new array of X length and fills the array using callback
 * @param {Number} count Number of elements to create
 * @param {Funtion} callback Callback to template the array element (passed only index)
 * @returns Array
 */
export function arrayCreate(count, callback) {
  return [...Array(count)].map((_, index) => callback(index));
}

/**
 * Removes an array element (modifies array)
 * @param {Array} array Array to remove element from
 * @param {Element} element Array element to remove
 */
export function removeArrayElement(array, element) {
  var index = array.indexOf(element);
  if (index > -1) {
    array.splice(index, 1);
  }
}

/**
 * Searches array for first item matching test, beginning at a start index but searching the entire array
 * @param {Array} array Array to search
 * @param {Number} start The index in the array to start the search from
 * @param {Function} callback A test function that is passed array item and index
 */
export function offsetFindIndexOf(array, start = 0, callback) {
  let found, offset;
  for (var i = 0; i < array.length; i++) {
    offset = (i + start) % array.length;
    found = callback(array[offset], offset);
    if (found) return offset;
  }
  return -1;
}

/**
 * Remove duplicate items in array
 * @param {Array} array Array to remove duplicates from
 * @returns {Array} New array with duplicates removed
 */
export function removeDuplicates(array) {
  const set = new Set(array);
  return [...set];
}

/**
 * Create empty matrix with optional value
 * @param {Number} columnCount 
 * @param {Number} rowCount 
 * @param {*} value Value to set, default is null
 * @returns {Array.Array} Matrix (array of arrays)
 */
export function createEmptyMatrix(columnCount, rowCount, value = null) {
  const matrix = [];
  while (rowCount) {
    matrix.push(Array(columnCount).fill(value));
    --rowCount;
  }
  return matrix;
}


/**
 * Joins an array into sentence form
 * - IE ["2013", "2015", "2020"] --> "2013, 2015 and 2020"
 * @param {Array.<String, Number>} arr Array to join
 * @returns {String}
 */
export function joinForSentence(array) {
  if (array.length === 0) {
    return "";
  } else if (array.length === 1) {
    return array[0];
  } else {
    return array.slice(0, array.length - 1).join(", ") + " and " + array[array.length - 1];
  }
}

/**
 * Test whether an index in an array is first or last
 * - Returns an object with both first/last keys (true/false)
 * @param {Array} array Array to check index against (uses .length)
 * @param {Number} index Index to get info for
 * @returns {Object} { first: {Boolean}, last: {Boolean} }
 */
export function getFirstLast(array, index) {
  return {
    first: index === 0,
    last: index === array.length - 1
  };
}


/**
 * Filter Array In Place
 * - Will not copy array, mutates the array passed (instead of copy [ie. Array.filter()])
 * - Note, removes items in reverse order (end-to-start of array), test will be called on last item first and so on
 * @param {Array} array The array to filter items from
 * @param {Function} test A function to filter elements based on a truthy/falsy condition
 * @returns {Array} The original array
 */
export function filterInPlace(array, test) {
  for (let i = array.length - 1; i >= 0; i--) {
    if (!test(array[i], i, array)) {
      array.splice(i, 1);
    }
  }
  return array;
}