Reputation: 481
I want to get the output as:
options: [{
id: 'parent1',
label: 'parent1',
children: [{
id: 'child1',
label: 'child1',
children: [{
id: 'lastChild1',
label: 'lastChild1',
}]
}, {
id: 'child2',
label: 'child2',
children: [{
id: 'lastChild2',
label: 'lastChild2',
}]
}]
}]
However, the output from getOptions()
is in the format where the children property array of parent1
object contain only the second child in the above format, first child is kind of overwritten or not visited by the for..in
loop in the recurseList()
.
Can anyone fix the code to output the first child child1
along with child2
as well, basically any level of nesting.
var myObj = {
parent1: {
child1: {
lastChild1: { test: 'cool'}
},
child2: {
lastChild2: { test: 'cool'}
}
},
parent2: {
child2_1: {
lastChild2_1: { test: 'cool'}
},
child2_2: {
lastChild2_2: { test: 'cool'}
}
}
}
var result = getOptions(myObj)
console.log('result', result)
function getOptions(obj) {
var options = []
for (key in obj) {
var data = recurseList(obj[key])
options.push(data)
}
return options
}
function recurseList(obj) {
let data= {}
let option= []
for (key in obj) {
data.id = key
data.label = key
data.children = []
if(obj[key] instanceof Object) {
var val = recurseList(obj[key])
data.children.push(val)
}
}
return data
}
Actually, I want data from my firebase real-time-database as show in the image below:
to be in the format for this vuejs plugin: https://vue-treeselect.js.org
Thanks
Upvotes: 0
Views: 1281
Reputation: 135227
variable depth
Here's an adaptation to Scott's (wonderful) answer that allows you to convert your nested structure to a user-controlled depth; convertUntil
-
o
, is not an object, (base case) there is nothing to convert, return the inputtest
, stop nesting and return children: {}
test
. Map over the input object and create a level of our output structure. Recur convertUntil
on each object value.Numbered comments above correspond to the code below -
const identity = x =>
x
const convertUntil = (test = identity, o = {}) =>
Object (o) !== o // 1
? o
: test (o) // 2
? {}
: Object
.entries (o) // 3
.map
( ([ k, v ]) =>
({ id: k, label: k, children: convertUntil (test, v) })
)
const myObj =
{ parent1:
{ child1: { lastChild1: { test: 'cool' } }
, child2: { lastChild2: { test: 'cool' } }
}
, parent2:
{ child2_1: { lastChild2_1: { test: 'cool' } }
, child2_2: { lastChild2_2: { test: 'cool' } }
}
}
console .log (convertUntil (x => x.test === "cool", myObj))
While I prefer this more consistent data structure, I can understand if you don't like the empty children: {}
created above. With a slight modification, we can remove empty children
properties -
const identity = x =>
x
const convertUntil = (test = identity, o = {}) =>
Object (o) !== o
? o
: Object
.entries (o)
.map
( ([ k, v ]) =>
test (v) // <-- test here
? { id: k, label: k } // <-- no children
: { id: k, label: k, children: convertUntil (test, v) }
)
const myObj =
{ parent1:
{ child1: { lastChild1: { test: 'cool' } }
, child2: { lastChild2: { test: 'cool' } }
}
, parent2:
{ child2_1: { lastChild2_1: { test: 'cool' } }
, child2_2: { lastChild2_2: { test: 'cool' } }
}
}
console .log (convertUntil (x => x.test === "cool", myObj))
But watch out for -
console .log (convertUntil (x => x.test === "cool", { test: "cool" }))
// [ { id: "test", label: "test", children: "cool" } ]
fixed depth
Another option would be to convert the nested structure to a specified depth
-
const identity = x =>
x
const convert = (o = {}, depth = 0) =>
Object (o) !== o
? o
: Object
.entries (o)
.map
( ([ k, v ]) =>
depth === 0 // <-- depth test
? { id: k, label: k } // <-- no children
: { id: k, label: k, children: convert (v, depth - 1) } // <-- depth minus one
)
const myObj =
{ parent1:
{ child1: { lastChild1: { test: 'cool' } }
, child2: { lastChild2: { test: 'cool' } }
}
, parent2:
{ child2_1: { lastChild2_1: { test: 'cool' } }
, child2_2: { lastChild2_2: { test: 'cool' } }
}
}
// show various depths
for (const d of [ 0, 1, 2 ])
console .log (`depth: ${d}`, convert (myObj, d))
combined technique
Per Scott's comment, the techniques can be combined into a single solution. This allows the user to continue conversion based on the object's properties or a specified depth
level -
const identity = x =>
x
const convertUntil = (test = identity, o = {}, depth = 0) =>
Object (o) !== o
? o
: Object
.entries (o)
.map
( ([ k, v ]) =>
test (v, depth) // <-- include depth in test
? { id: k, label: k }
: { id: k, label: k, children: convertUntil (test, v, depth + 1) } // <-- depth plus one
)
const myObj =
{ parent1:
{ child1: { lastChild1: { test: 'cool' } }
, child2: { lastChild2: { test: 'cool' } }
}
, parent2:
{ child2_1: { lastChild2_1: { test: 'cool' } }
, child2_2: { lastChild2_2: { test: 'cool' } }
}
}
console .log (convertUntil ((_, depth) => depth === 2, myObj))
Upvotes: 2
Reputation: 50787
If you don't need to distinguish the test
node from the other nodes, then I think this is straightforward:
const convert = (obj) =>
Object .entries (obj) .map (([k, v]) => ({
id: k,
label: k,
...(typeof v == 'object' ? {children: convert (v)} : {})
}))
const myObj = {
parent1: {child1: {lastChild1: { test: 'cool'}}, child2: {lastChild2: { test: 'cool'}}},
parent2: {child2_1: {lastChild2_1: { test: 'cool'}}, child2_2: {lastChild2_2: { test: 'cool'}}}
}
console .log (
convert (myObj)
)
I'm guessing that distinguishing that deepest node would make this significantly more complex.
Ok, so it's not that much more complex, if the condition is that the object has no properties which are themselves objects. (This is still not clear, and the requested output sample and the image posted as a comment on another answer seem to disagree. But that would be my best guess.) We can do this with either an inline test:
const convert = (obj) =>
Object .entries (obj) .map (([k, v]) => ({
id: k,
label: k,
...((typeof v == 'object' && Object .values (v) .some (o => typeof o == 'object'))
? {children: convert (v)}
: {}
)
}))
or with a helper function:
const hasObjectProperties = (obj) =>
Object .values (obj) .some (o => typeof o == 'object')
const convert = (obj) =>
Object .entries (obj) .map (([k, v]) => ({
id: k,
label: k,
...((typeof v == 'object' && hasObjectProperties(v))
? {children: convert (v)}
: {}
)
}))
Using the latter, the code becomes:
const hasObjectProperties = (obj) =>
Object .values (obj) .some (o => typeof o == 'object')
const convert = (obj) =>
Object .entries (obj) .map (([k, v]) => ({
id: k,
label: k,
...((typeof v == 'object' && hasObjectProperties(v))
? {children: convert (v)}
: {}
)
}))
const myObj = {
parent1: {child1: {lastChild1: { test: 'cool'}}, child2: {lastChild2: { test: 'cool'}}},
parent2: {child2_1: {lastChild2_1: { test: 'cool'}}, child2_2: {lastChild2_2: { test: 'cool'}}}
}
console .log (
convert (myObj)
)
Upvotes: 1
Reputation: 1229
const myObj = {
parent1: {
child1: {
lastChild1: { test: 'cool'}
},
child2: {
lastChild2: { test: 'cool'}
}
},
parent2: {
child2_1: {
lastChild2_1: { test: 'cool'}
},
child2_2: {
lastChild2_2: { test: 'cool'}
}
}
}
function getOptions(obj) {
return Object.keys(obj).reduce((acc, cur) => {
acc.push({
id: cur,
label: cur,
children: recurseList(obj[cur])
})
return acc;
}, [])
}
function recurseList(obj) {
return Object.keys(obj).reduce((acc, cur) => {
if(obj[cur] instanceof Object) {
let data = {
id: cur,
label:cur
}
const children = recurseList(obj[cur]);
If(children.length) {
data.children = children
}
acc.push(data)
}
return acc;
}, [])
}
var result = getOptions(myObj)
console.log('result', result)
The problem is that you always use empty children
array in a loop. And also you are not using your very first key parent1
to push to your result array.
Upvotes: 3