How to render recursive components outside of parent component in Vue.js? - javascript

I have a tree data structure, and i need to recursively render it
const tree = [
{
name: 'hello',
children: [
{
name: 'world',
children: [
{
name: 'bye'
}
]
}
]
}
];
the problem is that this component should be inside table as table-rows, so it cannot be nested in DOM inside each other
This is what it looks like
https://jsfiddle.net/zw4mydxb/2/
and this is the result i need
<tr>hello</tr>
<tr>world</tr>
<tr>bye</tr>
is it even possible to achieve with recursive components and without changing data structure?

You can use recursion for this, Following code snippet will help you.
const tree = [
{
name: "hello",
children: [
{
name: "world",
children: [
{
name: "bye"
}
]
},
{
name: "hola"
}
]
},
{
name: "how",
children: [
{
name: "is",
children: [
{
name: "life"
}
]
}
]
}
];
function getData(tree) {
if (tree && typeof tree[0].children === "undefined") return tree[0].name;
var outputString = [];
for (let i = 0; i < tree.length; i++) {
if (typeof tree[i].children != "undefined") {
outputString.push(tree[i].name, getData(tree[i].children));
} else {
outputString.push(tree[i].name);
}
}
return outputString.toString().split(",");
}
console.log(getData(tree));
Now you have an array of name and you can iterate this array.

Related

Recursivly change object nested property name

I have object with nested fields than named subItem I need to change names of this field to children.
How can this functionality be neatly implemented, perhaps there are ways besides recursion?
let data = [
{
name: 'Alex',
subItem: [
{
name: 'Den',
subItem: [
]
}
]
}
]
function transformData = (data) => {
return data.map(stat=>{
})
}
console.log(transformData(data))
//shoud be
let data = [
{
name: 'Alex',
children: [
{
name: 'Den',
children: []
}
]
}
]
Javascript already includes a recursive object transformer, JSON. stringify with replacer function:
let data = [
{
name: 'Alex',
subItem: [
{
name: 'Den',
subItem: [ {name: 'Foo', subItem: []} ],
}
]
}
]
JSON.stringify(data, function(_, v) {
if (v.subItem) {
v.children = v.subItem
delete v.subItem
}
return v
})
console.log(data)
In the simple case like this, you can also just manipulate the JSON string directly:
newData = JSON.parse(
JSON.stringify(data)
.replaceAll(`"subItem":`, `"children":`))

Nested recursive object loop

