All files index.js

100% Statements 113/113
100% Branches 47/47
100% Functions 5/5
100% Lines 113/113

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 1141x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 1x 1x 1x 1x 1x 1x 1x 1x 7x 7x 1x 1x 1x 1x 1x 1x 1x 1x 1x 153x 18x 18x 153x 16x 16x 16x 16x 16x 16x 16x 16x 16x 4x 153x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 18x 18x 17x 17x 17x 17x 17x 17x 2x 2x 17x 17x 13x 13x 13x 18x 12x 12x 12x 12x 12x 9x 9x 9x 18x 8x 8x 8x 8x 7x 7x 7x 7x 7x 7x 7x 7x 7x 7x 18x 1x 1x  
/**
 * Partially apply to a function the first argument and return specialization.
 * @param {function} func - function to be specialized.
 * @param {string} first - value to be fixed as first positional argument.
 * @returns {function} function with the first argument primed.
 */
const partial = (func, first) => {
  return (...rest) => {
    return func(first, ...rest)
  }
}
 
/**
 * Create zipper for pairs that creates a pair object given two parameters.
 * @param {string} first - key mapping to the first positional value.
 * @param {string} second - key mapping to the second and last positional value.
 * @returns {function} that creates a pair object.
 */
const zip_pair = (first, second) => {
  return (left, right) => ({[first]: left, [second]: right})
}
 
/**
 * We invented a wheel again - hooray
 *
 * @param {*} me - maybe an object.
 * @param {*} you - maybe an object.
 * @returns {bool} true if deeply equal.
 */
const deep_equal = (me, you) => {
  if (!is_object(me) || !is_object(you)) return me === you
 
  const [my_keys, your_keys] = [Object.keys(me), Object.keys(you)]
  if (my_keys.length !== your_keys.length) return false;
 
  for (const key of my_keys) {
    const [my_val, your_val] = [me[key], you[key]]
    const are_objects = is_object(my_val) && is_object(your_val)
    if (
      are_objects && !deep_equal(my_val, your_val) ||
      !are_objects && my_val !== your_val
    ) return false
  }
  return true
}
 
/**
 * We invented another wheel again - hip, hip, hooray
 *
 * @param {*} thing - maybe an object.
 * @returns {bool} true if given an object.
 */
const is_object = thing => thing != null && typeof thing === 'object'
 
/**
 * Derive an array of objects from pairs that map population samples to distinct features.
 *
 * The default (but optional) validation ensures:
 *
 * * population and pairs both are non-empty arrays of non-null values
 * * features are unique (first elements of pairs)
 * * pairs second entries only provide members of population
 *
 * @param {array} population - array with all members of the population.
 * @param {string} sample - key mapping to the sample from the population.
 * @param {string} feature - key mapping to the feature variations.
 * @param {array} pairs - pairs of features and array of samples (null for final pair).
 * @param {bool} [force=false] - (false does / true does not) validate use.
 * @returns {array} of objects that map population samples to distinct features.
 */
const cook = (population, sample, feature, pairs, force=false) => {
 
  if (!force) {
    if (!(population instanceof Array)
        || !population.length
        || !(pairs instanceof Array)
        || !pairs.length
    ) return undefined  // population or pairs no arrays or empty
    if (population.indexOf(null) !== -1) {
      population = population.filter(p => p != null)
    }
    if (!population.length) return undefined  // cleansed population empty
  }
 
  derived = [...pairs]  // Create a copy of the pairs array
 
  if (!force) {
    const features = derived.map(pair => pair[0]).filter((el, ndx, arr) => arr.indexOf(el) === ndx)
    if ((pairs.length !== features.length)  // simple duplicates
        || (!features.every((xi, i, arr) => arr.every((xj, j) => i === j || !deep_equal(xi, xj))))  // deep duplicates
    ) return undefined
  }
 
  const used = derived.map(pair => pair[1]).filter(arr => arr).flat()  // registry of used population members
 
  if (!force) {
    if ((used.length > population.length)  // over population
        || (used.some(e => population.indexOf(e) === -1))  // non-members
    ) return undefined
  }
 
  const rest = population.filter(m => !used.includes(m))  // set difference
 
  const variation = derived[derived.length-1][0]
  derived[(derived.length - 1)] = [variation, rest]  // complete population
 
  const make = zip_pair(feature, sample)  // primed object creator
  derived.forEach((pair, i, seq) => seq[i] = make(...pair))
 
  return derived
}
 
module.exports = { cook: cook, partial: partial }