crazy4groovy
8/21/2015 - 3:15 PM

see https://github.com/lodash/lodash/issues/1397

var x1 = {a:[{b:[{x:2},{x:3}]},{b:[{x:1},{x:9}]}]};
var x2 = {a:[{b:[{y:2},{x:3}]},{b:[{x:1},{y:9}]}]};
var x3 = [{b:[{x:2},{x:3}]},{b:[{x:1},{y:9}]}];
var x4 = {a:[{b:{c:10}},{b:{c:20}}]};

_getSplat(x1, 'a*.b') === [[{x:2},{x:3}],[{x:1},{x:9}]];
_getSplat(x1, 'a*.b*.x') === [[2, 3], [1, 9]];
_getSplat(x1, 'a*.b*.x*.z') === [[[undefined], [undefined]], [[undefined], [undefined]]];

_getSplat(x2, 'a*.b*.x') === [[undefined, 3], [1, undefined]];

_getSplat(x3, '*.b*.x') === [[2, 3], [1, undefined]];
_getSplat(x3, '*.b*.y'); === [[undefined, undefined], [undefined, 9]];
_getSplat(x3, '*.b*.y*.z*.q', true) === [[[true], [true]], [[true], [true]]];

_getSplat(x4, 'a*.b.c') === [10, 20];
// eg. path = 'a.b*.x*.z'

function _getSplat(obj, path, _default) {
  if (!_.isString(path)) return obj;

  var paths = path.split('*.');
  //console.log('get:', obj, paths);

  if (paths[0]) {
    obj = _.get(obj, paths[0], _default);
    //console.log('obj:', obj);
  }

  if (paths[1]) {
    if (!_.isObject(obj)) {
      return [_default];
    }
    //console.log('paths[1]:', paths[1]);
    var _obj = obj;
    obj = [];
    for (var prop in _obj) {
      //console.log('val:', _obj[prop], paths.slice(1).join('*.'));
      var recurseResult = _getSplat(_obj[prop], paths.slice(1).join('*.'), _default);
      //console.log('recurse:', recurseResult);
      obj.push(recurseResult);
    }
  }

  return obj;
}
// eg. path = 'a.b[].x[0].z'

function _getPath(obj, path, _default) {
  if (!_.isString(path)) return obj;
  
  var paths = path.match(/(.*?)(?:\[(-?\d*)\]\.?)/);
  //eg. paths = ["a.b[0]", "a.b", "0"]
  //console.log('get:', obj, paths);
  
  if (!Array.isArray(paths)) {
    //console.log('not-parsable:', path);
    return _.get(obj, path, _default);
  }
  
  if (paths[1]) {
    obj = _.get(obj, paths[1], _default);
    //console.log('obj:', obj);
  }
  
  path = path.slice(paths[0].length);
  if (path) {
    // _getPath the rest of the path...
    if (!_.isObject(obj)) {
      return [_default];
    }
    // recursively!
    //console.log('path:', path);
    var _obj = obj;
    obj = [];
    for (var prop in _obj) {
      if (!_obj.hasOwnProperty(prop)) continue;
      //console.log('val:', _obj[prop], path);
      var recurseResult = _getPath(_obj[prop], path, _default);
      //console.log('recurse:', recurseResult);
      obj.push(recurseResult);
    }
  }
  
  if (paths[2]) {
    var idx = +paths[2] >= 0 ? +paths[2] : obj.length + +paths[2];
    return obj[idx];
  }
  
  return obj;
}