I have an array of objects with items (only have name
property) and groups (with a children
property, they may contain items or other groups) and I need to get a full path to needle
value, so in this case it'd be myObj[2]["children"][0]["children"][1]["children"][0]
, plus I'm limited to quite old JS version ECMA 262 (I'm using it inside 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"
}]
}]
}]
}];
My first idea was to transform object to array or arrays so it'd be easier to process, so my object became
[
[
[
"item0"
]
],
"item1",
[
[
"item3",
[
"needle"
]
]
]
];
But.. that's it. I can't figure out hot to track down only the indexes I need. Could you please point out a correct direction.
Use a recursive function to look for the item you want. Once the function find it, it will return an array. Each step back of the recursion will unshift
the object key of this step:
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)
}
}
}
The output of this function will be an array of keys leading to item
. You can easily transform it to the format in the question:
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
}
Example:
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"));
Note: The indexes for the arrays are also formatted as strings, but that won't be a problem as someArray["2"]
is equivalent to someArray[2]
.
I've created something you might use. The code below returns an Array of paths to keys, values, objects you are looking for.
See snippet and example to see what you can do.
To make it work you have to pass key and/or value you want to find in element and element which is an Array or Object.
It's written in newer JS standard but it shouldn't be a problem to compile it to older standard.
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"]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With