简体   繁体   English

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

[英]Pick one field from Array of Objects recursively

I am looking for a way using JavaScript / Lodash to retrieve the same hirearchy as the input array of objects but want to retain only selected fields. 我正在寻找一种方法,使用JavaScript / Lodash来检索与输入对象数组相同的例外,但是只想保留选定的字段。

I could also formulate this question as doing a deep copy of an array of objects retaining only certain fields. 我也可以将这个问题表述为对仅保留某些字段的对象数组进行深度复制。

For example, given the following array: 例如,给定以下数组:

[
    {
        "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"
    }
]

The output should be as below. 输出应如下。 This is exactly the same as array of objects above but keeps only id and children's ids. 这与上面的对象数组完全相同,但只保留id和children的id。 The children can be any level deep. 孩子们可以深入任何层次。

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

You can make a function that takes an array and maps it to objects with just the id and children . 您可以创建一个带有数组的函数,并将其映射到只包含idchildren对象的对象。 To set the id, just copy the id, to set the children on the returned object pass the children array back into the function recursively: 要设置id,只需复制id,设置返回对象上的子节点,将children数组以递归方式传递回函数:

 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) 

Here's a non-recursive approach that uses an explicit stack and a set for fast lookup in cases when you have many keys to prune out. 这是一种非递归方法,它使用显式堆栈和快速查找的集合 ,以防有多个密钥要删除。 This is a general solution that should work on any keys you throw at it and doesn't mutate the original array. 这是一个通用的解决方案,应该对您抛出的任何键都有效,并且不会改变原始数组。

 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)); 

and lodash, love lodash, learn lodash ... 和lodash,爱lodash, 学习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']);

OR... instead of _.omit(..) to remove unwanted keys you could use _.pick(...) to specify only wanted keys: 或者...而不是_.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']);

Here's my version which does work recursively. 这是我的版本,它递归地工作。

/**
 * 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;
    }
};

and

/**
 * 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;
    }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM