Find possible paths on a map: recursive function in javascript

doc = {
  'a': {
    'b': {
      'c': 'hello'
    },
    'd': {
      'c': 'sup',
      'e': {
        'f': 'blah blah blah'
      }
    }
  }
}

function get(json, path) {
  var str = path.split('.');
  var temp = json;
  var arr = [];
  var keystr = "";
  for (var i = 0; i < str.length; i++) {
    if (str[i] != "*") {

      keystr += str[i] + ".";

      if (temp[str[i]] === undefined)
        break;
      else {
        temp = temp[str[i]];
        if (i == str.length - 1) {
          var nObj = {};
          nObjKey = keystr.substr(0, keystr.length - 1);
          nObj[nObjKey] = temp
            // console.log("Obj check" + JSON.stringify(nObj) + keystr)
          arr.push(nObj);
        }
      }
    } else {
      for (var key in temp) {
        var concat = key + "."
        for (var j = i + 1; j < str.length; j++)
          concat += str[j] + ".";
        if (temp[key] !== undefined && temp[key] instanceof Object) {

          var m = keystr + concat.substr(0, concat.length - 1);
          var obj = (get(temp, concat.substr(0, concat.length - 1)));

          if (obj != "") {
            // console.log("existing arr "+JSON.stringify(arr))
            obj[m] = (obj[0])[concat.substr(0, concat.length - 1)]
              //  console.log("hello "+JSON.stringify(obj) + " end hello")
            arr.push(obj);
          }
        } else if (temp[key] !== undefined && i == str.length - 1) {
          // arr.push(temp);
        }
      }
    }
  }
  return arr;
}

var result = (get(doc, 'a.*.e'))
console.log(result)
Run codeHide result

There 'a.*.e'must be an output for input {'a.d.e': {'f': 'blah blah blah'}}}. But I get the whole replacement for the wild card, as well as in the array. I am sure that something is wrong, but I am not able to detect it. Help will be appreciated.

+3
source share
3 answers

You can change the structure of the operation a little using the recursive approach and exit early exit, often a paradigm with checking individual parts with exit options, for example

  • length, the result of the part is found,
  • false or not types of objects,
  • part at index is a star, then iterate over all keys from an object or
  • the part in the index is the key and then calls the function again.

, , .

function get(object, path) {

    function iter(o, p, i) {
        if (i === parts.length) {
            result[p.join('.')] = o;
            return;
        }
        if (!o || typeof o !== 'object') {
            return;
        }
        if (parts[i] === '*') {
            Object.keys(o).forEach(function (k) {
                iter(o[k], p.concat(k), i + 1);
            });
            return;
        }
        if (parts[i] in o) {
            iter(o[parts[i]], p.concat(parts[i]), i + 1);
        }
    }

    var result = {},
        parts = path.split('.');

    iter(object, [], 0);
    return result;
}

var doc = { a: { b: { c: 'hello' }, d: { c: 'sup', e: { f: 'blah blah blah' } } } };

