简体   繁体   English

在深层嵌套 object 中按特定键查找所有值

[英]Find all values by specific key in a deep nested object

How would I find all values by specific key in a deep nested object?如何在深度嵌套的 object 中按特定键查找所有值?

For example, if I have an object like this:例如,如果我有这样的 object:

const myObj = {
  id: 1,
  children: [
    {
      id: 2,
      children: [
        {
          id: 3
        }
      ]
    },
    {
      id: 4,
      children: [
        {
          id: 5,
          children: [
            {
              id: 6,
              children: [
                {
                  id: 7,
                }
              ]
            }
          ]
        }
      ]
    },
  ]
}

How would I get an array of all values throughout all nests of this obj by the key of id .我如何通过id的键在这个 obj 的所有嵌套中获取所有值的数组。

Note: children is a consistent name, and id 's won't exist outside of a children object.注意: children是一个一致的名称,并且id不会存在于children object 之外。

So from the obj, I would like to produce an array like this:所以从 obj 中,我想生成一个这样的数组:

const idArray = [1, 2, 3, 4, 5, 6, 7]

This is a bit late but for anyone else finding this, here is a clean, generic recursive function:这有点晚了,但对于其他发现此问题的人,这是一个干净的通用递归函数:

function findAllByKey(obj, keyToFind) {
  return Object.entries(obj)
    .reduce((acc, [key, value]) => (key === keyToFind)
      ? acc.concat(value)
      : (typeof value === 'object')
      ? acc.concat(findAllByKey(value, keyToFind))
      : acc
    , [])
}

// USAGE
findAllByKey(myObj, 'id')

You could make a recursive function like this:你可以做一个这样的递归函数:

idArray = []

function func(obj) {
  idArray.push(obj.id)
  if (!obj.children) {
    return
  }

  obj.children.forEach(child => func(child))
}

Snippet for your sample:您的示例的代码段:

 const myObj = { id: 1, children: [{ id: 2, children: [{ id: 3 }] }, { id: 4, children: [{ id: 5, children: [{ id: 6, children: [{ id: 7, }] }] }] }, ] } idArray = [] function func(obj) { idArray.push(obj.id) if (!obj.children) { return } obj.children.forEach(child => func(child)) } func(myObj) console.log(idArray)

You can make a generic recursive function that works with any property and any object.您可以创建一个适用于任何属性和任何对象的通用递归函数。

This uses Object.entries() , Object.keys() , Array.reduce() , Array.isArray() , Array.map() and Array.flat() .这使用Object.entries()Object.keys()Array.reduce()Array.isArray()Array.map()Array.flat()

The stopping condition is when the object passed in is empty:停止条件是传入的对象为空时:

 const myObj = { id: 1, anyProp: [{ id: 2, thing: { a: 1, id: 10 }, children: [{ id: 3 }] }, { id: 4, children: [{ id: 5, children: [{ id: 6, children: [{ id: 7 }] }] }] }] }; const getValues = prop => obj => { if (!Object.keys(obj).length) { return []; } return Object.entries(obj).reduce((acc, [key, val]) => { if (key === prop) { acc.push(val); } else { acc.push(Array.isArray(val) ? val.map(getIds).flat() : getIds(val)); } return acc.flat(); }, []); } const getIds = getValues('id'); console.log(getIds(myObj));

Note: children is a consistent name, and id 's wont exist outside of a children object.注意: children是一个一致的名称,并且id不会存在于children对象之外。

So from the obj, I would like to produce an array like this:所以从 obj 中,我想生成一个这样的数组:

 const idArray = [1, 2, 3, 4, 5, 6, 7]

Given that the question does not contain any restrictions on how the output is derived from the input and that the input is consistent, where the value of property "id" is a digit and id property is defined only within "children" property, save for case of the first "id" in the object, the input JavaScript plain object can be converted to a JSON string using JSON.stringify() , RegExp /"id":\\d+/g matches the "id" property and one or more digit characters following the property name, which is then mapped to .match() the digit portion of the previous match using Regexp \\d+ and convert the array value to a JavaScript number using addition operator +鉴于该问题不包含对如何从输入导出输出以及输入一致的任何限制,其中属性"id"的值是一个数字,而id属性仅在"children"属性中定义,除了如果是对象中的第一个"id" ,可以使用JSON.stringify()将输入的 JavaScript 普通对象转换为JSON字符串, RegExp /"id":\\d+/g匹配"id"属性和一个或多个属性名称后面的数字字符,然后使用Regexp \\d+将其映射到.match()前一个匹配的数字部分,并使用加法运算符+将数组值转换为 JavaScript 数字

 const myObject = {"id":1,"children":[{"id":2,"children":[{"id":3}]},{"id":4,"children":[{"id":5,"children":[{"id":6,"children":[{"id":7}]}]}]}]}; let res = JSON.stringify(myObject).match(/"id":\\d+/g).map(m => +m.match(/\\d+/)); console.log(res);

JSON.stringify() replacer function can alternatively be used to .push() the value of every "id" property name within the object to an array JSON.stringify() replacer功能可以替代地用于.push()每值"id"对象物的内部属性名称的数组

 const myObject = {"id":1,"children":[{"id":2,"children":[{"id":3}]},{"id":4,"children":[{"id":5,"children":[{"id":6,"children":[{"id":7}]}]}]}]}; const getPropValues = (o, prop) => (res => (JSON.stringify(o, (key, value) => (key === prop && res.push(value), value)), res))([]); let res = getPropValues(myObject, "id"); console.log(res);

Since the property values of the input to be matched are digits, all the JavaScript object can be converted to a string and RegExp \\D can be used to replace all characters that are not digits, spread resulting string to array, and .map() digits to JavaScript numbers由于要匹配的输入的属性值为数字,因此可以将所有 JavaScript 对象转换为字符串,并且可以使用RegExp \\D替换所有非数字字符,将结果字符串扩展到数组,以及.map()数字到 JavaScript 数字

let res = [...JSON.stringify(myObj).replace(/\D/g,"")].map(Number)

