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]] || "";
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.
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]] || "";
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.
javascript arrays ecmascript-6 javascript-objects arrow-functions
1
Shouldn'tmen
be in thelikes
object not theswipes
object?
– 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]] || "";
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.
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]] || "";
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.
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);
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);
javascript arrays ecmascript-6 javascript-objects arrow-functions
javascript arrays ecmascript-6 javascript-objects arrow-functions
edited Dec 9 '18 at 1:01


Bharata
8,09661131
8,09661131
asked Nov 14 '18 at 21:20
totalnoobtotalnoob
3211631
3211631
1
Shouldn'tmen
be in thelikes
object not theswipes
object?
– 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'tmen
be in thelikes
object not theswipes
object?
– 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] || {}
: null
return 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 onlytruthy
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
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) {
const keys = menu.split("/").slice(1);
const prop = keys.pop();
const obj = keys.reduce((curr, key) => curr[key] || (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 becauseaddLabelToMap
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
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] || (acc[part] = {}), root);
leaf[lastPart] = null;
return root;
}, 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 split
ting 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) => {
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] || (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]] || "";
...
root = root[label];
}
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 || {}, source)
return 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/young
along with existing, theelse if
block can be simplified to a simpleelse
block. - 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] || {};
if(ar.length) addLabelToMap(obj[key], ar);
else if(!Object.keys(obj[key]).length) obj[key] = null;
return obj;
}
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++)
{
var parts = arr[i].slice(1).split('/'),
//if we have already one object then we take it. If not the we create new one:
curObj = result[parts[0]] = result[parts[0]] || {};
for(var k = 1; k < parts.length; k++)
{
//if we have next part
if(parts[k+1])
//if we have already one object then we take it. If not the we create new one:
curObj[parts[k]] = curObj[parts[k]] || {};
//if we do not have next part
else curObj[parts[k]] = null;
//or if-else block in one line:
//curObj[parts[k]] = parts[k+1] ? (curObj[parts[k]] || {}) : null;
//link to next object:
curObj = curObj[parts[k]];
}
}
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] || {}
: null;
return 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] || {}
: null
return 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 onlytruthy
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
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] || {}
: null
return 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 onlytruthy
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
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] || {}
: null
return 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] || {}
: null
return 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] || {}
: null
return 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] || {}
: null
return k[v]
}, o)
return o
}, {})
console.log(output)
answered Nov 23 '18 at 20:29


