递归地从对象数组中选择一个字段

时间:2018-08-17 00:25:45

标签: javascript lodash

我正在寻找一种使用JavaScript / Lodash检索与对象输入数组相同的架构的方法,但只想保留选定的字段。

我还可以将这个问题表述为对仅保留某些字段的对象数组进行深层复制。

例如,给定以下数组:

[
    {
        "id": "q1",
        "text": "Q1 text",
        "children": [
            {
                "id": "q11",
                "text": "t",
                "children": [
                    {
                        "id": "q111",
                        "text": "t"
                    },
                    {
                        "id": "q112",
                        "text": "t"
                    }
                ]
            }
        ]
    },
    {
        "id": "q2",
        "text": "e",
        "children": [
            {
                "id": "q22",
                "text": "e"
            }
        ]
    },
    {
        "id": "q3",
        "text": "e"
    }
]

输出应如下所示。这与上面的对象数组完全相同,但仅保留id和孩子的id。孩子们可以是任何水平的人。

[
        {
            "id": "q1",
            "children": [
                {
                    "id": "q11",
                    "children": [
                        {
                            "id": "q111",
                        },
                        {
                            "id": "q112"
                        }
                    ]
                }
            ]
        },
        {
            "id": "q2",
            "children": [
                {
                    "id": "q22",
                }
            ]
        },
        {
            "id": "q3"
        }
]

4 个答案:

答案 0 :(得分:3)

您可以创建一个仅使用idchildren即可将数组映射到对象的函数。要设置ID,只需复制ID,然后在返回的对象上设置子代,即可将子代数组递归地传递回函数:

let arr = [{"id": "q1","text": "Q1 text","children": [{"id": "q11","text": "t","children": [{"id": "q111","text": "t"},{"id": "q112","text": "t"}]}]},{"id": "q2","text": "e","children": [{"id": "q22","text": "e"}]},{"id": "q3","text": "e"}]

const justIDs = (arr) => arr.map(({id, children}) => {
        let ret = {id}
        if(children) ret.children = justIDs(children)
        return ret
    })

let filtered = justIDs(arr)
console.log(filtered)

答案 1 :(得分:1)

这是一种非递归方法,该方法使用显式堆栈和set进行快速查找,以防您有很多键需要修剪。这是一个通用的解决方案,该解决方案可以在您对其施加的任何键上起作用,并且不会突变原始数组。

const data = [
    {
        "id": "q1",
        "text": "Q1 text",
        "children": [
            {
                "id": "q11",
                "text": "t",
                "children": [
                    {
                        "id": "q111",
                        "text": "t"
                    },
                    {
                        "id": "q112",
                        "text": "t"
                    }
                ]
            }
        ]
    },
    {
        "id": "q2",
        "text": "e",
        "children": [
            {
                "id": "q22",
                "text": "e"
            }
        ]
    },
    {
        "id": "q3",
        "text": "e"
    }
];

const removeKeys = (arr, keys) => {
  const keep = new Set(keys);
  const res = [];
  const stack = [[arr, res]];

  while (stack.length) {
    const [curr, cpy] = stack.pop();

    if (Array.isArray(curr)) {
      curr.forEach((e, i) => {
        cpy[i] = {};
        
        for (const k in e) {
          if (keep.has(k)) {
            cpy[i][k] = e[k];
            stack.push([e[k], cpy[i][k]]);
          }
        }
      });
    }
  }

  return res;
};

console.log(JSON.stringify(removeKeys(data, ["id", "children"]), null, 4));

答案 2 :(得分:1)

和lodash,爱lodash,learn lodash ...

function omitKeysDeep(input, keys) {
  if(!_.isArray(keys)) throw new Error('omitKeys expected an array');
  return _.map(input, (elem) => {
    if(elem.children) elem.children = omitKeysDeep(elem.children, keys);
    return _.omit(elem, keys);
  });
}

omitKeysDeep(a, ['text']);

或...代替_.omit(..)删除不需要的密钥,您可以使用_.pick(...)仅指定需要的密钥:

function pickKeysDeep(input, keys) {
  if(!_.isArray(keys)) throw new Error('pickKeys expected an array');
  return _.map(input, (elem) => {
    if(elem.children) elem.children = pickKeysDeep(elem.children, keys);
    return _.pick(elem, keys);
  });
}

pickKeysDeep(a, ['id', 'children']);

答案 3 :(得分:0)

这是我的版本,可以递归工作。

/**
 * Like _.pick() but will also map over arrays implicitly.
 * ie. path 'a.b.c' will transform {a:[{b:{c:1,d:2}}]} => {a:[{b:{c:1}}]}
 *
 * @param {object} o - Object to copy.
 * @param {string[]} paths - List of paths to include.
 * @returns {mixed} - Copied object.
 */
Utils.pickDeep = (o, paths) => {
    if (Array.isArray(o)) {
        return _.map(o, v=>
            Utils.pickDeep(v, paths));
    }
    else if (null != o && 'object' === typeof o) {
        const result = {};
        for (const path of paths) {
            const parts = path.split('.');
            const part  = parts.shift();
            result[part] = o[part];
            if (parts.length < 1) {
                // do not recurse
            }
            else {
                // recurse
                result[part] = Utils.pickDeep(_.get(o, [part]), [parts.join('.')]);
            }
        }
        return result;
    }
    else {
        return o;
    }
};

/**
 * Like _.omit() but will also map over arrays implicitly.
 * ie. path 'a.b.c' will transform {a:[{b:{c:1,d:2}}],e:4} => {a:[{b:{d:2}}],e:4}
 *
 * @param {object} o - Object to copy.
 * @param {string[]} paths - List of paths to exclude.
 * @returns {mixed} - Copied object.
 */
Utils.omitDeep = (o, paths) => {
    if (Array.isArray(o)) {
        return _.map(o, v=>
            Utils.omitDeep(v, paths));
    }
    else if (null != o && 'object' === typeof o) {
        const result = { ...o };
        for (const path of paths) {
            const parts = path.split('.');
            const part  = parts.shift();
            delete result[part];
            if (parts.length < 1) {
                // do not recurse
            }
            else {
                // recurse
                result[part] = Utils.omitDeep(_.get(o, [part]), [parts.join('.')]);
            }
        }
        return result;
    }
    else {
        return o;
    }
};