我有一个对象数组,其中包含项目(仅具有name
属性)和组(具有children
属性,它们可能包含项目或其他组),我需要获取完整的路径needle
的值,因此在这种情况下为myObj[2]["children"][0]["children"][1]["children"][0]
,而且我仅限于相当老的JS版本ECMA 262(我在Photoshop中使用它)
var myObj = [
{
"name": "group1",
"children": [
{
"name": "group2",
"children": [
{
"name": "item0"
}]
}]
},
{
"name": "item1"
},
{
"name": "needleGroup",
"children": [
{
"name": "needleNestedGroup",
"children": [
{
"name": "item3"
},
{
"name": "needleNestedDeeperGroup",
"children": [
{
"name": "needle"
}]
}]
}]
}];
我的第一个想法是将对象转换为一个或多个数组,以便于处理,因此我的对象变成了
[
[
[
"item0"
]
],
"item1",
[
[
"item3",
[
"needle"
]
]
]
];
但是..就是这样。我无法找出仅追踪所需索引的方法。您能指出正确的方向吗?
答案 0 :(得分:1)
使用递归函数查找所需的项目。一旦找到函数,它将返回一个数组。递归的每一步将unshift
此步骤的对象键:
function find(obj, item) {
for(var key in obj) { // for each key in obj (obj is either an object or an array)
if(obj[key] && typeof obj[key] === "object") { // if the current property (value of obj[key]) is also an object/array
var result = find(obj[key], item); // try finding item in that object
if(result) { // if we find it
result.unshift(key); // we shift the current key to the path array (result will be an array of keys)
return result; // and we return it to our caller
}
} else if(obj[key] === item) { // otherwise (if obj[key] is not an object or array) we check if it is the item we're looking for
return [key]; // if it is then we return the path array (this is where the path array get constructed)
}
}
}
此函数的输出将是指向item
的一组键。您可以轻松地将其转换为问题中的格式:
function findFormatted(obj, item) {
var path = find(obj, item); // first let's get the path array to item (if it exists)
if(path == null) { // if it doesn't exist
return ""; // return something to signal its inexistance
}
return 'myObj["' + path.join('"]["') + '"]'; // otherwise format the path array into a string and return it
}
示例:
function find(obj, item) {
for(var key in obj) {
if(obj[key] && typeof obj[key] === "object") {
var result = find(obj[key], item);
if(result) {
result.unshift(key);
return result;
}
} else if(obj[key] === item) {
return [key];
}
}
}
function findFormatted(obj, item) {
var path = find(obj, item);
if(path == null) {
return "";
}
return 'myObj["' + path.join('"]["') + '"]';
}
var myObj = [{"name":"group1","children":[{"name":"group2","children":[{"name":"item0"}]}]},{"name":"item1"},{"name":"needleGroup","children":[{"name":"needleNestedGroup","children":[{"name":"item3"},{"name":"needleNestedDeeperGroup","children":[{"name":"needle"}]}]}]}];
console.log("find(myObj, \"needle\"): " + JSON.stringify(find(myObj, "needle")));
console.log("findFormatted(myObj, \"needle\"): " + findFormatted(myObj, "needle"));
注意:数组的索引也设置为字符串格式,但这不会造成问题,因为someArray["2"]
等效于someArray[2]
。
答案 1 :(得分:0)
假设您的数据集中有嵌套且重复的对象模式,以下解决方案将为您解决问题。
const nodePath = { value: [] };
function findTreeNodeAndPath(
targetNodeKey,
targetNodeValue,
nodeChildrenKey,
tree
) {
if (tree[targetNodeKey] === targetNodeValue) {
nodePath.value.push(tree);
return;
}
if (tree[nodeChildrenKey].length > 0) {
tree[nodeChildrenKey].forEach(children => {
if (nodePath.value.length < 1) {
findTreeNodeAndPath(
targetNodeKey,
targetNodeValue,
nodeChildrenKey,
children
);
}
});
} else if (tree[nodeChildrenKey].length === 0) {
return;
}
if (nodePath.value.length > 0) {
nodePath.value.push(tree);
}
}
const exampleData = {
name: "Root",
children: [
{
name: "A2",
children: [
{
name: "AC1",
children: [
{
name: "ACE1",
children: []
}
]
},
{
name: "AC2",
children: [
{
name: "ACF1",
children: []
},
{
name: "ACF2",
children: [
{
name: "ACFG1",
children: []
}
]
},
{
name: "ACF3",
children: [
{
name: "ACFH1",
children: []
}
]
}
]
}
]
}
]
};
findTreeNodeAndPath("name", "ACFG1", "children", exampleData);
console.log(nodePath.value)
代码的递归部分将在当前节点的子代上迭代。这里的现有策略取决于具有至少一个元素的nodePath.value
,这表明它找到了目标节点。稍后,它将跳过其余节点,并且会中断递归。
nodePath.value
变量将为您提供节点到根的路径。
答案 2 :(得分:0)
我创建了一些您可能会使用的东西。下面的代码返回到您要查找的键,值,对象的路径数组。
请参见代码段和示例,以了解您可以做什么。
要使其工作,您必须在要作为数组或对象的element和element中传递要查找的键和/或值。
它是用较新的JS标准编写的,但是将其编译为较旧的标准应该不是问题。
function findKeyValuePairsPath(keyToFind, valueToFind, element) {
if ((keyToFind === undefined || keyToFind === null) &&
(valueToFind === undefined || valueToFind === null)) {
console.error('You have to pass key and/or value to find in element!');
return [];
}
const parsedElement = JSON.parse(JSON.stringify(element));
const paths = [];
if (this.isObject(parsedElement) || this.isArray(parsedElement)) {
checkObjOrArr(parsedElement, keyToFind, valueToFind, 'baseElement', paths);
} else {
console.error('Element must be an Object or Array type!', parsedElement);
}
console.warn('Main element', parsedElement);
return paths;
}
function isObject(elem) {
return elem && typeof elem === 'object' && elem.constructor === Object;
}
function isArray(elem) {
return Array.isArray(elem);
}
function checkObj(obj, keyToFind, valueToFind, path, paths) {
Object.entries(obj).forEach(([key, value]) => {
if (!keyToFind && valueToFind === value) {
// we are looking for value only
paths.push(`${path}.${key}`);
} else if (!valueToFind && keyToFind === key) {
// we are looking for key only
paths.push(path);
} else if (key === keyToFind && value === valueToFind) {
// we ale looking for key: value pair
paths.push(path);
}
checkObjOrArr(value, keyToFind, valueToFind, `${path}.${key}`, paths);
});
}
function checkArr(array, keyToFind, valueToFind, path, paths) {
array.forEach((elem, i) => {
if (!keyToFind && valueToFind === elem) {
// we are looking for value only
paths.push(`${path}[${i}]`);
}
checkObjOrArr(elem, keyToFind, valueToFind, `${path}[${i}]`, paths);
});
}
function checkObjOrArr(elem, keyToFind, valueToFind, path, paths) {
if (this.isObject(elem)) {
checkObj(elem, keyToFind, valueToFind, path, paths);
} else if (this.isArray(elem)) {
checkArr(elem, keyToFind, valueToFind, path, paths);
}
}
const example = [
{
exampleArr: ['lol', 'test', 'rotfl', 'yolo'],
key: 'lol',
},
{
exampleArr: [],
key: 'lol',
},
{
anotherKey: {
nestedKey: {
anotherArr: ['yolo'],
},
anotherNestedKey: 'yolo',
},
emptyKey: null,
key: 'yolo',
},
];
console.log(findKeyValuePairsPath('key', 'lol', example)); // ["baseElement[0]", "baseElement[1]"]
console.log(findKeyValuePairsPath(null, 'yolo', example)); // ["baseElement[0].exampleArr[3]", "baseElement[2].anotherKey.nestedKey.anotherArr[0]", "baseElement[2].anotherKey.anotherNestedKey", "baseElement[2].key"]
console.log(findKeyValuePairsPath('anotherNestedKey', null, example)); //["baseElement[2].anotherKey"]