List all possible paths using lodash

前端 未结 6 1058
终归单人心
终归单人心 2021-01-18 00:08

I would like to list all paths of object that lead to leafs

Example:

var obj = {
 a:\"1\",
 b:{
  foo:\"2\",
  bar:3
 },
 c:[0,1]
}

相关标签:
6条回答
  • 2021-01-18 00:11

    Doesn't use lodash, but here it is with recursion:

    var getLeaves = function(tree) {
        var leaves = [];
        var walk = function(obj,path){
            path = path || "";
            for(var n in obj){
                if (obj.hasOwnProperty(n)) {
                    if(typeof obj[n] === "object" || obj[n] instanceof Array) {
                        walk(obj[n],path + "." + n);
                    } else {
                        leaves.push(path + "." + n);
                    }
                }
            }
        }
        walk(tree,"tree");
        return leaves;
    }
    
    0 讨论(0)
  • 2021-01-18 00:14

    Based on Nick answer, here is a TS / ES6 imports version of the same code

    import {isArray,flatMap,map,keys,isPlainObject,concat} from "lodash";
    
    // See https://stackoverflow.com/a/36490174/82609
    export function paths(obj: any, parentKey?: string): string[] {
      var result: string[];
      if (isArray(obj)) {
        var idx = 0;
        result = flatMap(obj, function(obj: any) {
          return paths(obj, (parentKey || '') + '[' + idx++ + ']');
        });
      } else if (isPlainObject(obj)) {
        result = flatMap(keys(obj), function(key) {
          return map(paths(obj[key], key), function(subkey) {
            return (parentKey ? parentKey + '.' : '') + subkey;
          });
        });
      } else {
        result = [];
      }
      return concat(result, parentKey || []);
    }
    
    0 讨论(0)
  • 2021-01-18 00:21

    Here is a solution that uses lodash in as many ways as I can think of:

    function paths(obj, parentKey) {
      var result;
      if (_.isArray(obj)) {
        var idx = 0;
        result = _.flatMap(obj, function (obj) {
          return paths(obj, (parentKey || '') + '[' + idx++ + ']');
        });
      }
      else if (_.isPlainObject(obj)) {
        result = _.flatMap(_.keys(obj), function (key) {
          return _.map(paths(obj[key], key), function (subkey) {
            return (parentKey ? parentKey + '.' : '') + subkey;
          });
        });
      }
      else {
        result = [];
      }
      return _.concat(result, parentKey || []);
    }
    

    Edit: If you truly want just the leaves, just return result in the last line.

    0 讨论(0)
  • 2021-01-18 00:22

    Here is my function. It generates all possible paths with dot notation, assuming there are no property names containing spaces

    function getAllPathes(dataObj) {
        const reducer = (aggregator, val, key) => {
            let paths = [key];
            if(_.isObject(val)) {
                paths = _.reduce(val, reducer, []);
                paths = _.map(paths, path => key + '.' + path);
            }
            aggregator.push(...paths);
            return aggregator;
        };
        const arrayIndexRegEx = /\.(\d+)/gi;
        let paths = _.reduce(dataObj, reducer, []);
        paths = _.map(paths, path => path.replace(arrayIndexRegEx, '[$1]'));
    
        return paths;
    }
    
    0 讨论(0)
  • 2021-01-18 00:26

    Feeding that object through this function should do it I think.

    recursePaths: function(obj){
    var result = [];
    //get keys for both arrays and objects
    var keys = _.map(obj, function(value, index, collection){
        return index;
    });
    
    
    //Iterate over keys
    for (var key in keys) {
        //Get paths for sub objects
        if (typeof obj[key] === 'object'){
            var paths = allPaths(obj[key]);
            for (var path in paths){
                result.push(key + "." + path);
            }
        } else {
            result.push(key);
        }
    }
    
    return result;
    }
    
    0 讨论(0)
  • 2021-01-18 00:35

    Here's my solution. I only did it because I felt the other solutions used too much logic. Mine does not use lodash since I don't think it would add any value. It also doesn't make array keys look like [0].

    const getAllPaths = (() => {
        function iterate(path,current,[key,value]){
            const currentPath = [...path,key];
            if(typeof value === 'object' && value != null){
                return [
                    ...current,
                    ...iterateObject(value,currentPath) 
                ];
            }
            else {
                return [
                    ...current,
                    currentPath.join('.')
                ];
            }
        }
    
        function iterateObject(obj,path = []){
            return Object.entries(obj).reduce(
                iterate.bind(null,path),
                []
            );
        }
    
        return iterateObject;
    })();
    

    If you need one where the keys are indexed using [] then use this:

        const getAllPaths = (() => {
            function iterate(path,isArray,current,[key,value]){
                const currentPath = [...path];
                if(isArray){
                    currentPath.push(`${currentPath.pop()}[${key}]`);
                }
                else {
                    currentPath.push(key);
                }
                if(typeof value === 'object' && value != null){
                    return [
                        ...current,
                        ...iterateObject(value,currentPath) 
                    ];
                }
                else {
                    return [
                        ...current,
                        currentPath.join('.')
                    ];
                }
            }
    
            function iterateObject(obj,path = []){
                return Object.entries(obj).reduce(
                    iterate.bind(null,path,Array.isArray(obj)),
                    []
                );
            }
    
            return iterateObject;
        })();
    
    0 讨论(0)
提交回复
热议问题