在深层嵌套对象中按特定键查找所有值
Posted
技术标签:
【中文标题】在深层嵌套对象中按特定键查找所有值【英文标题】:Find all values by specific key in a deep nested object 【发布时间】:2019-07-18 07:50:40 【问题描述】:如何?
例如,如果我有这样的对象:
const myObj =
id: 1,
children: [
id: 2,
children: [
id: 3
]
,
id: 4,
children: [
id: 5,
children: [
id: 6,
children: [
id: 7,
]
]
]
,
]
如何通过id
的键在这个 obj 的所有嵌套中获取所有值的数组。
注意:children
是一个一致的名称,id
不会存在于 children
对象之外。
所以从 obj 中,我想生成一个这样的数组:
const idArray = [1, 2, 3, 4, 5, 6, 7]
【问题讨论】:
【参考方案1】:这有点晚了,但对于其他发现它的人来说,这是一个干净的通用递归函数:
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')
【讨论】:
超级干净,效果很好,易于剪切和粘贴。最佳答案。 这为我节省了一些时间,而且考虑启动也很有趣。谢谢! 将第 5 行更改为: (typeof value === 'object' && value)
,因为出于某种原因,typeof null === "object"
。【参考方案2】:
你可以像这样创建一个递归函数:
idArray = []
function func(obj)
idArray.push(obj.id)
if (!obj.children)
return
obj.children.forEach(child => func(child))
您的示例代码段:
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)
【讨论】:
【参考方案3】:您可以创建一个适用于任何属性和任何对象的通用递归函数。
这使用Object.entries()
、Object.keys()
、Array.reduce()
、Array.isArray()
、Array.map()
和Array.flat()
。
停止条件是传入的对象为空时:
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));
【讨论】:
【参考方案4】:注意:
children
是一致的名称,id
不会存在于外部children
对象。所以从 obj 中,我想生成一个这样的数组:
const idArray = [1, 2, 3, 4, 5, 6, 7]
鉴于该问题不包含对如何从输入导出输出的任何限制,并且输入是一致的,其中属性"id"
的值是一个数字,而id
属性仅在@987654327 内定义@ 属性,除了对象中第一个 "id"
的情况外,输入的 javascript 普通对象可以使用 JSON.stringify()
转换为 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
函数也可以用于.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);
由于要匹配的输入的属性值是数字,所有的JavaScript对象都可以转换成字符串,RegExp
\D
可以用来替换所有不是数字的字符,将得到的字符串展开到数组中, 和 .map()
数字转换为 JavaScript 数字
let res = [...JSON.stringify(myObj).replace(/\D/g,"")].map(Number)
【讨论】:
【参考方案5】:使用递归。
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;
【讨论】:
【参考方案6】:我发现 steve 的答案最适合我在推断这一点并创建通用递归函数时的需要。也就是说,我在处理空值和未定义值时遇到了问题,因此我扩展了条件以适应这种情况。这种方法使用:
Array.reduce() - 它使用累加器函数将值附加到结果数组中。它还将每个对象拆分为它的键:值对,这允许您采取以下步骤:
-
你找到钥匙了吗?如果是,则将其添加到数组中;
如果没有,我是否找到了具有值的对象?如果是这样,钥匙可能就在里面。通过调用此对象上的函数并将结果附加到结果数组中继续挖掘;和
最后,如果这不是一个对象,则返回结果数组不变。
希望对你有帮助!
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)
【讨论】:
内部错误:递归过多 你有示例对象吗? 太棒了。正是我需要的!效果很好。【参考方案7】:您可以像这样使用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);
来自this answer的扁平化函数
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);
【讨论】:
没问题@cup_of,我很乐意提供帮助【参考方案8】: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]
【讨论】:
啊,非常聪明的方法。 +1【参考方案9】:我们现在使用object-scan 来满足我们的很多数据处理需求。它使代码更易于维护,但确实需要一点时间来理解。以下是您如何使用它来回答您的问题
// 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>
免责声明:我是object-scan的作者
【讨论】:
【参考方案10】: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);
【讨论】:
如果你写一个关于你的示例代码的解释会很有帮助。 @chrwahl 请查看上面代码的 jsdocs 注释,甚至附带示例。【参考方案11】:下面的解决方案是通用的,它将通过匹配嵌套键返回所有值,例如下面的 json 对象
"a":1,
"b":
"a":
"a":"red"
,
"c":
"d":2
要查找与键“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);
;
【讨论】:
以上是关于在深层嵌套对象中按特定键查找所有值的主要内容,如果未能解决你的问题,请参考以下文章