How to build a menu list object recursively in JavaScript?
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]] );
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.
javascript arrays ecmascript-6 javascript-objects arrow-functions
add a comment |
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]] );
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.
javascript arrays ecmascript-6 javascript-objects arrow-functions
1
Shouldn'tmenbe in thelikesobject not theswipesobject?
– ibrahim mahrir
Nov 14 '18 at 21:22
I've just edited it
– totalnoob
Nov 14 '18 at 21:23
add a comment |
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]] );
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.
javascript arrays ecmascript-6 javascript-objects arrow-functions
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]] );
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.
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]] );
buildMenuMap(menu);
console.log(map);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]] );
buildMenuMap(menu);
console.log(map);javascript arrays ecmascript-6 javascript-objects arrow-functions
javascript arrays ecmascript-6 javascript-objects arrow-functions
edited Dec 9 '18 at 1:01
Bharata
9,21261431
9,21261431
asked Nov 14 '18 at 21:20
totalnoobtotalnoob
3581831
3581831
1
Shouldn'tmenbe in thelikesobject not theswipesobject?
– ibrahim mahrir
Nov 14 '18 at 21:22
I've just edited it
– totalnoob
Nov 14 '18 at 21:23
add a comment |
1
Shouldn'tmenbe in thelikesobject not theswipesobject?
– ibrahim mahrir
Nov 14 '18 at 21:22
I've just edited it
– totalnoob
Nov 14 '18 at 21:23
1
1
Shouldn't
men be in the likes object not the swipes object?– ibrahim mahrir
Nov 14 '18 at 21:22
Shouldn't
men be in the likes object not the swipes object?– ibrahim mahrir
Nov 14 '18 at 21:22
I've just edited it
– totalnoob
Nov 14 '18 at 21:23
I've just edited it
– totalnoob
Nov 14 '18 at 21:23
add a comment |
8 Answers
8
active
oldest
votes
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 through input we start filling it and for last word in every string we assign the value null to object property.
let input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let output = input.reduce((o, d) =>
let keys = d.split('/').filter(d => d)
keys.reduce((k, v, i) =>
k[v] = (i != keys.length - 1)
? k[v] , o)
return o
, )
console.log(output)
how does let keys = d.split('/').filter(d => d) remove the empty entries?
– totalnoob
Nov 25 '18 at 19:23
3
.filter(d => d)represents filter onlytruthyvalue
– Nitish Narang
Nov 25 '18 at 19:26
Falsyvalues Doc - developer.mozilla.org/en-US/docs/Glossary/Falsy
– Nitish Narang
Nov 25 '18 at 19:27
clear and simple. thanks
– totalnoob
Nov 25 '18 at 19:27
you're most welcome @totalnoob
– Nitish Narang
Nov 25 '18 at 19:27
|
show 3 more comments
It is as easy as:
root = root[label];
if you change your helper function to:
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
I'd write it as:
const buildMenuMap = menus =>
const root = ;
for(const menu of menus) (curr[key] = ), root);
obj[prop] = null;
return root;
it didn't work when I tried it
– totalnoob
Nov 14 '18 at 21:26
that's awesome. can you tell me why the current code doesn't work even setting root properly?
– totalnoob
Nov 14 '18 at 21:31
@totalnoob becauseaddLabelToMapdoes not go deeper into the map
– Jonas Wilms
Nov 14 '18 at 21:32
when I use root = root[label], on the next loop root is undefined if I print it out
– totalnoob
Nov 14 '18 at 21:38
add a comment |
Use reduce instead of map. The root will be the accumulator in this case:
const buildMenuMap = menu =>
menu.reduce((root, item) =>
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] , Object.create(null));
Explanation:
For each item in the menu array, we extract the parts by first getting rid of the leading '/' (using slice(1)) and then splitting by '/'.
We then remove the lastPart from this resulting array (the last part is handled separetely from the rest).
For each remaining part in the parts array, we traverse the root array. At each level of traversing, we either return the object at that level acc[part] if it already exists, or we create and return a new one if it doesn't (acc[part] = ).
After we get to the the last level leaf, we use the lastPart to set the value as null.
Notice that we pass Object.create(null) to reduce. Object.create(null) creates a prototypeless object so it will ba safer to use root[someKey] without having to check if someKey is an owned property or not.
Example:
const buildMenuMap = menu =>
menu.reduce((root, item) => (acc[part] = ), root);
leaf[lastPart] = null;
return root;
, Object.create(null));
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let result = buildMenuMap(arr);
console.log(result);
I like this. thanks. can you explain why the original code doesn't work or how I can fix it?
– totalnoob
Nov 14 '18 at 22:33
add a comment |
You can solve this also with a recursive function in a concise way like this:
let obj=, input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const makeObj = (arr, obj=) =>
let prop = arr.shift()
prop in obj ? null : Object.assign(obj, [prop]: )
arr.length ? makeObj(arr, obj[prop]) : obj[prop] = null
return obj
input.forEach(x => makeObj(x.split('/').filter(Boolean), obj))
console.log(obj)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 common Array.reduce approach.
1
I like this one. I was only starting out with Array.reduce so the other solutions wasn't as easy to understand.
– totalnoob
Dec 9 '18 at 6:22
add a comment |
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 of root gets initialised to "" and label is swipes.
.forEach((element) =>
let root = map[element[0]]
So then you get root[label] is undefined and so the new root is undefined.
Second, you are using map everywhere as it is.
const addLabelToMap = (root, label) =>
if(!map[root]) map[root] = ;
if(!map[root][label]) map[root][label] = ;
Instead you should be taking it as a parameter, for you to be able to do a recursion.
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
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.
add a comment |
Try this as a holistic solution:
const menu = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const deepMerge = (target, source) =>
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (let key of Object.keys(source))
if (source[key] instanceof Object && key in target) Object.assign(source[key], deepMerge(target[key], source[key]))
// Join `target` and modified `source`
Object.assign(target ;
const buildMenuMap = menu =>
return menu
.map(item => item.split('/').splice(1))
// The `root` value is the object that we will be merging all directories into
.reduce((root, directory) =>
// Iterates backwards through each directory array, stacking the previous accumulated object into the current one
const branch = directory.slice().reverse().reduce((acc, cur) => const obj = ; obj[cur] = acc; return obj;,null);
// Uses the `deepMerge()` method to stitch together the accumulated `root` object with the newly constructed `branch` object.
return deepMerge(root, branch);
, );
;
buildMenuMap(menu);
Note: The deep merge solution was taken from @ahtcx on GitHubGist
add a comment |
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
obj - the current root object / node
ar - array of child hierarchy
and returns the updated object
Logic
- function pops the first value (
let key = ar.shift()) as key and add / update in the object (obj[key] = obj[key] || ;). - If there is child hierarchy of current object (
if(ar.length)), recursively call the function to update the object till the end (addLabelToMap(obj[key], ar)). - Else (no further child hierarchy), check whether the object has some hierarchy (
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/youngalong with existing, theelse ifblock can be simplified to a simpleelseblock. - The object has been update, return the final updated object
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
function addLabelToMap(obj, ar)
let key = ar.shift();
obj[key] = obj[key]
function buildMenuMap(ar)
return ar.reduce((a,c) => addLabelToMap(a,c.substring(1).split("/")), );
console.log(buildMenuMap(arr));add a comment |
Citate from bounty description:
The current answers do not contain enough detail.
I think you do not understand how it works in current answers. Because of this I will provide you two solutions: one alternative solution and one expanded solution with Array.reduce() function.
Alternative solution with for loops
The explanation of code see in the code comments.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ;
//if you want to have it shorter you can write for(var i = arr.length; i--;) too
for(var i = 0; i < arr.length; i++)
console.log(JSON.stringify(result, null, 4));But if you did not understand it then see this code:
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ,
parts = arr[2].slice(1).split('/'),
curObj = result[parts[0]] = ;
curObj[parts[1]] = parts[1+1] ? : null;
console.log(JSON.stringify(result, null, 4));Expanded solution with Array.reduce()
In this solution I use the code from user Nitish Narang in expanded version with some explanation in comments and console output – so yuo can see in console what the code does. My recommendation: if you do not understand the code with arrow functions then write it full with normal functions and appropriate variable names which explain themselves. We (humans) need some pictures to imagine all things. If we have only some short variable names then it is difficalt to imagine und undestand all this. I have also a little bit «shorted» his code.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
var result = arr.reduce(function(acc0, curVal, curIdx)
console.log('n' + curIdx + ': ------------n'
+ JSON.stringify(acc0, null, 4));
var keys = curVal.slice(1).split('/');
keys.reduce(function(acc1, currentValue, currentIndex)
acc1[currentValue] = keys[currentIndex+1]
? acc1[currentValue] , acc0); //acc0 - initialValue is the same object, but it is empty only in first cycle
return acc0
, ); // - initialValue is empty object
console.log('n------result------n'
+ JSON.stringify(result, null, 4));
1
what does curObj = result[parts[0]] = result[parts[0]] || ; do?
– totalnoob
Dec 9 '18 at 3:16
@totalnoob, if we have inparts[0]for example the valueupgrade(it could be alsosocial) then we ask: isresult["upgrade"] != "undefined"and if yes, then we take the same value:result["upgrade"] = result["upgrade"]. But if not then we create a new object:result["upgrade"] =. And the same value get our new variablecurObj. In a litle bit expanded version we could write it like follows:if(result[parts[0]] != "undefined") result[parts[0]] = result[parts[0]]; else result[parts[0]] = ; var curObj = result[parts[0]];I hope you can understand it now?
– Bharata
Dec 9 '18 at 9:55
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53308903%2fhow-to-build-a-menu-list-object-recursively-in-javascript%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
8 Answers
8
active
oldest
votes
8 Answers
8
active
oldest
votes
active
oldest
votes
active
oldest
votes
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 through input we start filling it and for last word in every string we assign the value null to object property.
let input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let output = input.reduce((o, d) =>
let keys = d.split('/').filter(d => d)
keys.reduce((k, v, i) =>
k[v] = (i != keys.length - 1)
? k[v] , o)
return o
, )
console.log(output)
how does let keys = d.split('/').filter(d => d) remove the empty entries?
– totalnoob
Nov 25 '18 at 19:23
3
.filter(d => d)represents filter onlytruthyvalue
– Nitish Narang
Nov 25 '18 at 19:26
Falsyvalues Doc - developer.mozilla.org/en-US/docs/Glossary/Falsy
– Nitish Narang
Nov 25 '18 at 19:27
clear and simple. thanks
– totalnoob
Nov 25 '18 at 19:27
you're most welcome @totalnoob
– Nitish Narang
Nov 25 '18 at 19:27
|
show 3 more comments
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 through input we start filling it and for last word in every string we assign the value null to object property.
let input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let output = input.reduce((o, d) =>
let keys = d.split('/').filter(d => d)
keys.reduce((k, v, i) =>
k[v] = (i != keys.length - 1)
? k[v] , o)
return o
, )
console.log(output)
how does let keys = d.split('/').filter(d => d) remove the empty entries?
– totalnoob
Nov 25 '18 at 19:23
3
.filter(d => d)represents filter onlytruthyvalue
– Nitish Narang
Nov 25 '18 at 19:26
Falsyvalues Doc - developer.mozilla.org/en-US/docs/Glossary/Falsy
– Nitish Narang
Nov 25 '18 at 19:27
clear and simple. thanks
– totalnoob
Nov 25 '18 at 19:27
you're most welcome @totalnoob
– Nitish Narang
Nov 25 '18 at 19:27
|
show 3 more comments
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 through input we start filling it and for last word in every string we assign the value null to object property.
let input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let output = input.reduce((o, d) =>
let keys = d.split('/').filter(d => d)
keys.reduce((k, v, i) =>
k[v] = (i != keys.length - 1)
? k[v] , o)
return o
, )
console.log(output)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 through input we start filling it and for last word in every string we assign the value null to object property.
let input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let output = input.reduce((o, d) =>
let keys = d.split('/').filter(d => d)
keys.reduce((k, v, i) =>
k[v] = (i != keys.length - 1)
? k[v] , o)
return o
, )
console.log(output)let input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let output = input.reduce((o, d) =>
let keys = d.split('/').filter(d => d)
keys.reduce((k, v, i) =>
k[v] = (i != keys.length - 1)
? k[v] , o)
return o
, )
console.log(output)let input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let output = input.reduce((o, d) =>
let keys = d.split('/').filter(d => d)
keys.reduce((k, v, i) =>
k[v] = (i != keys.length - 1)
? k[v] , o)
return o
, )
console.log(output)answered Nov 23 '18 at 20:29
Nitish NarangNitish Narang
2,9401815
2,9401815
how does let keys = d.split('/').filter(d => d) remove the empty entries?
– totalnoob
Nov 25 '18 at 19:23
3
.filter(d => d)represents filter onlytruthyvalue
– Nitish Narang
Nov 25 '18 at 19:26
Falsyvalues Doc - developer.mozilla.org/en-US/docs/Glossary/Falsy
– Nitish Narang
Nov 25 '18 at 19:27
clear and simple. thanks
– totalnoob
Nov 25 '18 at 19:27
you're most welcome @totalnoob
– Nitish Narang
Nov 25 '18 at 19:27
|
show 3 more comments
how does let keys = d.split('/').filter(d => d) remove the empty entries?
– totalnoob
Nov 25 '18 at 19:23
3
.filter(d => d)represents filter onlytruthyvalue
– Nitish Narang
Nov 25 '18 at 19:26
Falsyvalues Doc - developer.mozilla.org/en-US/docs/Glossary/Falsy
– Nitish Narang
Nov 25 '18 at 19:27
clear and simple. thanks
– totalnoob
Nov 25 '18 at 19:27
you're most welcome @totalnoob
– Nitish Narang
Nov 25 '18 at 19:27
how does let keys = d.split('/').filter(d => d) remove the empty entries?
– totalnoob
Nov 25 '18 at 19:23
how does let keys = d.split('/').filter(d => d) remove the empty entries?
– totalnoob
Nov 25 '18 at 19:23
3
3
.filter(d => d) represents filter only truthy value– Nitish Narang
Nov 25 '18 at 19:26
.filter(d => d) represents filter only truthy value– Nitish Narang
Nov 25 '18 at 19:26
Falsy values Doc - developer.mozilla.org/en-US/docs/Glossary/Falsy– Nitish Narang
Nov 25 '18 at 19:27
Falsy values Doc - developer.mozilla.org/en-US/docs/Glossary/Falsy– Nitish Narang
Nov 25 '18 at 19:27
clear and simple. thanks
– totalnoob
Nov 25 '18 at 19:27
clear and simple. thanks
– totalnoob
Nov 25 '18 at 19:27
you're most welcome @totalnoob
– Nitish Narang
Nov 25 '18 at 19:27
you're most welcome @totalnoob
– Nitish Narang
Nov 25 '18 at 19:27
|
show 3 more comments
It is as easy as:
root = root[label];
if you change your helper function to:
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
I'd write it as:
const buildMenuMap = menus =>
const root = ;
for(const menu of menus) (curr[key] = ), root);
obj[prop] = null;
return root;
it didn't work when I tried it
– totalnoob
Nov 14 '18 at 21:26
that's awesome. can you tell me why the current code doesn't work even setting root properly?
– totalnoob
Nov 14 '18 at 21:31
@totalnoob becauseaddLabelToMapdoes not go deeper into the map
– Jonas Wilms
Nov 14 '18 at 21:32
when I use root = root[label], on the next loop root is undefined if I print it out
– totalnoob
Nov 14 '18 at 21:38
add a comment |
It is as easy as:
root = root[label];
if you change your helper function to:
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
I'd write it as:
const buildMenuMap = menus =>
const root = ;
for(const menu of menus) (curr[key] = ), root);
obj[prop] = null;
return root;
it didn't work when I tried it
– totalnoob
Nov 14 '18 at 21:26
that's awesome. can you tell me why the current code doesn't work even setting root properly?
– totalnoob
Nov 14 '18 at 21:31
@totalnoob becauseaddLabelToMapdoes not go deeper into the map
– Jonas Wilms
Nov 14 '18 at 21:32
when I use root = root[label], on the next loop root is undefined if I print it out
– totalnoob
Nov 14 '18 at 21:38
add a comment |
It is as easy as:
root = root[label];
if you change your helper function to:
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
I'd write it as:
const buildMenuMap = menus =>
const root = ;
for(const menu of menus) (curr[key] = ), root);
obj[prop] = null;
return root;
It is as easy as:
root = root[label];
if you change your helper function to:
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
I'd write it as:
const buildMenuMap = menus =>
const root = ;
for(const menu of menus) (curr[key] = ), root);
obj[prop] = null;
return root;
edited Nov 14 '18 at 21:31
answered Nov 14 '18 at 21:25
Jonas WilmsJonas Wilms
58k43051
58k43051
it didn't work when I tried it
– totalnoob
Nov 14 '18 at 21:26
that's awesome. can you tell me why the current code doesn't work even setting root properly?
– totalnoob
Nov 14 '18 at 21:31
@totalnoob becauseaddLabelToMapdoes not go deeper into the map
– Jonas Wilms
Nov 14 '18 at 21:32
when I use root = root[label], on the next loop root is undefined if I print it out
– totalnoob
Nov 14 '18 at 21:38
add a comment |
it didn't work when I tried it
– totalnoob
Nov 14 '18 at 21:26
that's awesome. can you tell me why the current code doesn't work even setting root properly?
– totalnoob
Nov 14 '18 at 21:31
@totalnoob becauseaddLabelToMapdoes not go deeper into the map
– Jonas Wilms
Nov 14 '18 at 21:32
when I use root = root[label], on the next loop root is undefined if I print it out
– totalnoob
Nov 14 '18 at 21:38
it didn't work when I tried it
– totalnoob
Nov 14 '18 at 21:26
it didn't work when I tried it
– totalnoob
Nov 14 '18 at 21:26
that's awesome. can you tell me why the current code doesn't work even setting root properly?
– totalnoob
Nov 14 '18 at 21:31
that's awesome. can you tell me why the current code doesn't work even setting root properly?
– totalnoob
Nov 14 '18 at 21:31
@totalnoob because
addLabelToMap does not go deeper into the map– Jonas Wilms
Nov 14 '18 at 21:32
@totalnoob because
addLabelToMap does not go deeper into the map– Jonas Wilms
Nov 14 '18 at 21:32
when I use root = root[label], on the next loop root is undefined if I print it out
– totalnoob
Nov 14 '18 at 21:38
when I use root = root[label], on the next loop root is undefined if I print it out
– totalnoob
Nov 14 '18 at 21:38
add a comment |
Use reduce instead of map. The root will be the accumulator in this case:
const buildMenuMap = menu =>
menu.reduce((root, item) =>
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] , Object.create(null));
Explanation:
For each item in the menu array, we extract the parts by first getting rid of the leading '/' (using slice(1)) and then splitting by '/'.
We then remove the lastPart from this resulting array (the last part is handled separetely from the rest).
For each remaining part in the parts array, we traverse the root array. At each level of traversing, we either return the object at that level acc[part] if it already exists, or we create and return a new one if it doesn't (acc[part] = ).
After we get to the the last level leaf, we use the lastPart to set the value as null.
Notice that we pass Object.create(null) to reduce. Object.create(null) creates a prototypeless object so it will ba safer to use root[someKey] without having to check if someKey is an owned property or not.
Example:
const buildMenuMap = menu =>
menu.reduce((root, item) => (acc[part] = ), root);
leaf[lastPart] = null;
return root;
, Object.create(null));
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let result = buildMenuMap(arr);
console.log(result);
I like this. thanks. can you explain why the original code doesn't work or how I can fix it?
– totalnoob
Nov 14 '18 at 22:33
add a comment |
Use reduce instead of map. The root will be the accumulator in this case:
const buildMenuMap = menu =>
menu.reduce((root, item) =>
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] , Object.create(null));
Explanation:
For each item in the menu array, we extract the parts by first getting rid of the leading '/' (using slice(1)) and then splitting by '/'.
We then remove the lastPart from this resulting array (the last part is handled separetely from the rest).
For each remaining part in the parts array, we traverse the root array. At each level of traversing, we either return the object at that level acc[part] if it already exists, or we create and return a new one if it doesn't (acc[part] = ).
After we get to the the last level leaf, we use the lastPart to set the value as null.
Notice that we pass Object.create(null) to reduce. Object.create(null) creates a prototypeless object so it will ba safer to use root[someKey] without having to check if someKey is an owned property or not.
Example:
const buildMenuMap = menu =>
menu.reduce((root, item) => (acc[part] = ), root);
leaf[lastPart] = null;
return root;
, Object.create(null));
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let result = buildMenuMap(arr);
console.log(result);
I like this. thanks. can you explain why the original code doesn't work or how I can fix it?
– totalnoob
Nov 14 '18 at 22:33
add a comment |
Use reduce instead of map. The root will be the accumulator in this case:
const buildMenuMap = menu =>
menu.reduce((root, item) =>
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] , Object.create(null));
Explanation:
For each item in the menu array, we extract the parts by first getting rid of the leading '/' (using slice(1)) and then splitting by '/'.
We then remove the lastPart from this resulting array (the last part is handled separetely from the rest).
For each remaining part in the parts array, we traverse the root array. At each level of traversing, we either return the object at that level acc[part] if it already exists, or we create and return a new one if it doesn't (acc[part] = ).
After we get to the the last level leaf, we use the lastPart to set the value as null.
Notice that we pass Object.create(null) to reduce. Object.create(null) creates a prototypeless object so it will ba safer to use root[someKey] without having to check if someKey is an owned property or not.
Example:
const buildMenuMap = menu =>
menu.reduce((root, item) => (acc[part] = ), root);
leaf[lastPart] = null;
return root;
, Object.create(null));
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let result = buildMenuMap(arr);
console.log(result);Use reduce instead of map. The root will be the accumulator in this case:
const buildMenuMap = menu =>
menu.reduce((root, item) =>
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] , Object.create(null));
Explanation:
For each item in the menu array, we extract the parts by first getting rid of the leading '/' (using slice(1)) and then splitting by '/'.
We then remove the lastPart from this resulting array (the last part is handled separetely from the rest).
For each remaining part in the parts array, we traverse the root array. At each level of traversing, we either return the object at that level acc[part] if it already exists, or we create and return a new one if it doesn't (acc[part] = ).
After we get to the the last level leaf, we use the lastPart to set the value as null.
Notice that we pass Object.create(null) to reduce. Object.create(null) creates a prototypeless object so it will ba safer to use root[someKey] without having to check if someKey is an owned property or not.
Example:
const buildMenuMap = menu =>
menu.reduce((root, item) => (acc[part] = ), root);
leaf[lastPart] = null;
return root;
, Object.create(null));
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let result = buildMenuMap(arr);
console.log(result);const buildMenuMap = menu =>
menu.reduce((root, item) => (acc[part] = ), root);
leaf[lastPart] = null;
return root;
, Object.create(null));
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let result = buildMenuMap(arr);
console.log(result);const buildMenuMap = menu =>
menu.reduce((root, item) => (acc[part] = ), root);
leaf[lastPart] = null;
return root;
, Object.create(null));
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
let result = buildMenuMap(arr);
console.log(result);edited Nov 14 '18 at 21:36
answered Nov 14 '18 at 21:30
ibrahim mahriribrahim mahrir
22.1k41949
22.1k41949
I like this. thanks. can you explain why the original code doesn't work or how I can fix it?
– totalnoob
Nov 14 '18 at 22:33
add a comment |
I like this. thanks. can you explain why the original code doesn't work or how I can fix it?
– totalnoob
Nov 14 '18 at 22:33
I like this. thanks. can you explain why the original code doesn't work or how I can fix it?
– totalnoob
Nov 14 '18 at 22:33
I like this. thanks. can you explain why the original code doesn't work or how I can fix it?
– totalnoob
Nov 14 '18 at 22:33
add a comment |
You can solve this also with a recursive function in a concise way like this:
let obj=, input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const makeObj = (arr, obj=) =>
let prop = arr.shift()
prop in obj ? null : Object.assign(obj, [prop]: )
arr.length ? makeObj(arr, obj[prop]) : obj[prop] = null
return obj
input.forEach(x => makeObj(x.split('/').filter(Boolean), obj))
console.log(obj)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 common Array.reduce approach.
1
I like this one. I was only starting out with Array.reduce so the other solutions wasn't as easy to understand.
– totalnoob
Dec 9 '18 at 6:22
add a comment |
You can solve this also with a recursive function in a concise way like this:
let obj=, input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const makeObj = (arr, obj=) =>
let prop = arr.shift()
prop in obj ? null : Object.assign(obj, [prop]: )
arr.length ? makeObj(arr, obj[prop]) : obj[prop] = null
return obj
input.forEach(x => makeObj(x.split('/').filter(Boolean), obj))
console.log(obj)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 common Array.reduce approach.
1
I like this one. I was only starting out with Array.reduce so the other solutions wasn't as easy to understand.
– totalnoob
Dec 9 '18 at 6:22
add a comment |
You can solve this also with a recursive function in a concise way like this:
let obj=, input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const makeObj = (arr, obj=) =>
let prop = arr.shift()
prop in obj ? null : Object.assign(obj, [prop]: )
arr.length ? makeObj(arr, obj[prop]) : obj[prop] = null
return obj
input.forEach(x => makeObj(x.split('/').filter(Boolean), obj))
console.log(obj)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 common Array.reduce approach.
You can solve this also with a recursive function in a concise way like this:
let obj=, input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const makeObj = (arr, obj=) =>
let prop = arr.shift()
prop in obj ? null : Object.assign(obj, [prop]: )
arr.length ? makeObj(arr, obj[prop]) : obj[prop] = null
return obj
input.forEach(x => makeObj(x.split('/').filter(Boolean), obj))
console.log(obj)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 common Array.reduce approach.
let obj=, input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const makeObj = (arr, obj=) =>
let prop = arr.shift()
prop in obj ? null : Object.assign(obj, [prop]: )
arr.length ? makeObj(arr, obj[prop]) : obj[prop] = null
return obj
input.forEach(x => makeObj(x.split('/').filter(Boolean), obj))
console.log(obj)let obj=, input = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const makeObj = (arr, obj=) =>
let prop = arr.shift()
prop in obj ? null : Object.assign(obj, [prop]: )
arr.length ? makeObj(arr, obj[prop]) : obj[prop] = null
return obj
input.forEach(x => makeObj(x.split('/').filter(Boolean), obj))
console.log(obj)edited Dec 9 '18 at 6:15
answered Dec 9 '18 at 5:42
AkrionAkrion
9,45511224
9,45511224
1
I like this one. I was only starting out with Array.reduce so the other solutions wasn't as easy to understand.
– totalnoob
Dec 9 '18 at 6:22
add a comment |
1
I like this one. I was only starting out with Array.reduce so the other solutions wasn't as easy to understand.
– totalnoob
Dec 9 '18 at 6:22
1
1
I like this one. I was only starting out with Array.reduce so the other solutions wasn't as easy to understand.
– totalnoob
Dec 9 '18 at 6:22
I like this one. I was only starting out with Array.reduce so the other solutions wasn't as easy to understand.
– totalnoob
Dec 9 '18 at 6:22
add a comment |
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 of root gets initialised to "" and label is swipes.
.forEach((element) =>
let root = map[element[0]]
So then you get root[label] is undefined and so the new root is undefined.
Second, you are using map everywhere as it is.
const addLabelToMap = (root, label) =>
if(!map[root]) map[root] = ;
if(!map[root][label]) map[root][label] = ;
Instead you should be taking it as a parameter, for you to be able to do a recursion.
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
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.
add a comment |
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 of root gets initialised to "" and label is swipes.
.forEach((element) =>
let root = map[element[0]]
So then you get root[label] is undefined and so the new root is undefined.
Second, you are using map everywhere as it is.
const addLabelToMap = (root, label) =>
if(!map[root]) map[root] = ;
if(!map[root][label]) map[root][label] = ;
Instead you should be taking it as a parameter, for you to be able to do a recursion.
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
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.
add a comment |
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 of root gets initialised to "" and label is swipes.
.forEach((element) =>
let root = map[element[0]]
So then you get root[label] is undefined and so the new root is undefined.
Second, you are using map everywhere as it is.
const addLabelToMap = (root, label) =>
if(!map[root]) map[root] = ;
if(!map[root][label]) map[root][label] = ;
Instead you should be taking it as a parameter, for you to be able to do a recursion.
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
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.
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 of root gets initialised to "" and label is swipes.
.forEach((element) =>
let root = map[element[0]]
So then you get root[label] is undefined and so the new root is undefined.
Second, you are using map everywhere as it is.
const addLabelToMap = (root, label) =>
if(!map[root]) map[root] = ;
if(!map[root][label]) map[root][label] = ;
Instead you should be taking it as a parameter, for you to be able to do a recursion.
const addLabelToMap = (root, label) =>
if(!root[label]) root[label] = ;
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.
answered Nov 18 '18 at 15:48
TheChetanTheChetan
2,35811932
2,35811932
add a comment |
add a comment |
Try this as a holistic solution:
const menu = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const deepMerge = (target, source) =>
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (let key of Object.keys(source))
if (source[key] instanceof Object && key in target) Object.assign(source[key], deepMerge(target[key], source[key]))
// Join `target` and modified `source`
Object.assign(target ;
const buildMenuMap = menu =>
return menu
.map(item => item.split('/').splice(1))
// The `root` value is the object that we will be merging all directories into
.reduce((root, directory) =>
// Iterates backwards through each directory array, stacking the previous accumulated object into the current one
const branch = directory.slice().reverse().reduce((acc, cur) => const obj = ; obj[cur] = acc; return obj;,null);
// Uses the `deepMerge()` method to stitch together the accumulated `root` object with the newly constructed `branch` object.
return deepMerge(root, branch);
, );
;
buildMenuMap(menu);
Note: The deep merge solution was taken from @ahtcx on GitHubGist
add a comment |
Try this as a holistic solution:
const menu = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const deepMerge = (target, source) =>
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (let key of Object.keys(source))
if (source[key] instanceof Object && key in target) Object.assign(source[key], deepMerge(target[key], source[key]))
// Join `target` and modified `source`
Object.assign(target ;
const buildMenuMap = menu =>
return menu
.map(item => item.split('/').splice(1))
// The `root` value is the object that we will be merging all directories into
.reduce((root, directory) =>
// Iterates backwards through each directory array, stacking the previous accumulated object into the current one
const branch = directory.slice().reverse().reduce((acc, cur) => const obj = ; obj[cur] = acc; return obj;,null);
// Uses the `deepMerge()` method to stitch together the accumulated `root` object with the newly constructed `branch` object.
return deepMerge(root, branch);
, );
;
buildMenuMap(menu);
Note: The deep merge solution was taken from @ahtcx on GitHubGist
add a comment |
Try this as a holistic solution:
const menu = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const deepMerge = (target, source) =>
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (let key of Object.keys(source))
if (source[key] instanceof Object && key in target) Object.assign(source[key], deepMerge(target[key], source[key]))
// Join `target` and modified `source`
Object.assign(target ;
const buildMenuMap = menu =>
return menu
.map(item => item.split('/').splice(1))
// The `root` value is the object that we will be merging all directories into
.reduce((root, directory) =>
// Iterates backwards through each directory array, stacking the previous accumulated object into the current one
const branch = directory.slice().reverse().reduce((acc, cur) => const obj = ; obj[cur] = acc; return obj;,null);
// Uses the `deepMerge()` method to stitch together the accumulated `root` object with the newly constructed `branch` object.
return deepMerge(root, branch);
, );
;
buildMenuMap(menu);
Note: The deep merge solution was taken from @ahtcx on GitHubGist
Try this as a holistic solution:
const menu = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
const deepMerge = (target, source) =>
// Iterate through `source` properties and if an `Object` set property to merge of `target` and `source` properties
for (let key of Object.keys(source))
if (source[key] instanceof Object && key in target) Object.assign(source[key], deepMerge(target[key], source[key]))
// Join `target` and modified `source`
Object.assign(target ;
const buildMenuMap = menu =>
return menu
.map(item => item.split('/').splice(1))
// The `root` value is the object that we will be merging all directories into
.reduce((root, directory) =>
// Iterates backwards through each directory array, stacking the previous accumulated object into the current one
const branch = directory.slice().reverse().reduce((acc, cur) => const obj = ; obj[cur] = acc; return obj;,null);
// Uses the `deepMerge()` method to stitch together the accumulated `root` object with the newly constructed `branch` object.
return deepMerge(root, branch);
, );
;
buildMenuMap(menu);
Note: The deep merge solution was taken from @ahtcx on GitHubGist
edited Nov 19 '18 at 21:02
answered Nov 19 '18 at 20:54
astangeloastangelo
958
958
add a comment |
add a comment |
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
obj - the current root object / node
ar - array of child hierarchy
and returns the updated object
Logic
- function pops the first value (
let key = ar.shift()) as key and add / update in the object (obj[key] = obj[key] || ;). - If there is child hierarchy of current object (
if(ar.length)), recursively call the function to update the object till the end (addLabelToMap(obj[key], ar)). - Else (no further child hierarchy), check whether the object has some hierarchy (
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/youngalong with existing, theelse ifblock can be simplified to a simpleelseblock. - The object has been update, return the final updated object
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
function addLabelToMap(obj, ar)
let key = ar.shift();
obj[key] = obj[key]
function buildMenuMap(ar)
return ar.reduce((a,c) => addLabelToMap(a,c.substring(1).split("/")), );
console.log(buildMenuMap(arr));add a comment |
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
obj - the current root object / node
ar - array of child hierarchy
and returns the updated object
Logic
- function pops the first value (
let key = ar.shift()) as key and add / update in the object (obj[key] = obj[key] || ;). - If there is child hierarchy of current object (
if(ar.length)), recursively call the function to update the object till the end (addLabelToMap(obj[key], ar)). - Else (no further child hierarchy), check whether the object has some hierarchy (
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/youngalong with existing, theelse ifblock can be simplified to a simpleelseblock. - The object has been update, return the final updated object
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
function addLabelToMap(obj, ar)
let key = ar.shift();
obj[key] = obj[key]
function buildMenuMap(ar)
return ar.reduce((a,c) => addLabelToMap(a,c.substring(1).split("/")), );
console.log(buildMenuMap(arr));add a comment |
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
obj - the current root object / node
ar - array of child hierarchy
and returns the updated object
Logic
- function pops the first value (
let key = ar.shift()) as key and add / update in the object (obj[key] = obj[key] || ;). - If there is child hierarchy of current object (
if(ar.length)), recursively call the function to update the object till the end (addLabelToMap(obj[key], ar)). - Else (no further child hierarchy), check whether the object has some hierarchy (
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/youngalong with existing, theelse ifblock can be simplified to a simpleelseblock. - The object has been update, return the final updated object
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
function addLabelToMap(obj, ar)
let key = ar.shift();
obj[key] = obj[key]
function buildMenuMap(ar)
return ar.reduce((a,c) => addLabelToMap(a,c.substring(1).split("/")), );
console.log(buildMenuMap(arr));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
obj - the current root object / node
ar - array of child hierarchy
and returns the updated object
Logic
- function pops the first value (
let key = ar.shift()) as key and add / update in the object (obj[key] = obj[key] || ;). - If there is child hierarchy of current object (
if(ar.length)), recursively call the function to update the object till the end (addLabelToMap(obj[key], ar)). - Else (no further child hierarchy), check whether the object has some hierarchy (
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/youngalong with existing, theelse ifblock can be simplified to a simpleelseblock. - The object has been update, return the final updated object
let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
function addLabelToMap(obj, ar)
let key = ar.shift();
obj[key] = obj[key]
function buildMenuMap(ar)
return ar.reduce((a,c) => addLabelToMap(a,c.substring(1).split("/")), );
console.log(buildMenuMap(arr));let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
function addLabelToMap(obj, ar)
let key = ar.shift();
obj[key] = obj[key]
function buildMenuMap(ar)
return ar.reduce((a,c) => addLabelToMap(a,c.substring(1).split("/")), );
console.log(buildMenuMap(arr));let arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
function addLabelToMap(obj, ar)
let key = ar.shift();
obj[key] = obj[key]
function buildMenuMap(ar)
return ar.reduce((a,c) => addLabelToMap(a,c.substring(1).split("/")), );
console.log(buildMenuMap(arr));answered Nov 22 '18 at 15:25
Nikhil AggarwalNikhil Aggarwal
24k32747
24k32747
add a comment |
add a comment |
Citate from bounty description:
The current answers do not contain enough detail.
I think you do not understand how it works in current answers. Because of this I will provide you two solutions: one alternative solution and one expanded solution with Array.reduce() function.
Alternative solution with for loops
The explanation of code see in the code comments.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ;
//if you want to have it shorter you can write for(var i = arr.length; i--;) too
for(var i = 0; i < arr.length; i++)
console.log(JSON.stringify(result, null, 4));But if you did not understand it then see this code:
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ,
parts = arr[2].slice(1).split('/'),
curObj = result[parts[0]] = ;
curObj[parts[1]] = parts[1+1] ? : null;
console.log(JSON.stringify(result, null, 4));Expanded solution with Array.reduce()
In this solution I use the code from user Nitish Narang in expanded version with some explanation in comments and console output – so yuo can see in console what the code does. My recommendation: if you do not understand the code with arrow functions then write it full with normal functions and appropriate variable names which explain themselves. We (humans) need some pictures to imagine all things. If we have only some short variable names then it is difficalt to imagine und undestand all this. I have also a little bit «shorted» his code.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
var result = arr.reduce(function(acc0, curVal, curIdx)
console.log('n' + curIdx + ': ------------n'
+ JSON.stringify(acc0, null, 4));
var keys = curVal.slice(1).split('/');
keys.reduce(function(acc1, currentValue, currentIndex)
acc1[currentValue] = keys[currentIndex+1]
? acc1[currentValue] , acc0); //acc0 - initialValue is the same object, but it is empty only in first cycle
return acc0
, ); // - initialValue is empty object
console.log('n------result------n'
+ JSON.stringify(result, null, 4));
1
what does curObj = result[parts[0]] = result[parts[0]] || ; do?
– totalnoob
Dec 9 '18 at 3:16
@totalnoob, if we have inparts[0]for example the valueupgrade(it could be alsosocial) then we ask: isresult["upgrade"] != "undefined"and if yes, then we take the same value:result["upgrade"] = result["upgrade"]. But if not then we create a new object:result["upgrade"] =. And the same value get our new variablecurObj. In a litle bit expanded version we could write it like follows:if(result[parts[0]] != "undefined") result[parts[0]] = result[parts[0]]; else result[parts[0]] = ; var curObj = result[parts[0]];I hope you can understand it now?
– Bharata
Dec 9 '18 at 9:55
add a comment |
Citate from bounty description:
The current answers do not contain enough detail.
I think you do not understand how it works in current answers. Because of this I will provide you two solutions: one alternative solution and one expanded solution with Array.reduce() function.
Alternative solution with for loops
The explanation of code see in the code comments.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ;
//if you want to have it shorter you can write for(var i = arr.length; i--;) too
for(var i = 0; i < arr.length; i++)
console.log(JSON.stringify(result, null, 4));But if you did not understand it then see this code:
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ,
parts = arr[2].slice(1).split('/'),
curObj = result[parts[0]] = ;
curObj[parts[1]] = parts[1+1] ? : null;
console.log(JSON.stringify(result, null, 4));Expanded solution with Array.reduce()
In this solution I use the code from user Nitish Narang in expanded version with some explanation in comments and console output – so yuo can see in console what the code does. My recommendation: if you do not understand the code with arrow functions then write it full with normal functions and appropriate variable names which explain themselves. We (humans) need some pictures to imagine all things. If we have only some short variable names then it is difficalt to imagine und undestand all this. I have also a little bit «shorted» his code.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
var result = arr.reduce(function(acc0, curVal, curIdx)
console.log('n' + curIdx + ': ------------n'
+ JSON.stringify(acc0, null, 4));
var keys = curVal.slice(1).split('/');
keys.reduce(function(acc1, currentValue, currentIndex)
acc1[currentValue] = keys[currentIndex+1]
? acc1[currentValue] , acc0); //acc0 - initialValue is the same object, but it is empty only in first cycle
return acc0
, ); // - initialValue is empty object
console.log('n------result------n'
+ JSON.stringify(result, null, 4));
1
what does curObj = result[parts[0]] = result[parts[0]] || ; do?
– totalnoob
Dec 9 '18 at 3:16
@totalnoob, if we have inparts[0]for example the valueupgrade(it could be alsosocial) then we ask: isresult["upgrade"] != "undefined"and if yes, then we take the same value:result["upgrade"] = result["upgrade"]. But if not then we create a new object:result["upgrade"] =. And the same value get our new variablecurObj. In a litle bit expanded version we could write it like follows:if(result[parts[0]] != "undefined") result[parts[0]] = result[parts[0]]; else result[parts[0]] = ; var curObj = result[parts[0]];I hope you can understand it now?
– Bharata
Dec 9 '18 at 9:55
add a comment |
Citate from bounty description:
The current answers do not contain enough detail.
I think you do not understand how it works in current answers. Because of this I will provide you two solutions: one alternative solution and one expanded solution with Array.reduce() function.
Alternative solution with for loops
The explanation of code see in the code comments.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ;
//if you want to have it shorter you can write for(var i = arr.length; i--;) too
for(var i = 0; i < arr.length; i++)
console.log(JSON.stringify(result, null, 4));But if you did not understand it then see this code:
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ,
parts = arr[2].slice(1).split('/'),
curObj = result[parts[0]] = ;
curObj[parts[1]] = parts[1+1] ? : null;
console.log(JSON.stringify(result, null, 4));Expanded solution with Array.reduce()
In this solution I use the code from user Nitish Narang in expanded version with some explanation in comments and console output – so yuo can see in console what the code does. My recommendation: if you do not understand the code with arrow functions then write it full with normal functions and appropriate variable names which explain themselves. We (humans) need some pictures to imagine all things. If we have only some short variable names then it is difficalt to imagine und undestand all this. I have also a little bit «shorted» his code.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
var result = arr.reduce(function(acc0, curVal, curIdx)
console.log('n' + curIdx + ': ------------n'
+ JSON.stringify(acc0, null, 4));
var keys = curVal.slice(1).split('/');
keys.reduce(function(acc1, currentValue, currentIndex)
acc1[currentValue] = keys[currentIndex+1]
? acc1[currentValue] , acc0); //acc0 - initialValue is the same object, but it is empty only in first cycle
return acc0
, ); // - initialValue is empty object
console.log('n------result------n'
+ JSON.stringify(result, null, 4));
Citate from bounty description:
The current answers do not contain enough detail.
I think you do not understand how it works in current answers. Because of this I will provide you two solutions: one alternative solution and one expanded solution with Array.reduce() function.
Alternative solution with for loops
The explanation of code see in the code comments.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ;
//if you want to have it shorter you can write for(var i = arr.length; i--;) too
for(var i = 0; i < arr.length; i++)
console.log(JSON.stringify(result, null, 4));But if you did not understand it then see this code:
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ,
parts = arr[2].slice(1).split('/'),
curObj = result[parts[0]] = ;
curObj[parts[1]] = parts[1+1] ? : null;
console.log(JSON.stringify(result, null, 4));Expanded solution with Array.reduce()
In this solution I use the code from user Nitish Narang in expanded version with some explanation in comments and console output – so yuo can see in console what the code does. My recommendation: if you do not understand the code with arrow functions then write it full with normal functions and appropriate variable names which explain themselves. We (humans) need some pictures to imagine all things. If we have only some short variable names then it is difficalt to imagine und undestand all this. I have also a little bit «shorted» his code.
var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
var result = arr.reduce(function(acc0, curVal, curIdx)
console.log('n' + curIdx + ': ------------n'
+ JSON.stringify(acc0, null, 4));
var keys = curVal.slice(1).split('/');
keys.reduce(function(acc1, currentValue, currentIndex)
acc1[currentValue] = keys[currentIndex+1]
? acc1[currentValue] , acc0); //acc0 - initialValue is the same object, but it is empty only in first cycle
return acc0
, ); // - initialValue is empty object
console.log('n------result------n'
+ JSON.stringify(result, null, 4));var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ;
//if you want to have it shorter you can write for(var i = arr.length; i--;) too
for(var i = 0; i < arr.length; i++)
console.log(JSON.stringify(result, null, 4));var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ;
//if you want to have it shorter you can write for(var i = arr.length; i--;) too
for(var i = 0; i < arr.length; i++)
console.log(JSON.stringify(result, null, 4));var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ,
parts = arr[2].slice(1).split('/'),
curObj = result[parts[0]] = ;
curObj[parts[1]] = parts[1+1] ? : null;
console.log(JSON.stringify(result, null, 4));var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'],
result = ,
parts = arr[2].slice(1).split('/'),
curObj = result[parts[0]] = ;
curObj[parts[1]] = parts[1+1] ? : null;
console.log(JSON.stringify(result, null, 4));var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
var result = arr.reduce(function(acc0, curVal, curIdx)
console.log('n' + curIdx + ': ------------n'
+ JSON.stringify(acc0, null, 4));
var keys = curVal.slice(1).split('/');
keys.reduce(function(acc1, currentValue, currentIndex)
acc1[currentValue] = keys[currentIndex+1]
? acc1[currentValue] , acc0); //acc0 - initialValue is the same object, but it is empty only in first cycle
return acc0
, ); // - initialValue is empty object
console.log('n------result------n'
+ JSON.stringify(result, null, 4));var arr = ['/social/swipes/women', '/social/swipes/men', '/upgrade/premium'];
var result = arr.reduce(function(acc0, curVal, curIdx)
console.log('n' + curIdx + ': ------------n'
+ JSON.stringify(acc0, null, 4));
var keys = curVal.slice(1).split('/');
keys.reduce(function(acc1, currentValue, currentIndex)
acc1[currentValue] = keys[currentIndex+1]
? acc1[currentValue] , acc0); //acc0 - initialValue is the same object, but it is empty only in first cycle
return acc0
, ); // - initialValue is empty object
console.log('n------result------n'
+ JSON.stringify(result, null, 4));answered Dec 9 '18 at 0:51
BharataBharata
9,21261431
9,21261431
1
what does curObj = result[parts[0]] = result[parts[0]] || ; do?
– totalnoob
Dec 9 '18 at 3:16
@totalnoob, if we have inparts[0]for example the valueupgrade(it could be alsosocial) then we ask: isresult["upgrade"] != "undefined"and if yes, then we take the same value:result["upgrade"] = result["upgrade"]. But if not then we create a new object:result["upgrade"] =. And the same value get our new variablecurObj. In a litle bit expanded version we could write it like follows:if(result[parts[0]] != "undefined") result[parts[0]] = result[parts[0]]; else result[parts[0]] = ; var curObj = result[parts[0]];I hope you can understand it now?
– Bharata
Dec 9 '18 at 9:55
add a comment |
1
what does curObj = result[parts[0]] = result[parts[0]] || ; do?
– totalnoob
Dec 9 '18 at 3:16
@totalnoob, if we have inparts[0]for example the valueupgrade(it could be alsosocial) then we ask: isresult["upgrade"] != "undefined"and if yes, then we take the same value:result["upgrade"] = result["upgrade"]. But if not then we create a new object:result["upgrade"] =. And the same value get our new variablecurObj. In a litle bit expanded version we could write it like follows:if(result[parts[0]] != "undefined") result[parts[0]] = result[parts[0]]; else result[parts[0]] = ; var curObj = result[parts[0]];I hope you can understand it now?
– Bharata
Dec 9 '18 at 9:55
1
1
what does curObj = result[parts[0]] = result[parts[0]] || ; do?
– totalnoob
Dec 9 '18 at 3:16
what does curObj = result[parts[0]] = result[parts[0]] || ; do?
– totalnoob
Dec 9 '18 at 3:16
@totalnoob, if we have in
parts[0] for example the value upgrade (it could be also social) then we ask: is result["upgrade"] != "undefined" and if yes, then we take the same value: result["upgrade"] = result["upgrade"]. But if not then we create a new object: result["upgrade"] = . And the same value get our new variable curObj. In a litle bit expanded version we could write it like follows: if(result[parts[0]] != "undefined") result[parts[0]] = result[parts[0]]; else result[parts[0]] = ; var curObj = result[parts[0]]; I hope you can understand it now?– Bharata
Dec 9 '18 at 9:55
@totalnoob, if we have in
parts[0] for example the value upgrade (it could be also social) then we ask: is result["upgrade"] != "undefined" and if yes, then we take the same value: result["upgrade"] = result["upgrade"]. But if not then we create a new object: result["upgrade"] = . And the same value get our new variable curObj. In a litle bit expanded version we could write it like follows: if(result[parts[0]] != "undefined") result[parts[0]] = result[parts[0]]; else result[parts[0]] = ; var curObj = result[parts[0]]; I hope you can understand it now?– Bharata
Dec 9 '18 at 9:55
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53308903%2fhow-to-build-a-menu-list-object-recursively-in-javascript%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Shouldn't
menbe in thelikesobject not theswipesobject?– ibrahim mahrir
Nov 14 '18 at 21:22
I've just edited it
– totalnoob
Nov 14 '18 at 21:23