Nitish NarangNitish Narang
2,948815
2,948815
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 onlytruthy
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
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 onlytruthy
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
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) {
const keys = menu.split("/").slice(1);
const prop = keys.pop();
const obj = keys.reduce((curr, key) => curr[key] || (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 becauseaddLabelToMap
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
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) {
const keys = menu.split("/").slice(1);
const prop = keys.pop();
const obj = keys.reduce((curr, key) => curr[key] || (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 becauseaddLabelToMap
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
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) {
const keys = menu.split("/").slice(1);
const prop = keys.pop();
const obj = keys.reduce((curr, key) => curr[key] || (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) {
const keys = menu.split("/").slice(1);
const prop = keys.pop();
const obj = keys.reduce((curr, key) => curr[key] || (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
55.9k42851
55.9k42851
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 becauseaddLabelToMap
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
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 becauseaddLabelToMap
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
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] || (acc[part] = {}), root);
leaf[lastPart] = null;
return root;
}, 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 split
ting 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) => {
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] || (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] || (acc[part] = {}), root);
leaf[lastPart] = null;
return root;
}, 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 split
ting 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) => {
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] || (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] || (acc[part] = {}), root);
leaf[lastPart] = null;
return root;
}, 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 split
ting 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) => {
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] || (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] || (acc[part] = {}), root);
leaf[lastPart] = null;
return root;
}, 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 split
ting 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) => {
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] || (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) => {
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] || (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) => {
let parts = item.slice(1).split("/");
let lastPart = parts.pop();
let leaf = parts.reduce((acc, part) => acc[part] || (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
21.8k41847
21.8k41847
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,43211224
9,43211224
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]] || "";
...
root = root[label];
}
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]] || "";
...
root = root[label];
}
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]] || "";
...
root = root[label];
}
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]] || "";
...
root = root[label];
}
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,30611631
2,30611631
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 || {}, source)
return 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 || {}, source)
return 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 || {}, source)
return 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 || {}, source)
return 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
858
858
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/young
along with existing, theelse if
block can be simplified to a simpleelse
block. - 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] || {};
if(ar.length) addLabelToMap(obj[key], ar);
else if(!Object.keys(obj[key]).length) obj[key] = null;
return obj;
}
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/young
along with existing, theelse if
block can be simplified to a simpleelse
block. - 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] || {};
if(ar.length) addLabelToMap(obj[key], ar);
else if(!Object.keys(obj[key]).length) obj[key] = null;
return obj;
}
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/young
along with existing, theelse if
block can be simplified to a simpleelse
block. - 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] || {};
if(ar.length) addLabelToMap(obj[key], ar);
else if(!Object.keys(obj[key]).length) obj[key] = null;
return obj;
}
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/young
along with existing, theelse if
block can be simplified to a simpleelse
block. - 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] || {};
if(ar.length) addLabelToMap(obj[key], ar);
else if(!Object.keys(obj[key]).length) obj[key] = null;
return obj;
}
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] || {};
if(ar.length) addLabelToMap(obj[key], ar);
else if(!Object.keys(obj[key]).length) obj[key] = null;
return obj;
}
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] || {};
if(ar.length) addLabelToMap(obj[key], ar);
else if(!Object.keys(obj[key]).length) obj[key] = null;
return obj;
}
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
23.7k32747
23.7k32747
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++)
{
var parts = arr[i].slice(1).split('/'),
//if we have already one object then we take it. If not the we create new one:
curObj = result[parts[0]] = result[parts[0]] || {};
for(var k = 1; k < parts.length; k++)
{
//if we have next part
if(parts[k+1])
//if we have already one object then we take it. If not the we create new one:
curObj[parts[k]] = curObj[parts[k]] || {};
//if we do not have next part
else curObj[parts[k]] = null;
//or if-else block in one line:
//curObj[parts[k]] = parts[k+1] ? (curObj[parts[k]] || {}) : null;
//link to next object:
curObj = curObj[parts[k]];
}
}
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] || {}
: null;
return 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++)
{
var parts = arr[i].slice(1).split('/'),
//if we have already one object then we take it. If not the we create new one:
curObj = result[parts[0]] = result[parts[0]] || {};
for(var k = 1; k < parts.length; k++)
{
//if we have next part
if(parts[k+1])
//if we have already one object then we take it. If not the we create new one:
curObj[parts[k]] = curObj[parts[k]] || {};
//if we do not have next part
else curObj[parts[k]] = null;
//or if-else block in one line:
//curObj[parts[k]] = parts[k+1] ? (curObj[parts[k]] || {}) : null;
//link to next object:
curObj = curObj[parts[k]];
}
}
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] || {}
: null;
return 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++)
{
var parts = arr[i].slice(1).split('/'),
//if we have already one object then we take it. If not the we create new one:
curObj = result[parts[0]] = result[parts[0]] || {};
for(var k = 1; k < parts.length; k++)
{
//if we have next part
if(parts[k+1])
//if we have already one object then we take it. If not the we create new one:
curObj[parts[k]] = curObj[parts[k]] || {};
//if we do not have next part
else curObj[parts[k]] = null;
//or if-else block in one line:
//curObj[parts[k]] = parts[k+1] ? (curObj[parts[k]] || {}) : null;
//link to next object:
curObj = curObj[parts[k]];
}
}
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] || {}
: null;
return 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++)
{
var parts = arr[i].slice(1).split('/'),
//if we have already one object then we take it. If not the we create new one:
curObj = result[parts[0]] = result[parts[0]] || {};
for(var k = 1; k < parts.length; k++)
{
//if we have next part
if(parts[k+1])
//if we have already one object then we take it. If not the we create new one:
curObj[parts[k]] = curObj[parts[k]] || {};
//if we do not have next part
else curObj[parts[k]] = null;
//or if-else block in one line:
//curObj[parts[k]] = parts[k+1] ? (curObj[parts[k]] || {}) : null;
//link to next object:
curObj = curObj[parts[k]];
}
}
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] || {}
: null;
return 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++)
{
var parts = arr[i].slice(1).split('/'),
//if we have already one object then we take it. If not the we create new one:
curObj = result[parts[0]] = result[parts[0]] || {};
for(var k = 1; k < parts.length; k++)
{
//if we have next part
if(parts[k+1])
//if we have already one object then we take it. If not the we create new one:
curObj[parts[k]] = curObj[parts[k]] || {};
//if we do not have next part
else curObj[parts[k]] = null;
//or if-else block in one line:
//curObj[parts[k]] = parts[k+1] ? (curObj[parts[k]] || {}) : null;
//link to next object:
curObj = curObj[parts[k]];
}
}
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++)
{
var parts = arr[i].slice(1).split('/'),
//if we have already one object then we take it. If not the we create new one:
curObj = result[parts[0]] = result[parts[0]] || {};
for(var k = 1; k < parts.length; k++)
{
//if we have next part
if(parts[k+1])
//if we have already one object then we take it. If not the we create new one:
curObj[parts[k]] = curObj[parts[k]] || {};
//if we do not have next part
else curObj[parts[k]] = null;
//or if-else block in one line:
//curObj[parts[k]] = parts[k+1] ? (curObj[parts[k]] || {}) : null;
//link to next object:
curObj = curObj[parts[k]];
}
}
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] || {}
: null;
return 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] || {}
: null;
return 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
8,09661131
8,09661131
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
men
be in thelikes
object not theswipes
object?– ibrahim mahrir
Nov 14 '18 at 21:22
I've just edited it
– totalnoob
Nov 14 '18 at 21:23