我有一个javascript对象宽度深度。
我需要知道这个键在对象中的确切路径:"obj1.obj2.data1"
我已经知道键是data1,值是123。
我的javascript对象是这样的
{
obj1: {
obj2: {
data1: 213,
data2: "1231",
obj3: {
data: "milf"
}
}
},
obj4: {
description: "toto"
}
}
我怎么才能做到呢?
这里是一个jsfiddle: http://jsfiddle.net/3hvav8xf/8/我试图实现getPath。
我认为递归函数可以帮助你(更新版本,检查值)
function path(c, name, v, currentPath, t){
var currentPath = currentPath || "root";
for(var i in c){
if(i == name && c[i] == v){
t = currentPath;
}
else if(typeof c[i] == "object"){
return path(c[i], name, v, currentPath + "." + i);
}
}
return t + "." + name;
};
console.log(path({1: 2, s: 5, 2: {3: {2: {s: 1, p: 2}}}}, "s", 1));
下面的命令在任何级别的嵌套对象中查找路径。还有数组。它返回找到的所有路径,如果您有相同名称的键,这是您想要的。
我喜欢这种方法,因为它适用于lodash
方法get
和set
开箱即用。
function findPathsToKey(options) {
let results = [];
(function findKey({
key,
obj,
pathToKey,
}) {
const oldPath = `${pathToKey ? pathToKey + "." : ""}`;
if (obj.hasOwnProperty(key)) {
results.push(`${oldPath}${key}`);
return;
}
if (obj !== null && typeof obj === "object" && !Array.isArray(obj)) {
for (const k in obj) {
if (obj.hasOwnProperty(k)) {
if (Array.isArray(obj[k])) {
for (let j = 0; j < obj[k].length; j++) {
findKey({
obj: obj[k][j],
key,
pathToKey: `${oldPath}${k}[${j}]`,
});
}
}
if (obj[k] !== null && typeof obj[k] === "object") {
findKey({
obj: obj[k],
key,
pathToKey: `${oldPath}${k}`,
});
}
}
}
}
})(options);
return results;
}
findPathsToKey({ obj: objWithDuplicates, key: "d" })
// ["parentKey.arr[0].c.d", "parentKey.arr[1].c.d", "parentKey.arr[2].c.d"]
试试这里- https://jsfiddle.net/spuhb8v7/1/
如果您希望结果是单个键(第一次遇到),您可以将results
更改为字符串,如果定义了,然后返回函数。
我最终得到了以下函数,它适用于嵌套的对象/数组:
function findPath (obj, name, val, currentPath) {
currentPath = currentPath || ''
let matchingPath
if (!obj || typeof obj !== 'object') return
if (obj[name] === val) return `${currentPath}['${name}']`
for (const key of Object.keys(obj)) {
if (key === name && obj[key] === val) {
matchingPath = currentPath
} else {
matchingPath = findPath(obj[key], name, val, `${currentPath}['${key}']`)
}
if (matchingPath) break
}
return matchingPath
}
const treeData = [{
id: 1,
children: [{
id: 2
}]
}, {
id: 3,
children: [{
id: 4,
children: [{
id: 5
}]
}]
}]
console.log(findPath (treeData, 'id', 5))
给你!
function getPath(obj, value, path) {
if(typeof obj !== 'object') {
return;
}
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
console.log(key);
var t = path;
var v = obj[key];
if(!path) {
path = key;
}
else {
path = path + '.' + key;
}
if(v === value) {
return path;
}
else if(typeof v !== 'object'){
path = t;
}
var res = getPath(v, value, path);
if(res) {
return res;
}
}
}
}
getPath(yourObject, valueYouWantToFindPath);
如果找到则返回路径,否则返回未定义。我只是用实物测试过它。比较非常严格(即:使用===
)。
更新:以key作为参数的更新版本。
function getPath(obj, key, value, path) {
if(typeof obj !== 'object') {
return;
}
for(var k in obj) {
if(obj.hasOwnProperty(k)) {
console.log(k);
var t = path;
var v = obj[k];
if(!path) {
path = k;
}
else {
path = path + '.' + k;
}
if(v === value) {
if(key === k) {
return path;
}
else {
path = t;
}
}
else if(typeof v !== 'object'){
path = t;
}
var res = getPath(v, key, value, path);
if(res) {
return res;
}
}
}
}
getPath(yourObject, key, valueYouWantToFindPath);
JSON Object可以在JavaScript中作为关联数组处理。
所以你可以循环遍历和存储"parent "的索引
假设整个对象存储在名为obj的变量中。
for(var p1 in obj)
{
for( var p2 in obj[ p1 ] ) { for( var p3 in obj[ p1 ][ p2 ] ) { // obj[ p1 ][ p2 ][ p3 ] is current node // so for Your example it is obj.obj1.obj2.data1 } }
}
希望回答是有帮助的。
我将按如下方法做这项工作;
Object.prototype.paths = function(root = [], result = {}) {
var ok = Object.keys(this);
return ok.reduce((res,key) => { var path = root.concat(key);
typeof this[key] === "object" &&
this[key] !== null ? this[key].paths(path,res)
: res[this[key]] == 0 || res[this[key]] ? res[this[key]].push(path)
: res[this[key]] = [path];
return res;
},result);
};
var myObj = {
obj1: {
obj2: {
data1: 213,
data2: "1231",
obj3: {
data: "milf"
}
}
},
obj4: {
description: "toto",
cougars: "Jodi",
category: "milf"
}
},
value = "milf",
milfPath = myObj.paths()[value]; // the value can be set dynamically and if exists it's path will be listed.
console.log(milfPath);
几句警告:我们在使用Object原型时应该谨慎。我们的修改应该有描述符enumerable = false
,否则它将在for in
循环中列出,例如jQuery将无法工作。(这是多么愚蠢的jQuery,因为显然他们没有使hasOwnProperty
检查在他们的for in循环)一些好的读取在这里和这里,所以我们必须添加这个对象方法与Object.defineProperty()
,使其成为enumerable = false;
。但为了简单起见,并保持在问题的范围内,我没有在代码中包含这一部分。
这是我写的一个相当短的,相对容易理解的函数,用于检索对象上每个属性/字段的JSON路径(无论嵌套有多深)。
getPaths(object)
函数只是接受你想要JSON路径的对象,并返回一个路径数组。或者,如果您希望初始对象用与标准JSON路径符号$不同的符号表示,您可以调用getPaths(object, path)
,每个JSON路径将以指定的路径开始。
例如:getPaths({prop: "string"}, 'obj');
将返回以下JSON路径:obj.prop
,而不是$.prop
。
请参阅下面的getPaths
返回的更详细、更深入的示例,以及如何使用它。
object = {
"firstName": "John",
"lastName": "doe",
"age": 26,
"fakeData": true,
"address": {
"streetAddress": "fake street",
"city": "fake city",
"postalCode": "12345"
},
"phoneNumbers": [{
"type": "iPhone",
"number": "0123-4567-8888"
}, {
"type": "home",
"number": "0123-4567-8910"
}]
};
function getPaths(object, path = "$") {
return Object.entries(object).flatMap(function(o, i) {
if (typeof o[1] === "object" && !o[1].length) {
return `${getPaths(o[1], path + '.' + o[0])}`.split(',');
} else if (typeof o[1] === "object" && o[1].length) {
return Object.entries(o[1]).flatMap((no, i) => getPaths(no[1], `${path}.${o[0]}[${i}]`));
} else {
return `${path}.${o[0]}`;
}
});
}
console.log(`%o`, getPaths(object));
我真的很喜欢Roland Jegorov的答案,但我有一个非常复杂的对象,我需要搜索,这个答案不能解释它。
如果你是在像我这样的情况下,你可能想要首先确保你没有循环引用(否则你会遇到无限搜索)。有几种方法可以做到这一点,但我不得不将我的对象字符串化以将其复制到其他窗口,所以我最终使用了这个循环替换器:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cyclic_object_value
(更新在这里-我做了一个小的改变从MDN的getCircularReplacer函数,所以它不再离开函数引用,因为这是我正在寻找的!)
(更新3 -我还想检查类的任何实例的方法,但我只是返回"函数"太早,所以我已经调整它包括实例方法。我想它终于像我想的那样工作了!)
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === "function") {
if (value?.prototype) {
if (seen.has(value.prototype)) {
return;
}
seen.add(value.prototype)
return value.prototype
}
return "function";
}
if (typeof value === "object" && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};
const nonCyclicObject = JSON.parse(JSON.stringify(myComplexObject, getCircularReplacer()));
然后我使用了Roland的这个修改版本的答案:
(更新2:我必须确保在找到键后不返回,因为如果对象的第一层具有该键,则它总是在只调用函数一次后返回)
function findPathsToKey(options) {
let count = 0;
let results = [];
(function findKey({
key,
obj,
pathToKey,
}) {
count += 1;
if (obj === null) return;
const oldPath = `${pathToKey ? pathToKey + "." : ""}`;
if (Object.hasOwnProperty.call(obj, key)) {
results.push(`${oldPath}${key}`);
}
if (typeof obj === "object" && !Array.isArray(obj)) {
for (const k in obj) {
if (Object.hasOwnProperty.call(obj, k)) {
if (Array.isArray(obj[k])) {
for (let j = 0; j < obj[k].length; j++) {
findKey({
obj: obj[k][j],
key,
pathToKey: `${oldPath}${k}[${j}]`,
});
}
}
if (typeof obj[k] === "object") {
findKey({
obj: obj[k],
key,
pathToKey: `${oldPath}${k}`,
});
}
}
}
}
})(options);
return { count, results };
};
计数只是为了解决一些问题,并确保它实际上运行了我认为的键数。希望这有助于其他人寻找解决方案!
⚠️这段代码没有回答这个问题,但做了相关的工作:将嵌套对象转换为使用dot.divided.path
作为键和非对象值的查询对象;与URlSearchParams &qs。也许会对某人有用。
const isPlainObject = (v) => {
if (Object.prototype.toString.call(v) !== '[object Object]') return false;
const prototype = Object.getPrototypeOf(v);
return prototype === null || prototype === Object.prototype;
};
const objectToQueryObject = (obj, path) => {
return Object.entries(obj).reduce((acc, [key, value]) => {
const newPath = path ? `${path}.${key}` : key;
if (isPlainObject(value)) {
return {
...acc,
...objectToQueryObject(value, newPath)
};
}
acc[newPath] = value;
return acc;
}, {})
};
const queryObjectRaw = {
value: {
field: {
array: {
'[*]': {
field2: {
eq: 'foo',
ne: 'bar',
}
}
},
someOtherProp: { in: [1, 2, 3],
ne: 'baz',
}
},
someOtherField: {
gt: 123
},
},
otherValue: {
eq: 2
},
};
const result = objectToQueryObject(queryObjectRaw);
console.log('result', result);
const queryString = new URLSearchParams(result).toString();
console.log('queryString', queryString);
如果您只知道值而不知道键,并且希望查找包含该值的所有路径,则使用此方法。
它将找到所有具有该值的属性,并打印每个创建值的完整路径。
const createArrayOfKeys = (obj, value) => {
const result = []
function iter(o) {
Object.keys(o).forEach(function(k) {
if (o[k] !== null && typeof o[k] === 'object') {
iter(o[k])
return
}
if (o[k]=== value) {
result.push(k)
return
}
})
}
iter(obj)
return result
}
function findPath (obj, name, val, currentPath) {
currentPath = currentPath || ''
let matchingPath
if (!obj || typeof obj !== 'object') return
if (obj[name] === val) return `${currentPath}/${name}/${val}`
for (const key of Object.keys(obj)) {
if (key === name && obj[key] === val) {
matchingPath = currentPath
} else {
matchingPath = findPath(obj[key], name, val, `${currentPath}/${key}`)
}
if (matchingPath) break
}
return matchingPath
}
const searchMultiplePaths = (obj, value) => {
const keys = createArrayOfKeys(obj, value)
console.log(keys);
keys.forEach(key => {
console.log(findPath(obj, key, value))
})
}
var data = { ffs: false, customer: { customer_id: 1544248, z_cx_id: '123456' }, selected_items: { '3600196': [{ id: 4122652, name: 'Essential Large (up to 8'x10')', selected: true }] }, service_partner: { id: 3486, name: 'Some String', street: '1234 King St.', hop: '123456' }, subject: 'Project-2810191 - Orange Juice Stain (Rug)', description: 'Product Type: nnIssue: (copy/paste service request details here)nnAction Required:', yes: '123456' };
searchMultiplePaths(data, '123456')
我知道这篇文章很老了,但是上面的答案并不能让我真正满意。
一个简单的解决方案是为结构中的每个对象添加对象路径。当你需要的时候,你可以很容易地读取路径。
let myObject = {
name: 'abc',
arrayWithObject: [
{
name: "def"
},
{
name: "ghi",
obj: {
name: "jkl"
}
}
],
array: [15, 'mno'],
arrayArrayObject: [
[
{
name: '...'
}
]
]
}
function addPath(obj, path = [], objectPathKey = '_path') {
if (Array.isArray(obj)) {
obj.map((item, idx) => addPath(item, [...path, idx]))
} else if (typeof obj === "object") {
obj[objectPathKey] = path;
for (const key in obj) {
obj[key] = addPath(obj[key], [...path, key])
}
}
return obj
}
myObject = addPath(myObject);
let changeMe = _.cloneDeep(myObject.arrayWithObject[0])
changeMe.newProp = "NEW"
changeMe.newNested = {name: "new", deeper: {name: "asdasda"}}
changeMe = addPath(changeMe, changeMe._path)
_.set(myObject, changeMe._path, changeMe);
更新完成后,清理对象并删除_path属性。
方案优点:
- 你只做一次工作
- 你保持你的代码简单
- 不需要自己的属性检查
- 无认知超载
我强烈建议您使用lodash来解决这个问题。
在他们的文档中,这应该可以帮助你。
// using "_.where" callback shorthand
_.find(characters, { 'age': 1 });
// → { 'name': 'pebbles', 'age': 1, 'blocked': false }