Using recursion.使用递归。

 const myObj = { id: 1, children: [ { id: 2, children: [ { id: 3 } ] }, { id: 4, children: [ { id: 5, children: [ { id: 6, children: [ { id: 7, } ] } ] } ] }, ]}, loop = (array, key, obj) => { if (!obj.children) return; obj.children.forEach(c => { if (c[key]) array.push(c[key]); // is not present, skip! loop(array, key, c); }); }, arr = myObj["id"] ? [myObj["id"]] : []; loop(arr, "id", myObj); console.log(arr);
 .as-console-wrapper { max-height: 100% !important; top: 0; }

You can make a recursive function with Object.entries like so:您可以使用Object.entries制作递归函数,如下所示:

 const myObj = { id: 1, children: [{ id: 2, children: [{ id: 3 }] }, { id: 4, children: [{ id: 5, children: [{ id: 6, children: [{ id: 7, }] }] }] }, ] }; function findIds(obj) { const entries = Object.entries(obj); let result = entries.map(e => { if (e[0] == "children") { return e[1].map(child => findIds(child)); } else { return e[1]; } }); function flatten(arr, flat = []) { for (let i = 0, length = arr.length; i < length; i++) { const value = arr[i]; if (Array.isArray(value)) { flatten(value, flat); } else { flat.push(value); } } return flat; } return flatten(result); } var ids = findIds(myObj); console.log(ids);

Flattening function from this answer来自这个答案的展平功能

ES5 syntax: ES5 语法:

 var myObj = { id: 1, children: [{ id: 2, children: [{ id: 3 }] }, { id: 4, children: [{ id: 5, children: [{ id: 6, children: [{ id: 7, }] }] }] }, ] }; function findIds(obj) { const entries = Object.entries(obj); let result = entries.map(function(e) { if (e[0] == "children") { return e[1].map(function(child) { return findIds(child) }); } else { return e[1]; } }); function flatten(arr, flat = []) { for (let i = 0, length = arr.length; i < length; i++) { const value = arr[i]; if (Array.isArray(value)) { flatten(value, flat); } else { flat.push(value); } } return flat; } return flatten(result); } var ids = findIds(myObj); console.log(ids);

I found steve's answer to be most suited for my needs in extrapolating this out and creating a general recursive function.我发现史蒂夫的答案最适合我在推断这一点并创建通用递归函数时的需求。 That said, I encountered issues when dealing with nulls and undefined values, so I extended the condition to accommodate for this.也就是说,我在处理空值和未定义值时遇到了问题,所以我扩展了条件以适应这一点。 This approach uses:这种方法使用:

Array.reduce() - It uses an accumulator function which appends the value's onto the result array. Array.reduce() - 它使用累加器函数将值附加到结果数组上。 It also splits each object into it's key:value pair which allows you to take the following steps:它还将每个对象拆分为它的键:值对,允许您执行以下步骤:

  1. Have you've found the key?你找到钥匙了吗? If so, add it to the array;如果是,则将其添加到数组中;
  2. If not, have I found an object with values?如果没有,我是否找到了一个带有值的对象? If so, the key is possibly within there.如果是这样,钥匙可能就在那里。 Keep digging by calling the function on this object and append the result onto the result array;通过调用此对象上的函数继续挖掘并将结果附加到结果数组中; and
  3. Finally, if this is not an object, return the result array unchanged.最后,如果这不是一个对象,则返回结果数组不变。

Hope it helps!希望能帮助到你!

 const myObj = { id: 1, children: [{ id: 2, children: [{ id: 3 }] }, { id: 4, children: [{ id: 5, children: [{ id: 6, children: [{ id: 7, }] }] }] }, ] } function findAllByKey(obj, keyToFind) { return Object.entries(obj) .reduce((acc, [key, value]) => (key === keyToFind) ? acc.concat(value) : (typeof value === 'object' && value) ? acc.concat(findAllByKey(value, keyToFind)) : acc , []) || []; } const ids = findAllByKey(myObj, 'id'); console.log(ids)

We use object-scan for a lot of our data processing needs now.我们现在使用对象扫描来满足我们的许多数据处理需求。 It makes the code much more maintainable, but does take a moment to wrap your head around.它使代码更易于维护,但确实需要一些时间来解决。 Here is how you could use it to answer your question这是您如何使用它来回答您的问题

 // const objectScan = require('object-scan'); const find = (data, needle) => objectScan([needle], { rtn: 'value' })(data); const myObj = { id: 1, children: [{ id: 2, children: [ { id: 3 } ] }, { id: 4, children: [ { id: 5, children: [ { id: 6, children: [ { id: 7 } ] } ] } ] }] }; console.log(find(myObj, '**.id')); // => [ 7, 6, 5, 4, 3, 2, 1 ]
 .as-console-wrapper {max-height: 100% !important; top: 0}
 <script src="https://bundle.run/object-scan@13.7.1"></script>

Disclaimer : I'm the author of object-scan免责声明:我是对象扫描的作者

import {flattenDeep} from 'lodash';

/**
 * Extracts all values from an object (also nested objects)
 * into a single array
 *
 * @param obj
 * @returns
 *
 * @example
 * const test = {
 *  alpha: 'foo',
 *  beta: {
 *    gamma: 'bar',
 *    lambda: 'baz'
 *  }
 * }
 *
 * objectFlatten(test) // ['foo', 'bar', 'baz']
 */
export function objectFlatten(obj: {}) {
  const result = [];
  for (const prop in obj) {
    const value = obj[prop];
    if (typeof value === 'object') {
      result.push(objectFlatten(value));
    } else {
      result.push(value);
    }
  }
  return flattenDeep(result);
}
let str = JSON.stringify(myObj);
let array = str.match(/\d+/g).map(v => v * 1);
console.log(array); // [1, 2, 3, 4, 5, 6, 7]

Below solution is generic which will return all values by matching nested keys as well eg for below json object下面的解决方案是通用的,它将通过匹配嵌套键返回所有值,例如对于下面的 json 对象

{
   "a":1,
   "b":{
      "a":{
         "a":"red"
      }
   },
   "c":{
      "d":2
   }
}

to find all values matching key "a" output should be return找到匹配键“a”的所有值应该返回

[1,{a:"red"},"red"]
const findkey = (obj, key) => {
  let arr = [];
  if (isPrimitive(obj)) return obj;

  for (let [k, val] of Object.entries(obj)) {
    if (k === key) arr.push(val);
    if (!isPrimitive(val)) arr = [...arr, ...findkey(val, key)];
  }
  return arr;
};

const isPrimitive = (val) => {
  return val !== Object(val);
};

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

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