Source: object/getFlattenedArray.js

const getFlattened = require('./getFlattened');
const setFlattened = require('./setFlattened');
const { UNIVERSAL_UNDEFINED_VALUE_INDICATOR } = require('./private');

/**
 * @param {string[]} paths
 * @param {object} obj
 * @return {string[]|null}
 * @ignore
 */
function handleSpread (paths, obj) {
  const firstPathSplitted = paths[0].split('.');
  const spreadPosition = firstPathSplitted.indexOf('*');
  if (spreadPosition === -1) {
    return null;
  }

  const path = firstPathSplitted.slice(0, spreadPosition).join('.');
  const arraySize = getFlattened(path, obj, []).length;

  const resp = [];
  paths.forEach(localPath => {
    for (let i = 0; i < arraySize; i++) {
      resp.push(localPath.replace('*', `[${i}]`));
    }
  });

  return resp;
}

/**
 * validate if paths are valid
 * @param {any} paths
 */
function validatePaths (paths) {
  if (!Array.isArray(paths)) {
    throw Error('paths must be array');
  }

  if (paths.some(path => typeof path !== 'string')) {
    throw Error('paths must be array of strings');
  }
}

/**
 * Extracts a fragment of an object, including nested properties and array support.
 * Note: it doen´t work when the keys have '.'.
 *
 * @param {string[]} paths to extract a value from every object in a collection use '*'. eg: ['a.*.name']
 * @param {object} obj
 * @return {object}
 * @memberof object
 *
 * @example
 * const input = {
 *   name: "Xpto",
 *   age: 77,
 *   address: {
 *     street: "first",
 *     number: 88,
 *   },
 *   phones: [
 *     { number: 1111111, code: 11 },
 *     { number: 2222222, code: 22 },
 *   ],
 * };
 *
 * const res = getFlattenedArray(["name", "address.number", "phones.*.code"], input)
 *
 * // result
 * {
 *   name: "Xpto",
 *   address: { number: 88 },
 *   phones: [
 *     { code: 11 },
 *     { code: 22 },
 *   ],
 * };
 */
module.exports = function getFlattenedArray (paths, obj) {
  validatePaths(paths);

  let response = {};
  paths.forEach(path => {
    let pathsToGet = [];

    if (path.indexOf('*') > -1) {
      let resp;
      let tempPaths = [path];
      do {
        resp = handleSpread(tempPaths, obj);
        if (resp !== null) {
          tempPaths = resp;
        }
      } while (resp !== null && resp.length > 0);
      pathsToGet = tempPaths;

      // contains array
    } else {
      pathsToGet.push(path);
    }

    pathsToGet.forEach(currentPath => {
      const value = getFlattened(currentPath, obj, UNIVERSAL_UNDEFINED_VALUE_INDICATOR);
      if (value !== UNIVERSAL_UNDEFINED_VALUE_INDICATOR) {
        response = setFlattened(currentPath, value, response);
      }
    });
  });

  return response;
};