all files / src/ write.js

98.33% Statements 59/60
89.47% Branches 51/57
100% Functions 5/5
97.44% Lines 38/39
6 statements, 1 function, 3 branches Ignored     
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        248× 248× 248× 21×   227× 227× 227×   35× 35×         192×   39×   38× 38× 38× 38× 38×   27×   15× 15× 15× 15×         12× 12× 12×           11×               16×                 153×              
/**
 * Writes any potentially deep value from an object using dot and array syntax,
 * and returns a new copy of the object.
 */
const write = (path, value, object) => {
  const dotIndex = path.indexOf('.');
  if (dotIndex === 0) {
    return write(path.substring(1), value, object);
  }
  const openIndex = path.indexOf('[');
  const closeIndex = path.indexOf(']');
  if (dotIndex >= 0 && (openIndex < 0 || dotIndex < openIndex)) {
    // is dot notation
    const key = path.substring(0, dotIndex);
    return {
      ...object,
      [key]: write(path.substring(dotIndex + 1), value, object[key] || {})
    };
  }
  if (openIndex >= 0 && (dotIndex < 0 || openIndex < dotIndex)) E{
    // is array notation
    if (closeIndex < 0) {
      throw new Error('found [ but no ]');
    }
    const key = path.substring(0, openIndex);
    const index = path.substring(openIndex + 1, closeIndex);
    const array = object[key] || [];
    const rest = path.substring(closeIndex + 1);
    if (index) {
      // indexed array
      if (rest.length) {
        // need to keep recursing
        const dest = array[index] || {};
        const arrayCopy = [...array];
        arrayCopy[index] = write(rest, value, dest);
        return {
          ...(object || {}),
          [key]: arrayCopy
        };
      }
      const copy = [...array];
      copy[index] = typeof value === 'function' ? value(copy[index]) : value;
      return {
        ...(object || {}),
        [key]: copy
      };
    }
    // indexless array
    if (rest.length) {
      // need to keep recursing
      if ((!array || !array.length) && typeof value === 'function') {
        return object;  // don't even set a value under [key]
      }
      const arrayCopy = array.map(dest => write(rest, value, dest));
      return {
        ...(object || {}),
        [key]: arrayCopy
      };
    }
    let result;
    if (Array.isArray(value)) {
      result = value;
    } else if (object[key]) {
      result = array.map(dest => typeof value === 'function' ? value(dest) : value);
    } else Eif (typeof value === 'function') {
      return object;  // don't even set a value under [key]
    } else {
      result = value;
    }
    return {
      ...(object || {}),
      [key]: result
    };
  }
  return {
    ...object,
    [path]: typeof value === 'function' ? value(object[path]) : value
  };
};
 
export default write;