I have arrays of objects that can also have arrays of their own. My main goal is to find an object with a given id in the whole tree and get readmap to that element by displaying the names of the objects names where it occurs.
For example I have data object like this:
{
id: '0',
name: "Boys"
children: [
{
name: "Soldiers",
children: [
{
name: "Bravo"
children: [
{name: "Tom"},
{name: "Andrew"}
]
}
]
},
{
name: "Runners",
children: [
{
name: "Team B"
children: [
{name: "Mark"},
{name: "David"}
]
}
]
}
]
}
I am currently finding an item by a function
function findByName (name, array) {
for (const node of array) {
if (node.name === name) return node;
if (node.children) {
const child = findByName(name, node.children);
if (child) return child;
}
}
}
But to achive my goal I need also roadmap to that value. For example.
When I want to find "Tom". Besides results of findByName I would like to get {name: "Tom", road: ["Boys", "Soldiers", "Bravo"]
You would need to pass down another property which handles the path. Start by defining path as an empty array. And since you only care about the name, you can push the name into this array everytime you find a node that has children.
Then you just keep passing the updated array to your recursive function. See my working example below:
(I updated your function to return an object which contains both the result and path)
function findByName(name, array, path = []) {
for (const node of array) {
if (node.name === name) return {result: node, path};
if (node.children) {
path.push(node.name) // We update the path with the current node name that has children
const child = findByName(name, node.children, path );
if (child) return { result: child, path};
}
}
}
Demo:
https://jsitor.com/VnktoLq49
You could add the path for every leve in the calling function without handing over the path.
const
findByName = (array, name) => {
for (const node of array) {
if (node.name === name) return { ...node, path: [] };
if (node.children) {
const child = findByName(node.children, name);
if (child) return { ...child, path: [node.name, ...child.path] };
}
}
},
data = [{ id: '0', name: "Boys", children: [{ name: "Soldiers", children: [{ name: "Bravo", children: [{ name: "Tom" }, { name: "Andrew" }] }] }, { name: "Runners", children: [{ name: "Team B", children: [{ name: "Mark" }, { name: "David" }] }] }] }];
console.log(findByName(data, 'Tom'));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I like generators for this kind or problem because it allows you to select one, many, or all results. Additionally generators give control to the caller, allowing you to stop searching whenever you are satisfied with the result. This can be accomplished with a single function -
function* select(a = [], query = Boolean, path = [])
{ for (const t of a)
{ if (query(t)) yield { ...t, path }
yield *select(t.children, query, [...path, t.name])
}
}
const data =
[{ id: '0', name: "Boys", children: [{ name: "Soldiers", children: [{ name: "Bravo", children: [{ name: "Tom" }, { name: "Andrew" }] }] }, { name: "Runners", children: [{ name: "Team B", children: [{ name: "Mark" }, { name: "David" }] }] }] }]
// select "Tom" OR "Mark"
for (const r of select(data, v => v.name == 'Tom' || v.name == "Mark"))
console.log("found:", r)
found: {
"name": "Tom",
"path": [
"Boys",
"Soldiers",
"Bravo"
]
}
found: {
"name": "Mark",
"path": [
"Boys",
"Runners",
"Team B"
]
}
If you want only the first result, we can use return or break, and searching stops immediately, potentially saving many wasted computations -
function first (it)
{ for (const x of it)
return x // <- return and stop searching
}
first(select(data, v => v.name == "Andrew"))
{
"name": "Andrew",
"path": [
"Boys",
"Soldiers",
"Bravo"
]
}
If you want all of the results, we can use Array.from. Because select is flexible, it allows us to do all sorts of useful queries -
Array.from(select(data, v => !v.children), r => r.name)
[
"Tom",
"Andrew",
"Mark",
"David"
]

How to get the last children in a deeply nested array with objects

So let's say I have a deeply nested array and I want to get the deepest nested children and I'm unable to think of a good way to implement it
basically as long as the children property exists, it needs to dive inside it and it not I want to test if the name matches my search
[
{
name: 'something',
children: [
{
name: 'something',
children: [
{
...
}
]
}
]
},
{
name: 'something',
children: [
{
name: 'something',
children: [
{
...
}
]
}
]
},
]
hasOwnProperty() may help you knowing if the property Children exist or not, and then, knowing if you need a recursive call or not
For example :
var MyObj = [
{
name: 'something',
children: [
{
name: 'something',
children: [
{
name: 'no child'
},
{
name: 'something empty',
children: [ ]
}
]
}
]
},
{
name: 'something',
children: [
{
name: 'something',
children: [
{
name: 'no child'
}
]
}
]
},
{
name: "children isn't an array",
children: 42
}
]
/*
* This will display in the console the "name" property, if it exists,
* of elements that has :
* - no "children" property
* - a "children" property that isn't an array
* - a "children" property that is an empty array
*/
function ChildrenNames(obj)
{
obj.forEach((subObj) =>
{
if (subObj.hasOwnProperty('children')
&& subObj.children instanceof Array
&& subObj.children.length > 0)
{
ChildrenNames(subObj.children);
}
else
{
if (subObj.hasOwnProperty('name'))
console.log(subObj.name);
}
});
}
ChildrenNames(MyObj);

Need help recursively iterating through all levels of a Javascript object

I am trying to create an object, and within this object will be the name and an array of other objects under children. Really I want to create a hierarchy from another object.
I have tried to create a recursive function but what I end up with is a vertical slice rather than the whole picture. I am unsure how to adjust my recursion to go back add iterate through the other horizontal objects.
buildHierarchy(json) {
console.log("Entered Build Hierarchy");
let newObject;
newObject = this.buildChildren(json);
console.log(newObject);
return newObject
}
buildChildren(json) {
let returnObject;
for (var key in json) {
returnObject = {
name: key,
children: []
};
var subObject = json[key];
if (Array.isArray(subObject)) {
returnObject = {
name: key,
_proficiency: subObject
}
} else {
returnObject["children"].push(this.buildChildren(subObject))
}
}
return returnObject;
}
Imagine you have this json file below
{users:
{sandy: {
posts: [
{ title: 'Bar', comments: [ 'Ok' ] },
]
followers: [
{ name: 'Foo' },
]
}
ron: {
photos: [
{ title: 'Foo', comments: [ 'Ok' ] },
]
}
}
}
I am looking for something like this...
{
name: "users",
children: [
{
name: "sandy",
children: [
{
name: "posts",
children: [
{
name: "Bar",
comments: "OK"
}],
{ name: "followers"
children: [
{
name: "Foo"
}
]
}
}
]
},
{
name: "ron",
photos: [
{
name: "photos",
children: [
{
name: "Foo",
comments: "OK"
}
]
}
]
}
]
}
From what I see as the output I'm making these inferences:
If the child object is an array, it is blindly copied to children key
If the child element is an object, then they are changed to {name: <KEY>, children: <VALUE>}
function buildChildren(json) {
let returnObject = [];
if (typeof json !== 'object') {
return json;
}
for (var key in json) {
if (Array.isArray(json)) {
returnObject.push(buildChildren(json[key]));
} else {
returnObject.push({
name: key,
children: buildChildren(json[key])
});
}
}
return returnObject;
}
function buildHierarchy(json) {
console.log("Entered Build Hierarchy");
let newObject;
newObject = buildChildren(json);
console.log(newObject);
}
function buildChildren(json) {
if (Array.isArray(json)) {
return {
_proficiency: json
}
}
var children = Object.keys(json);
let final = [];
for (var i = 0; count = children.length, i < count; i++) {
let result = {
name: children[i]
}
let d = buildChildren(json[children[i]]);
if (d._proficiency) {
result._proficiency = d._proficiency;
} else {
result.children = d;
}
final.push(result);
}
return final;
}

How to find a tree inside a tree in typescript

let say i have a tree in javascript
a1
--b
----c1
a2
--b2
--b3
----c2
and if i wanted to find c2, it should return a2->b3->c2
Lets say my json looked like this?
treeFamily = {
name : "Parent",
children: [{
name : "Child1",
children: [{
name : "Grandchild1",
children: []
},{
name : "Grandchild2",
children: []
},{
name : "Grandchild3",
children: []
}]
}, {
name: "Child2",
children: []
}]
};
You could check if the nested children have the wanted key/value. Then take the name and hand over the result to the outer call.
function findPath(array, target) {
var path;
array.some(({ name, children }) => {
var temp;
if (name === target) {
path = [name];
return true;
}
if (temp = findPath(children, target)) {
path = [name, ...temp];
return true;
}
});
return path;
}
var treeFamily = { name: "Parent", children: [{ name: "Child1", children: [{ name: "Grandchild1", children: [] }, { name: "Grandchild2", children: [] }, { name: "Grandchild3", children: [] }] }, { name: "Child2", children: [] }] };
console.log(findPath([treeFamily], 'Grandchild2'));
console.log(findPath([treeFamily], 'foo'));
You can use for...of to search the children by calling the function recursively. If the target is found, the name is returned, and combined with the previous names. If not, the function will return undefined. Alternatively, you can return an empty array.
const findPath = (targetName, { name, children }) => {
if(name === targetName) return [name];
for(const child of children) {
const result = findPath(targetName, child);
if(result) return [name, ...result];
}
// if child not found implicitly return undefined or return [] to get an empty array
};
const treeFamily = { name: "Parent", children: [{ name: "Child1", children: [{ name: "Grandchild1", children: [] }, { name: "Grandchild2", children: [] }, { name: "Grandchild3", children: [] }] }, { name: "Child2", children: [] }] };
console.log(findPath('Child2', treeFamily));
console.log(findPath('Grandchild3', treeFamily));
console.log(findPath('Grandchild400', treeFamily));

Categories