All files / src recursive-obj.js

100% Statements 71/71
100% Branches 0/0
100% Functions 4/4
100% Lines 71/71

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 721x 1x 1x 3x 3x 3x 8x 3x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 13x 3x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 4x 4x 15x 15x 15x 15x 15x 15x 3x 3x 2x 2x  
export const RecursiveObj = {
  largeRandom() {
    return `ref:${Math.floor(Math.random() * 100_000_000_000)}`;
  },
  flatten(a, references = {}, found = []) {
    if (typeof a !== 'object') { return { obj: a, references, found }; }
    const keys = Object.keys(a);
    const newObj = a instanceof Array ? [] : {};
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i];
      // Already been cataloged
      const kvPair = found.find(({ v }) => v === a[key]);
      if (kvPair) {
        newObj[key] = kvPair.k;
        continue;
      }
      const r = { k: RecursiveObj.largeRandom() };
      const flattened = RecursiveObj.flatten(
        a[key],
        references,
        found.concat({ v: a[key], k: r.k }),
      );
      r.v = flattened.obj;
 
      references = { // eslint-disable-line no-param-reassign
        ...references,
        ...flattened.references,
        [r.k]: r.v,
      };
      found = flattened.found; // eslint-disable-line no-param-reassign
 
      newObj[key] = flattened.obj;
    }
    return { obj: newObj, references, found };
  },
  inflate(a, references, inflated = {}) {
    if (typeof a !== 'object') { return a; }
    const keys = Object.keys(a);
    const newObj = a instanceof Array ? [] : {};
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i];
      const val = a[key];
      // Already been inflated
      if (typeof val === 'string' && inflated[val] != null) {
        newObj[key] = inflated[val];
        continue;
      }
      // Uninflated reference
      if (typeof val === 'string' && references[val] != null) {
        const flat = { ...references[val] };
        inflated[val] = flat; // eslint-disable-line no-param-reassign
        const filled = RecursiveObj.inflate(flat, references, inflated);
        // Move the inflated properties over to the "flat" object
        Object.keys(filled).forEach((t) => { flat[t] = filled[t]; });
        newObj[key] = inflated[val];
        continue;
      }
      // Not a reference
      newObj[key] = RecursiveObj.inflate(val, references, inflated);
    }
    return newObj;
  },
  stringify(a) {
    const { obj, references } = RecursiveObj.flatten(a);
    return JSON.stringify({ obj, references });
  },
  parse(a) {
    const { obj, references } = JSON.parse(a);
    return RecursiveObj.inflate(obj, references);
  },
};