代码之家  ›  专栏  ›  技术社区  ›  caesay

将嵌套对象数组扩展为路径数组的最快方法(lodash)

  •  2
  • caesay  · 技术社区  · 7 年前

    我有一个查询语法,需要应用于json对象,并在json对象中返回一个有效路径数组。

    例如,对于这样的查询:

    People.[].Dependents.[]
    

    以及以下JSON对象:

    {
        "People": [
            {
                "FirstName": "John",
                "LastName": "Doe",
                "Dependents": [
                    {
                        "Name": "John First Dep"
                    },
                    {
                        "Name": "John Second Dep"
                    }
                ]
            },
            {
                "FirstName": "Jane",
                "LastName": "Smith",
                "Dependents": [
                    {
                        "Name": "Jane First Dep"
                    }
                ]
            }
        ]
    }
    

    结果将是:

    [
        "People.0.Dependents.0",
        "People.0.Dependents.1",
        "People.1.Dependents.0",
    ]
    

    编辑:当前代码:

    function expandQuery(data, path) {
        const parts = path.split("[]").map(s => _.trim(s, "."));
        const [outer, ...right] = parts;
    
        const inner = _.join(right, ".[].");
    
        let groupData = _.get(data, outer, []);
        if (!_.isArray(groupData)) {
            groupData = [groupData];
        }
        const groupLength = groupData.length;
    
        let items = [];
        for (let ind = 0; ind < groupLength; ind++) {
            items.push(outer + "." + ind.toString() + "." + inner);
        }
    
        const result = [];
    
        for (let ind = 0; ind < items.length; ind++) {
            const item = items[ind];
            if (item.includes("[]")) {
                result.push(...expandQuery(data, item));
            } else {
                result.push(_.trim(item, "."));
            }
        }
        return result;
    }
    

    我特别想把它缩短。

    4 回复  |  直到 7 年前
        1
  •  2
  •   alexloehr    7 年前

    这是您想要的,但并不比您的解决方案简单/简短。

    function getPaths (collection, query) {
        let tokens = query.split(".")
    
        function walkPath (collection, [currentToken, ...restTokens], paths) {
            if (!currentToken) { // no more tokens to follow
                return paths.join(".")
            }
            if (currentToken === "[]") { // iterate array
                const elemPaths = _.range(collection.length)
                return elemPaths.map(elemPath => walkPath(collection[elemPath], restTokens, [...paths, elemPath]))
            }
            else {
                return walkPath(collection[currentToken], restTokens, [...paths, currentToken])
            }
        }
    
        return _.flattenDeep(walkPath(collection, tokens, []))
    }
    


        2
  •  0
  •   lumio    7 年前

    要展平深度嵌套的数组,可以使用 _.flattenDeep 但是,如果您不介意离开lodash,您可以执行以下操作:

    function getNextToken(path) {
      let token = path.trim();
      let rest = '';
    
      const separatorPos = path.indexOf('.');
      if (separatorPos > -1) {
        token = path.substr(0, separatorPos).trim();
        rest = path.substr(separatorPos + 1).trim();
      }
    
      return { token, rest };
    }
    
    
    const expandQuery = (data, path) => {
      const expand = (data, path, found = []) => {
        if (data === undefined) {
          return [];
        }
    
        const { token, rest } = getNextToken(path);
        switch (token) {
          // Got to the end of path
          case '':
            return [found.join('.')];
    
          // Got an array
          case '[]':
            if (data.constructor !== Array) {
              return [];
            }
    
            const foundPaths = [];
            let i;
            for (i = 0; i < data.length; ++i) {
              // Flatten out foundPaths
              foundPaths.push.apply(
                foundPaths,
                expand(data[i], rest, [...found, i])
              );
            }
            return foundPaths;
    
          // Got a property name
          default:
            return expand(data[token], rest, [...found, token]);
        }
      };
      
      return expand(data, path);
    };
    
    const query = 'People.[].Dependents.[]';
    const input = {
      "People": [{
          "FirstName": "John",
          "LastName": "Doe",
          "Dependents": [{
              "Name": "John First Dep"
            },
            {
              "Name": "John Second Dep"
            }
          ]
        },
        {
          "FirstName": "Jane",
          "LastName": "Smith",
          "Dependents": [{
            "Name": "Jane First Dep"
          }]
        }
      ]
    };
    console.log(expandQuery(input, query));

    可能不是最短的,但它会检查数据类型。阿尔索 null data === null .

        3
  •  0
  •   Maksym    7 年前

    另一个例子:)

    var _ = require('lodash');
    var test = {
        "People": [
            {
                "FirstName": "John",
                "LastName": "Doe",
                "Dependents": [
                    {
                        "Name": "John First Dep"
                    },
                    {
                        "Name": "John Second Dep"
                    }
                ]
            },
            {
                "FirstName": "Jane",
                "LastName": "Smith",
                "Dependents": [
                    {
                        "Name": "Jane First Dep"
                    }
                ]
            }
        ]
    }
    
    function mapper(thing, prefix, paths) {
        if (_.isObject(thing)) {
            _.forEach(thing, function(value, key) {mapper(value, prefix+key+'.', paths);});
        } else if (_.isArray(thing)) {
            for (var i = 0; i < thing.length; i++) mapper(value, prefix+i+'.', paths);
        } else {
            paths.push(prefix.replace(/\.$/, ''));
        }
    }
    
    var query = 'People.[].Dependents.[]';
    var paths = [];
    var results;
    
    query = new RegExp(query.replace(/\[\]/g,'\\d'));
    mapper(test, '', paths); // Collect all paths
    results = _.filter(paths, function(v) {return query.test(v);}); // Apply query
    console.log(results);
    
        4
  •  0
  •   HMR    7 年前

    我也会尝试一下,注意当你使用 _.get(object,'path.with.point') 你的对象键名上有点你的代码会被破坏,我更喜欢用 _.get(object,['path','with','point'])

    const data = {"People":[{"FirstName":"John","LastName":"Doe","Dependents":[{"Name":"John First Dep","a":[1,2]},{"Name":"John Second Dep","a":[3]}]},{"FirstName":"Jane","LastName":"Smith","Dependents":[{"Name":"Jane First Dep","a":[1]}]}]};
    
    const flatten = arr =>
      arr.reduce((result, item) => result.concat(item))
    const ARRAY = {}
    const getPath = (obj, path) => {
      const recur = (result, path, item, index) => {
        if (path.length === index) {
          return result.concat(path)
        }
        if (item === undefined) {
          throw new Error('Wrong path')
        }
        if (path[index] === ARRAY) {
          const start = path.slice(0, index)
          const end = path.slice(index + 1, path.length)
          return item.map((_, i) =>
            recur(result, start.concat(i).concat(end), item, index)
          )
        }
        return recur(result, path, item[path[index]], index + 1)
      }
      const result = recur([], path, obj, 0)
      const levels = path.filter(item => item === ARRAY).length - 1
      return levels > 0
        ? [...new Array(levels)].reduce(flatten, result)
        : result
    }
    
    console.log(
      getPath(data, ['People', ARRAY, 'Dependents', ARRAY, 'a', ARRAY])
    )