Source: index.js

/**
 * Functions that work with hashes/objects/dicionaries using them as "bags", sets that can have repeated elements. Also, assorted utility functions for the same task.
 *
 * In general a "bag" will be a hash or associative array with numeric values. Makes more sense with positive integer values, but all operations will work if values are just numbers.
 *
 * These are sensitive numeric operations, so we prefer to leave all checks on the client side. That means that if you make any operation with a saco with negative coefficients, it will work. What you do with it is up to you.
 *
 * Operations with other non-Object values will either fail or behave as if it was an empty "saco". Again, caveat emptor.
 */

/**
 * Value or 0, returns the value corresponding to a key if it exists, 0 if it does not.
 *
 * @param {Object} hash - a hash, associative array or Object
 * @param {String} key  - A string representing a key, present or not in the hash
 * @returns value corresponding to the `key`or 0 if it does not exist
 */
export function valOr0(hash, key) {
  return key in hash ? hash[key] : 0;
}

/**
 * Initializes a hash value for a key if it does not exist, increments if it does
 * @param {*} hash - hash to be mutated
 * @param {*} key  - key whose value is going to be set or changed
 * @returns the new version of the hash
 */
export function incrementOrInit(hash, key) {
  if (key in hash) {
    hash[key]++;
  } else {
    hash[key] = 1;
  }
  return hash;
}

/**
 *
 * @param {*} arrayOrSet an  composed preferably of strings or a Set.
 * @returns a "saco" or bag that counts the number of repeated elements
 */
export function hashify(arrayOrSet) {
  const initialHash = {};
  const pureArray =
    arrayOrSet instanceof Set ? Array.from(arrayOrSet) : arrayOrSet;
  return pureArray.reduce(
    (runningHash, el) => incrementOrInit(runningHash, el),
    initialHash
  );
}

/**
 * Union of "sacos", retuns an array that uses keys from both sacos and adds their values
 * @param {*} unSaco   - first saco
 * @param {*} otroSaco - second saco
 * @returns {Object} merged "saco"
 */
export function sacoUnion(unSaco, otroSaco) {
  const keys = new Set([...Object.keys(unSaco), ...Object.keys(otroSaco)]);
  const resultsSet = {};
  for (const i of keys) {
    resultsSet[i] = valOr0(unSaco, i) + valOr0(otroSaco, i);
  }
  return resultsSet;
}

/**
 * Intersection of "sacos", retuns an array that uses keys common to both sacos, with the minimum value. Non-compatible objects will work as emmpty sacos; simple arrays will act as sets with as many numeric different elements as elements in the array; thus, [1,2] will have a "0" element that appears 1 and an "1" element that appears 2 times.
 * @param {*} unSaco   - first saco
 * @param {*} otroSaco - second saco
 * @returns {Object} merged "saco"
 */
export function sacoIntersection(unSaco, otroSaco) {
  const resultsSet = {};
  for (const i in unSaco) {
    if (i in otroSaco) {
      const min = Math.min(unSaco[i], otroSaco[i]);
      if (min > 0) {
        resultsSet[i] = min;
      }
    }
  }
  return resultsSet;
}