Intended output: { children: { Display: { children: { … value: 2 } } } }
Real output: { children: {}, Display: {}, … value: 2 }
Code:
// Load default settings
let defaultSettings;
load("/assets/json/default-settings.json", 'json', function(defset) {
defaultSettings = defset;
// Create custom settings
if(!Object.keys(localStorage).includes('settings')) {
setLs('settings', JSON.stringify({}));
};
customiseSetting('Display/UI/Distance', 2)
});
function settingURL(url) {
return('children/' + url.split('/').join('/children/') + '/value');
}
function customiseSetting(url, value) {
url = settingURL(url);
// Split the string by '/' and use reduce to access the nested properties
const newSettings = url.split('/').reduce(function(accumulator, val, index, array) {
// If the object does not have the current component as a property, create an empty object for it
// If the current component is the last one, assign the value
if (index == array.length - 1) {
accumulator[val] = value;
} else if (!accumulator.hasOwnProperty(val)) {
accumulator[val] = {}; // update the accumulator object
}
log([accumulator, val, index, array])
// Return the updated object
return(accumulator);
}, JSON.parse(ls('settings')));
log(newSettings);
setLs('settings', JSON.stringify(newSettings));
}
I’ve been trying unsuccessfully for several days to fix to what must be a simple error. I’ve looked over it myself, but I can’t find the cause of the bug. I asked Bing, which usually helps, but it was unhelpful. So I’m sorry to be bothering you, but if you could help me solve this problem, I would really appreciate it.
EDIT: I fixed my code by using a recursive function as follows:
function customiseSetting(url, value) {
url = settingURL(url).split('/');
let newSettings;
function recursiveSet(object, list, index, setTo) {
// If the current component is the last one, assign the value
if(index == list.length - 1) {
object[list[index]] = setTo;
return(object);
} else {
// Check if it already contains the value
if(object.hasOwnProperty(list[index])) {
object[list[index]] = recursiveSet(object[list[index]], list, index + 1, setTo);
} else {
object[list[index]] = recursiveSet({}, list, index + 1, setTo);
}
return(object);
}
};
newSettings = recursiveSet(JSON.parse(ls('settings')), url, 0, value);
log(newSettings);
setLs('settings', JSON.stringify(newSettings));
}
You can use more debug outputs (log(…)) to narrow it down. Challenge your assumptions! If necessary, check line by line if all the variables still behave as expected. Or use a debugger if available/familiar.
This takes a few minutes tops and guarantees you to find at which line the actual behaviour diverts from your expectations. Then, you can make a more precise search. But usually the solution is obvious once you have found the precise cause.
The issue is that, in the function passed to
reduce
, you’re adding each object directly to the accumulator rather than to its intended parent. These are the problem lines:if (index == array.length - 1) { accumulator[val] = value; } else if (!accumulator.hasOwnProperty(val)) { accumulator[val] = {}; // update the accumulator object }
There’s no pretty way (that I can think of at least) to do what you want using methods like
reduce
in vanilla JS, so I’d suggest using a for loop instead - especially if you’re new to programming. Something along these lines (not written to be actual code, just to give you an idea):let curr = settings; const split = url.split("/"); for (let i = 0; i < split.length: i++) { const val = split[i]; if (i != split.length-1) { //add a check to see if curr[val] exists let next = {}; curr[val] = next; curr = next; } //add else branch }
It’s missing some things, but the important part is there - every time we move one level deeper in the URL, we update
curr
so that we keep our place instead of always adding to the top level.I fixed my code by using a recursive function as follows:
function customiseSetting(url, value) { url = settingURL(url).split('/'); let newSettings; function recursiveSet(object, list, index, setTo) { // If the current component is the last one, assign the value if(index == list.length - 1) { object[list[index]] = setTo; return(object); } else { // Check if it already contains the value if(object.hasOwnProperty(list[index])) { object[list[index]] = recursiveSet(object[list[index]], list, index + 1, setTo); } else { object[list[index]] = recursiveSet({}, list, index + 1, setTo); } return(object); } }; newSettings = recursiveSet(JSON.parse(ls('settings')), url, 0, value); log(newSettings); setLs('settings', JSON.stringify(newSettings)); }
What does i < mean in line 3 of your answer?
Lemmy’s code formatter has a bug where it escapes the less than symbol <
Look at your settingURL function.
You always prepend a
children/
to the URL. Then you split the incoming URL up into an array and concatenate the elements withchildren/
.So it will become something like
children/Display/children/UI/
Yes, that’s intentional. What’s not intentional is that they are all direct children of the parent object, rather than children of each other
I’ve been trying unsuccessfully for several days to fix to what must be a simple error.
That really sucks. Others have already helped out so I won’t go there, but seriously do yourself a favour and start using large language models. I personally pay for ChatGPT Plus, but there are free ones (from other companies, not the free Open AI models) that could have helped you with this problem in minutes instead of days.
Like I said, I was using Bing. It’s usually helpful, but in this case it couldn’t help. Bing is based on GPT4 and is free