console.log(get(doc, 'a.*.e'));
console.log(get(doc, 'a.*.c'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Hide result

* .

function get(object, path) {

    function iter(o, p, i) {
        if (i === parts.length) {
            result[p.join('.')] = o;
            return;
        }
        if (!o || typeof o !== 'object') {
            return;
        }
        if (parts[i] === '*') {
            Object.keys(o).forEach(function (k) {
                iter(o[k], p.concat(k), i);
                iter(o[k], p.concat(k), i + 1);
            });
            return;
        }
        if (parts[i] in o) {
            iter(o[parts[i]], p.concat(parts[i]), i + 1);
        }
    }

    var result = {},
        parts = path.split('.');

    iter(object, [], 0);
    return result;
}

var doc = { a: { b: { c: 'hello' }, d: { c: 'sup', e: { f: 'blah blah blah' } } } };

console.log(get(doc, 'a.*.e'));
console.log(get(doc, 'a.*.c'));
console.log(get(doc, 'a.*.f'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Hide result
+3

, {'a.d.e': {'f': 'blah blah blah'}}} , , arr .

nObj , .

-, , , . , != "", .

- (. ), , , , , :

function get(json, path) {
  var str = path.split('.');
  var temp = json;
  var arr = [];
  var keystr = "";
  // *** Define here the object to return
  var nObj = {};
          
  for (var i = 0; i < str.length; i++) {
    if (str[i] != "*") {
      keystr += str[i] + ".";
      if (temp[str[i]] === undefined)
        break;
      else {
        temp = temp[str[i]];
        if (i == str.length - 1) {
          // *** Move this to start of the function
          //var nObj = {}; 
          nObjKey = keystr.substr(0, keystr.length - 1);
          nObj[nObjKey] = temp
        }
      }
    } else {
      for (var key in temp) {
        var concat = key + "."
        for (var j = i + 1; j < str.length; j++)
          concat += str[j] + ".";
        if (temp[key] !== undefined && temp[key] instanceof Object) {

          var m = keystr + concat.substr(0, concat.length - 1);
          var obj = get(temp, concat.substr(0, concat.length - 1));
          // *** Return value is object with path(s) as keys
          // *** Don't compare array with string
          //if (arr != "") { 
            // *** Iterate over the returned object properties, and prefix them 
            for (var deepKey in obj) {
                nObj[keystr + deepKey] = obj[deepKey];
            }
            //*** No need for array; we already have the object properties
            //arr.push(obj);
          //}
        // *** No need for array
        //} else if (temp[key] !== undefined && i == str.length - 1) {
          // arr.push(temp);
        }
      }
    }
  }
  // *** Return object 
  return nObj;
}

var doc = {
  'a': {
    'b': {
      'c': 'hello'
    },
    'd': {
      'c': 'sup',
      'e': {
        'f': 'blah blah blah'
      },
    },
    'g': {
      'e': {
        'also': 1
      }
    }
  }
}

var result = (get(doc, 'a.*.e'));
console.log(result);
Hide result

json, : JSON - , JavaScript - , JSON.

ES6

reduce , ES6:

function get(obj, path) {
    if (typeof path === 'string') path = path.split('.');
    return !path.length ? { '': obj } // Match
        : obj !== Object(obj) ? {} // No match
        : (path[0] === '*' ? Object.keys(obj) : [path[0]]) // Candidates
            .reduce( (acc, key) => {
                const match = get(obj[key], path.slice(1)); // Recurse
                return Object.assign(acc, ...Object.keys(match).map( dotKey => 
                    ({ [key + (dotKey ? '.'  + dotKey : '')]: match[dotKey] })
                ));
            }, {});
}

const doc = {
  'a': {
    'b': {
      'c': 'hello'
    },
    'd': {
      'c': 'sup',
      'e': {
        'f': 'blah blah blah'
      },
    },
    'g': {
      'e': {
        'also': 1
      }
    }
  }
};

const result = get(doc, 'a.*.e');
console.log(result);
Hide result
+1

, , , 0, 1 . , List . , .

find, get, , get

, ; ^ _ ^

const List = xs =>
  ({
    value:
      xs,
    bind: f =>
      List (xs.reduce ((acc, x) =>
        acc.concat (f (x) .value), []))
  })

const find = (path, [key, ...keys], data) =>
  {
    if (key === undefined)
      return List([{ [path.join('.')]: data }])
    else if (key === '*')
      return List (Object.keys (data)) .bind (k =>
        find ([...path, k], keys, data[k]))
    else if (data[key] === undefined)
      return List ([])
    else
      return find ([...path, key], keys, data[key])
  }

const get = (path, doc) =>
  find ([], path.split ('.'), doc) .value

const doc =
  {a: {b: {c: 'hello'},d: {c: 'sup',e: {f: 'blah blah blah'}}}}
  
console.log (get ('a.b.c', doc))
// [ { 'a.b.c': 'hello' } ]
   
console.log (get ('a.*.c', doc))
// [ { 'a.b.c': 'hello' }, { 'a.d.c': 'sup' } ]

console.log (get ('a.*', doc))
// [ { 'a.b': { c: 'hello' } },
//   { 'a.d': { c: 'sup', e: { f: 'blah blah blah' } } } ]

console.log (get ('*.b', doc))
// [ { 'a.b': { c: 'hello' } } ]
Hide result

List . , , . '*' -key, flatmap

const find = (path, [key, ...keys], data) =>
  {
    if (key === undefined)
      return [{ [path.join ('.')]: data }]
    else if (key === '*')
      return Object.keys (data) .reduce ((acc, k) =>
        acc.concat (find ([...path, k], keys, data[k])), [])
    else if (data[key] === undefined)
      return []
    else
      return find ([...path, key], keys, data[key])
  }

const get = (path, doc) =>
  find([], path.split('.'), doc)

const doc =
  {a: {b: {c: 'hello'},d: {c: 'sup',e: {f: 'blah blah blah'}}}}

console.log (get ('a.b.c', doc))
// [ { 'a.b.c': 'hello' } ]

console.log (get ('a.*.c', doc))
// [ { 'a.b.c': 'hello' }, { 'a.d.c': 'sup' } ]

console.log (get ('a.*', doc))
// [ { 'a.b': { c: 'hello' } },
//   { 'a.d': { c: 'sup', e: { f: 'blah blah blah' } } } ]

console.log (get ('*.b', doc))
// [ { 'a.b': { c: 'hello' } } ]
Hide result

Monad List, find . , . List , .


. , /. - , , - .

, Object . ?

// get ('a.*', doc) returns
let result =
  [ { 'a.b': { c: 'hello' } },
    { 'a.d': { c: 'sup', e: { f: 'blah blah blah' } } } ]

result.forEach (match =>
  Object.keys (match) .forEach (path =>
    console.log ('path:', path, 'value:', match[path])))
    
// path: a.b value: { c: 'hello' }
// path: a.d value: { c: 'sup', e: { f: 'blah blah blah' } }
Hide result

, [<key>, <value>] {<key>: <value>}? . , , : Array#entries Map#entries()

// get ('a.*', doc) returns proposed
let result =
  [ [ 'a.b', { c: 'hello' } ],
    [ 'a.d', { c: 'sup', e: { f: 'blah blah blah' } } ] ]

for (let [path, value] of result)
  console.log ('path:', path, 'value:', value)

// path: a.b value: { c: 'hello' }
// path: a.d value: { c: 'sup', e: { f: 'blah blah blah' } }
Hide result

, , ( )

// List monad version
const find = (path, [key, ...keys], data) => {
  if (key === undefined)
    return List ([[path.join ('.'), data]])
  ...
}

// native arrays version
const find = (path, [key, ...keys], data) => {
  if (key === undefined)
    return [[path.join ('.'), data]]
  ...
}
+1

Source: https://habr.com/ru/post/1687232/


All Articles