With an array of
['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
I'd like to construct an map object that looks like:
{
'social': {
swipes: {
women: null,
men: null
}
},
'upgrade': {
premium: null
}
}
const menu = ['/social/swipes/women', '/social/likes/men', '/upgrade/premium'];
const map = {};
const addLabelToMap = (root, label) => {
if(!map[root]) map[root] = {};
if(!map[root][label]) map[root][label] = {};
}
const buildMenuMap = menu => {
menu
// make a copy of menu
// .slice returns a copy of the original array
.slice()
// convert the string to an array by splitting the /'s
// remove the first one as it's empty
// .map returns a new array
.map(item => item.split('/').splice(1))
// iterate through each array and its elements
.forEach((element) => {
let root = map[element[0]] || "";
for (let i = 1; i < element.length; i++) {
const label = element[i];
addLabelToMap(root, label)
// set root to [root][label]
//root = ?
root = root[label];
}
});
}
buildMenuMap(menu);
console.log(map);
But I'm unsure how to switch the value of root
.
What do I set root
to so that it recursively calls addLabelToMap
with
'[social]'
, 'swipes' => '[social][swipes]'
, 'women' => '[social][swipes]'
, 'men'
?
I've used root = root[element]
but it's giving an error.
Alternative solutions would be great, but I'd like to understand why this isn't working fundamentally.
This problem is about creating the object and maintaining it's state while looping through
input
array and splitting string based upon/
.This can be accomplished using
Array.reduce
where we start with empty object and while looping throughinput
we start filling it and for last word in every string we assign the valuenull
to object property.Try this as a holistic solution:
Note: The deep merge solution was taken from @ahtcx on GitHubGist
You can simplify your code using Array.reduce, Object.keys & String.substring
buildMenuMap
The function takes the array as input and reduce it into an object where for each entry in array, the object is updated with corresponding hierarchy using
addLabelToMap
function. Each entry is converted into an array of levels (c.substring(1).split("/")
).addLabelToMap
The function takes 2 inputs
and returns the updated object
Logic
let key = ar.shift()
) as key and add / update in the object (obj[key] = obj[key] || {};
).if(ar.length)
), recursively call the function to update the object till the end (addLabelToMap(obj[key], ar)
).else if(!Object.keys(obj[key]).length)
) because of other entries in array. If there is no hierarchy, i.e. it is a leaf, hence, set the value tonull
(obj[key] = null
). Note, if there will never be case where there is an entry in array like/social/swipes/men/young
along with existing, theelse if
block can be simplified to a simpleelse
block.It is as easy as:
if you change your helper function to:
I'd write it as:
You can solve this also with a recursive function in a concise way like this:
The idea is for each of the paths to pass them to a
makeObj
function which will decorate an object with the paths recursively until it reaches the end of the path array. This is another alternative to the commonArray.reduce
approach.I just debugged your code to see what was wrong and I urge you to do the same. You make two (obvious) mistakes:
Firstly, In the very first iteration, here the value of
map
is just an empty object{}
, the value ofroot
gets initialised to""
andlabel
isswipes
.So then you get
root[label]
isundefined
and so the new root isundefined
.Second, you are using
map
everywhere as it is.Instead you should be taking it as a parameter, for you to be able to do a recursion.
To debug you code, create a simple HTML file with the js in the script tags and then serve it from your local machine using
python -m http.server
. You can then add a debug point and go through your code step by step.