Sorry for the potentially confusing question. What I mean is this:
I've been tasked to rename a specific set of object keys on a massively nested JSON object I've converted from XML. These keys are scattered throughout the nested object, buried under both arrays and objects. Let me show you what I mean:
const obj = {
"Zone": [
{
ExtWall: [
{ Win: "10" }
],
HVACDist: {
HVACCool: [
{ Win: "10" }
]
}
}
]
};
In this case, I need to rename the key Win to ResWin. However, this tag Win is scattered across a nested object far deeper than this. I'm currently using JSONPath to find the path of these keys. For example:
var WinPath = jp.paths(object, '$..Win');
// Returns - [
// [ '$', 'Zone', 0, 'ExtWall', 0, 'Win' ],
// [ '$', 'Zone', 0, 'HVACDist', 'HVACCool', 0, 'Win' ]
// ]
JSONPath also has a stringify function which generates a path based on the paths array it produces. Let's focus on one element for now:
const pathStringified = jp.stringify(WinPath[0])
// Returns - $['Zone'][0]['ExtWall][0]['Win']
Somehow, I need to rename this Win key to ResWin.
Right now my best guess is to replace the $
with an empty string so I can access the objec for key replacement.
My goal:
obj['Zone'][0]['ExtWall][0]['ResWin'] = obj['Zone'][0]['ExtWall][0]['Win'];
delete obj['Zone'][0]['ExtWall][0]['Win'];
Any feedback or suggestions would be much appreciated. Also, let me know how to restructure my question because I can understand how it would be difficult to understand.
CodePudding user response:
If you want to replace all keys with same name you can iterate the entire tree. If you want to rename by path, basically loop the path drilling down until you are at the requested key.
const obj = {
"Zone": [{
ExtWall: [{
Win: "10"
}],
HVACDist: {
HVACCool: [{
Win: "10"
}]
}
}]
};
var WinPath = [
['$', 'Zone', 0, 'ExtWall', 0, 'Win'],
['$', 'Zone', 0, 'HVACDist', 'HVACCool', 0, 'Win']
];
function rename_by_path(obj, arr_path, new_name) {
var pointer = obj;
var dollar = arr_path.shift();
var key = arr_path.shift();
while (arr_path.length) {
pointer = pointer[key]
key = arr_path.shift();
}
pointer[new_name] = pointer[key]
delete pointer[key]
}
function rename_all(obj, key_name, new_name) {
const iterate = (obj) => {
if (!obj) {
return;
}
Object.keys(obj).forEach(key => {
var value = obj[key]
if (typeof value === "object" && value !== null) {
iterate(value)
}
if (key == key_name) {
obj[new_name] = value;
delete obj[key];
}
})
}
iterate(obj)
}
function rename_by_paths(obj, arr_paths, new_name) {
arr_paths.forEach(arr_path => rename_by_path(obj, arr_path, new_name))
}
rename_by_paths(obj, WinPath, "ResWin")
console.log(obj)
rename_all(obj, "ResWin", "ResWin2");
console.log(obj)
.as-console-wrapper {
max-height: 100% !important;
}
CodePudding user response:
Sounds like you're ok using a library, so here is a solution using object-scan.
.as-console-wrapper {max-height: 100% !important; top: 0}
<script type="module">
import objectScan from 'https://cdn.jsdelivr.net/npm/[email protected]/lib/index.min.js';
const obj = { Zone: [{ ExtWall: [{ Win: '10' }], HVACDist: { HVACCool: [{ Win: '10' }] } }] };
const modify = objectScan(['**.Win'], {
filterFn: ({ parent, property, value }) => {
delete parent[property];
parent.ResWin = value;
},
rtn: 'count'
});
const r = modify(obj);
console.log(r); // number of matches
// => 2
console.log(obj);
// => { Zone: [ { ExtWall: [ { ResWin: '10' } ], HVACDist: { HVACCool: [ { ResWin: '10' } ] } } ] }
</script>
Disclaimer: I'm the author of object-scan
The syntax is very similar to the JSONPath sytax, but it provides callbacks. The code could easily be generified to allow renaming of multiple keys in a single iteration.
Note that obj
is modified in place. You could do a copy before modification, if that is not desired (e.g. using lodash cloneDeep)