Javascript Recursion thru children and display in HTML - javascript

I want to display a result based on nested children's value. I am using the recursion function for this. I am having trouble with how will I display it to HTML based on the example result below.
I am using React.js.
For now I am using console.log.
Here is my code: (Sample data are in the bottom)
function recursion(data, label) {
if (!data) {
return;
}
for (let a = 0; a < data.length; a++) {
const item = data[a]
if (!item.items) {
const isLast = data.length - a === a
console.log(`(${item.course_id}) ${ isLast ? '' : label} `)
} else {
if (label) console.log(label)
recursion(item.items, item.type)
}
}
}
/*
Example result that I want to achieve is something like be:
(1894 $or 1501)
$or
(7068 $or 7120 or 7141)
$or
(997 $and 9001)
$or
(7256 $or 4534)
*/
//My sample data:
const data = [{
"type": "$or",
"items": [{
"course_id": "1894"
},
{
"course_id": "1501"
},
{
"type": "$or",
"items": [{
"course_id": "7068"
},
{
"course_id": "7120"
},
{
"course_id": "7141"
},
{
"type": "$and",
"items": [{
"course_id": "997"
},
{
"course_id": "9001"
}
]
}
]
},
{
"type": "$or",
"items": [{
"course_id": "7256"
},
{
"course_id": "4534"
}
]
}
]
}]
//start here
recursion(data)

Puts all leaf nodes (nodes without children) in items into an array, then recurses into nodes. Then joins them all together using the $type.
const data = [{
"type": "$or",
"items": [
{
"course_id": "1894"
},
{
"course_id": "1501"
},
{
"type": "$or",
"items": [
{
"course_id": "7068"
},
{
"course_id": "7120"
},
{
"course_id": "7141"
},
{
"type": "$and",
"items": [
{
"course_id": "997"
},
{
"course_id": "9001"
}
]
}
]
},
{
"type": "$or",
"items": [
{
"course_id": "7256"
},
{
"course_id": "4534"
}
]
}
]
}]
/*
function recursion(data, label) {
if (!data) { return; }
for (let a = 0; a < data.length; a++) {
const item = data[a]
if (!item.items){
console.log(`${item.course_id} ${label} `)
} else {
console.log('')
console.log(label || '')
setParentLabel = item.type
const child = recursion(item.items, setParentLabel)
}
}
}
*/
const recursion = (data, label) => {
let nodes = [[]];
const type = data.type;
data.items.forEach( x => {
const leafs = nodes[0];
const isLeaf = !x.items;
if ( isLeaf ) leafs.push( x.course_id );
else nodes.push( recursion( x, x.type ) );
});
if ( nodes[0].length > 0 )
nodes[0] = '('+nodes[0].join(` ${type} `)+')';
else nodes = nodes.slice(1);
let string = '';
string = nodes.join(`\n${type}\n`);
return string;
};
console.log(recursion({items: data}))
const data = [{
"type": "$or",
"items": [
{
"course_id": "1894"
},
{
"course_id": "1501"
},
{
"type": "$or",
"items": [
{
"course_id": "7068"
},
{
"course_id": "7120"
},
{
"course_id": "7141"
},
{
"type": "$and",
"items": [
{
"course_id": "997"
},
{
"course_id": "9001"
}
]
}
]
},
{
"type": "$or",
"items": [
{
"course_id": "7256"
},
{
"course_id": "4534"
}
]
}
]
}]
/*
Example result that I want to achieve is something like be:
(1894 $or 1501)
$or
(7068 $or 7120 or 7141)
$or
(997 $and 9001)
$or
(7256 $or 4534)
*/
function recursion(data, label) {
if (!data) { return; }
let nodes = [[]];
let leafs = nodes[0];
for (let a = 0; a < data.length; a++) {
const item = data[a]
if (!item.items){
leafs.push(item.course_id)
} else {
setParentLabel = item.type
const child = recursion(item.items, setParentLabel)
nodes.push(child);
}
}
if ( nodes[0].length > 0 )
nodes[0] = '(' + nodes[0].join(` ${label} `) + ')';
else nodes = nodes.slice(1);
return nodes.filter(x=>!!x).join(`\n${label}\n`);
}
console.log(recursion(data))

const unSortedData = [{
"type": "$or",
"items": [{
"course_id": "1894"
},
{
"type": "$or",
"items": [{
"course_id": "7068"
},
{
"course_id": "7120"
},
{
"course_id": "7141"
},
{
"type": "$and",
"items": [{
"course_id": "997"
},
{
"course_id": "9001"
}
]
}
]
},
{
"type": "$or",
"items": [{
"course_id": "7256"
},
{
"course_id": "4534"
}
]
},
{
"course_id": "1501"
}
]
},
{
"type": "$or",
"items": [{
"course_id": "1894"
},
{
"type": "$or",
"items": [{
"course_id": "7068"
},
{
"course_id": "7120"
},
{
"course_id": "7141"
},
{
"type": "$and",
"items": [{
"course_id": "997"
},
{
"course_id": "9001"
}
]
}
]
},
{
"type": "$or",
"items": [{
"course_id": "7256"
},
{
"course_id": "4534"
}
]
},
{
"course_id": "1501"
}
]
}]
/**Inorder to split between two data I have used value.type if you want
*something else then you can use your own delimiter(just replace
*value.type with your requirement). Also if you want to store the looped
*item, you can store it into an array rather than returning string value.
*Example instead of dataJoiner you an use
*const arr = [];
*Inside forEach loop. arr.push(loopItems(value.items, value.type));
*and finally return arr. It's clearly upto you what you want to do with
*the recursed item
**/
function loopData(data) {
let dataJoiner = '';
data.forEach(value => {
dataJoiner = `${dataJoiner}\n ${dataJoiner ? ' ' + value.type + ' ' : dataJoiner}\n ${loopItems(value.items, value.type)})`;
});
console.log(dataJoiner);
return dataJoiner;
};
/** In this method items are sorted as per object type. Objects with only
*course_id are sorted and then Object with items are sorted. After that
*using
*reduce funtion it checks whether item has type and if it hasn't it
*accumulates by joining course_id with the given type. And whenever it
*detects type on the item it recursively calls loopItems whose result is
*accumulated into accumulator i.e. acc
**/
function loopItems(items, type) {
if(items && items.length) {
const sortedItems = items.sort((a, b) => Object.keys(a).length - Object.keys(b).length);
return sortedItems.reduce((acc, item, index) => {
if(item.type) {
return `${acc})\n ${acc ? ' ' + type + ' ' : acc}\n ${loopItems(item.items, item.type)}`;
}
const leftBracket = index === 0 ? '(' : ''
return `${leftBracket }${acc}${acc ? ' ' + type + ' ' : acc}${item.course_id}`;
}, '');
}
}
loopData(unSortedData);

function recursion(items, type = '') {
let result = [];
for (let item of items) {
if (!item.items && !item.type) {
result.push(item.course_id);
} else {
result.push(`(${recursion(item.items, item.type)})`);
}
}
return result.join(` ${type} `);
}
//start here
console.log(recursion(data));

Related

How do I destructure this deep nested json objects and map it in JS

I have a nested array like below. There are about 100 de objects in the array. The de objects also have deg[0] array but most likely I will only have the first index. Now the trick is that the de are subset of deg. Which means each deg can have say 10 de. How can I retrieve the deg and there associated de and map it into a new array like:
newArray = [
deg1: [
{de1},
{de2}
],
deg2: [
{de1},
{de2}
]
]
Here is my nested array. I posted four but the list is over a 100.
{
"name": "Report",
"id": "2YYUEZ6I1r9",
"dse1": [
{
"de1": {
"name": "Number",
"id": "HjMOngg3kuy",
"de1-av": [
{
"value": "FHaQMPv9zc7",
"attribute": {
"id": "uwVkIP7PZDt"
}
},
{
"value": "something",
"attribute": {
"id": "FHaQMPv9zc7"
}
}
],
"deg1": [
{
"name": "TB",
"id": "2XJB1JO9qX8"
}
]
}
},
{
"de2": {
"name": "Number of",
"id": "a3dtGETTawy",
"de2-av": [
{
"value": "FHaQMPv9zc7",
"attribute": {
"id": "uwVkIP7PZDt"
}
},
{
"value": "something",
"attribute": {
"id": "FHaQMPv9zc7"
}
}
],
"deg1": [
{
"name": "Secondary",
"id": "w99RWzXHgtw"
}
]
}
},
{
"de1": {
"name": "Number of",
"id": "a3dtGETTawy",
"de1av": [
{
"value": "FHaQMPv9zc7",
"attribute": {
"id": "uwVkIP7PZDt"
}
},
{
"value": "something",
"attribute": {
"id": "FHaQMPv9zc7"
}
}
],
"deg2": [
{
"name": "Secondary",
"id": "w99RWzXHgtw"
}
]
}
},
{
"de2": {
"name": "Number of",
"id": "a3dtGETTawy",
"de2av": [
{
"value": "FHaQMPv9zc7",
"attribute": {
"id": "uwVkIP7PZDt"
}
},
{
"value": "something",
"attribute": {
"id": "FHaQMPv9zc7"
}
}
],
"deg2": [
{
"name": "Tertiary",
"id": "w99RWzXHgtw"
}
]
}
}
]
}
Group array of objects by property (this time a property to be matched by a reg exp) using Array.reduce.
Update: Ignoring missing keys.
var input={name:"Report",id:"2YYUEZ6I1r9",dse1:[{de1:{name:"Number",id:"HjMOngg3kuy","de1-av":[{value:"FHaQMPv9zc7",attribute:{id:"uwVkIP7PZDt"}},{value:"something",attribute:{id:"FHaQMPv9zc7"}}],deg1:[{name:"TB",id:"2XJB1JO9qX8"}]}},{de2:{name:"Number of",id:"a3dtGETTawy","de2-av":[{value:"FHaQMPv9zc7",attribute:{id:"uwVkIP7PZDt"}},{value:"something",attribute:{id:"FHaQMPv9zc7"}}],deg1:[{name:"Secondary",id:"w99RWzXHgtw"}]}},{de1:{name:"Number of",id:"a3dtGETTawy",de1av:[{value:"FHaQMPv9zc7",attribute:{id:"uwVkIP7PZDt"}},{value:"something",attribute:{id:"FHaQMPv9zc7"}}],deg2:[{name:"Secondary",id:"w99RWzXHgtw"}]}},{de2:{name:"Number of",id:"a3dtGETTawy",de2av:[{value:"FHaQMPv9zc7",attribute:{id:"uwVkIP7PZDt"}},{value:"something",attribute:{id:"FHaQMPv9zc7"}}],deg2:[{name:"Tertiary",id:"w99RWzXHgtw"}]}}]}
var reg = new RegExp("^de[0-9]+$");
var reg2 = new RegExp("^deg[0-9]+$");
let obj = input['dse1'].reduce(function(agg, item) {
// do your group by logic below this line
var key = Object.keys(item).find(function(key) {
return key.match(reg) ? key : null;
})
if (key) {
var key2 = Object.keys(item[key]).find(function(key) {
return key.match(reg2) ? key : null;
})
agg[key] = agg[key] || [];
if (key2) {
var to_push = {}
to_push[key2] = item[key][key2]
agg[key].push(to_push)
}
}
// do your group by logic above this line
return agg
}, {});
console.log(obj)
.as-console-wrapper {
max-height: 100% !important;
}

Recursion not returning right result

I am working on a problem where I would like to transform the following object structure:
[
{
"label": "testType",
"categories": [
{
"label": "testCatType",
"subCategories": [
{
"label": "newSubCat",
"subSubCategories": [
{
"label": "newSubSubCat1"
},
{
"label": "newSubSubCat2"
},
{
"label": "newSubSubCat3"
}
]
}
]
}
]
},
{
"label": "newType",
"categories": [
{
"label": "newCat10",
"subCategories": [
{
"label": "newCatSub1",
"subSubCategories": [
{
"label": "bingo11"
},
{
"label": "bingo12"
},
{
"label": "bingo15"
}
]
}
]
}
]
},
{
"label": "displacement",
"categories": []
},
{
"label": "brush",
"categories": [
{
"label": "blood",
"subCategories": []
},
{
"label": "damage",
"subCategories": []
},
]
}
]
into something like this:
{
"testType": {
"testCatType": {
"newSubCat": {
"newSubSubCat1": {},
"newSubSubCat2": {},
"newSubSubCat3": {}
}
}
},
"newType": {
"newCat10": {
"newCatSub1": {
"bingo11": {},
"bingo12": {},
"bingo15": {}
}
}
},
"displacement": {},
....
}
I have implemented the following recursive solution for this:
recursiveAssign(obj, json) {
if (_.isUndefined(obj)) {
return;
}
let totalKeys = _.keys(obj);
return _.assign(json, { [obj['label']]: this.test(obj[totalKeys[1]], json) })
}
transform(typeObj){
let json = {};
_.forEach(typeObj, obj => {
let totalKeys = _.keys(obj);
if (totalKeys < 2) {
_.assign(json, obj['label']);
} else {
_.assign(json, { [obj['label']]: this.recursiveAssign(obj[totalKeys[1]], json) })
}
})
}
Now the end result of this is an object that is a copy of itself on each level and I don't quite understand what the problem is. I think my approach is not faulty as I take the label and call the recursive function on the other part of that object. Can please someone point out to the potential issue!
You can write transform as a simple recursive function -
function transform (all = []) {
return Object.fromEntries(all.map(t =>
[t.label, transform(t.categories ?? t.subCategories ?? t.subSubCategories)]
))
}
const data =
[{"label":"testType","categories":[{"label":"testCatType","subCategories":[{"label":"newSubCat","subSubCategories":[{"label":"newSubSubCat1"},{"label":"newSubSubCat2"},{"label":"newSubSubCat3"}]}]}]},{"label":"newType","categories":[{"label":"newCat10","subCategories":[{"label":"newCatSub1","subSubCategories":[{"label":"bingo11"},{"label":"bingo12"},{"label":"bingo15"}]}]}]},{"label":"displacement","categories":[]},{"label":"brush","categories":[{"label":"blood","subCategories":[]},{"label":"damage","subCategories":[]},]}]
console.log(JSON.stringify(transform(data), null, 2))
{
"testType": {
"testCatType": {
"newSubCat": {
"newSubSubCat1": {},
"newSubSubCat2": {},
"newSubSubCat3": {}
}
}
},
"newType": {
"newCat10": {
"newCatSub1": {
"bingo11": {},
"bingo12": {},
"bingo15": {}
}
}
},
"displacement": {},
"brush": {
"blood": {},
"damage": {}
}
}
You can chain ... ?? t.subSubSubCategories and ... ?? t.subSubSubSubCategories, if needed. A better approach would be to create a consistent node interface for all nodes in your graph. You could achieve this by renaming all sub*Categories to simply categories. This will give each node an inteface like
Node: { label: String, categories: Array<Node> }
You can solve this in pure js like this:
const arr = [{ "label": "testType", "categories": [{ "label": "testCatType", "subCategories": [{ "label": "newSubCat", "subSubCategories": [{ "label": "newSubSubCat1" }, { "label": "newSubSubCat2" }, { "label": "newSubSubCat3" }] }] }] }, { "label": "newType", "categories": [{ "label": "newCat10", "subCategories": [{ "label": "newCatSub1", "subSubCategories": [{ "label": "bingo11" }, { "label": "bingo12" }, { "label": "bingo15" }] }] }] }, { "label": "displacement", "categories": [] }, { "label": "brush", "categories": [{ "label": "blood", "subCategories": [] }, { "label": "damage", "subCategories": [] }, ] }];
let result = recursive(arr);
console.log(result);
function recursive(arr) {
let result = {};
let returnValue = {}
for (let obj of arr) {
let entries = Object.entries(obj);
if (!entries.length) {
return {};
}
for (let [key, value] of entries) {
if (typeof value == "object") {
result = recursive(value);
}
}
const label = obj["label"];
returnValue = { ...returnValue,
[obj["label"]]: result
};
}
return returnValue;
}
.as-console-wrapper {min-height: 100%;}

map values to an array inside an object

I have this object below
{
"root": {
"data": {
"page": 1,
"contents": [
{
"node": {
"id": "UzpfSTE",
"stats": {
"viewers": {
"nodes": [
{
"id": "1",
"name": "John"
},
{
"id": "2",
"name": "Shaun"
}
]
}
}
}
},
{
"node": {
"id": "UzpfSTG",
"stats": {
"viewers": {
"nodes": [
{
"id": "3",
"name": "Liam"
}
]
}
}
}
}
]
}
}
}
There is contents node, each of them will have many viewers, all I want is to extract all viewers name to an array, in this instance my result will be [John, Shaun, Liam]
I have this approach:
const data = JSON.parse(rt)
const contents = data.root.data.contents
const newArray = []
for (i = 0; i < contents.length; i++) {
arr2 = contents[i].node.stats.viewers.nodes
for (n = 0; n < arr2.length; n++) {
name = arr2[n].name
newArray.push(name)
}
}
console.log(newArray)
>>> [John, Shaun, Liam]
Which does the job, but occasionaly the object key names change and I have to alter everytime.
So is there more elegant way to do this?
You can simplify that imperative logic like this.
I don't understand what you mean by "the object key names change" though
const data = {
"root": {
"data": {
"page": 1,
"contents": [{
"node": {
"id": "UzpfSTE",
"stats": {
"viewers": {
"nodes": [{
"id": "1",
"name": "John"
},
{
"id": "2",
"name": "Shaun"
}
]
}
}
}
},
{
"node": {
"id": "UzpfSTG",
"stats": {
"viewers": {
"nodes": [{
"id": "3",
"name": "Liam"
}]
}
}
}
}
]
}
}
}
const names = data.root.data.contents.flatMap(({
node: {
stats: {
viewers: {
nodes
}
}
}
}) => nodes.map(({
name
}) => name))
console.log(names)
const data = JSON.parse(rt)
const contents = data.root.data.contents
const viewers = contents.map(item => item.node.stats.viewers.nodes).flat()
const viewerNames = viewers.map(viewer => viewer.name)

JSON data formatting through dynamic Key, Value

I have a JSON response data. I want to display it as a tabular format but I am not able to do it correctly. please correct me where I am doing wrong.
Response Data
const response = [
{
"key": "Name",
"data": [ "Tom", "Mark", "Jack" ]
},
{
"key": "OfcLocation",
"data": [ "USA", "Spain", "Japan" ]
},
{
"key": "IPAddress",
"data": [ "XX.XXX.X.XX", "XX.XXX.X.XX", "XX.XXX.X.XX" ]
},
{
"key": "Port",
"data": [ "4300", "4080", "9200" ]
},
{
"key": "Role",
"data": [ "Admin", "Limited", "Admin" ]
}
];
Desired Output Format
[
{ "Name": "Tom", "OfcLocation": "USA", "IPAddress": "XX.XXX.X.XX", "Port": "4300", "Role": "Admin" },
{ "Name": "Mark", "OfcLocation": "Spain", "IPAddress": "XX.XXX.X.XX", "Port": "4080", "Role": "Limited" },
{ "Name": "Jack", "OfcLocation": "Japan", "IPAddress": "XX.XXX.X.XX", "Port": "9200", "Role": "Admin" }
]
Code
let formatedData = [];
let rowArr = {};
for( let i=0; i< response.length; i++) {
for( let j=0;j< response[i].data.length; j++) {
rowArr[response[i].key] = response[i].data[j];
}
formatedData.push(rowArr);
}
You can get the result you want using Array.reduce function.
const response = [
{
"key": "Name",
"data": [ "Tom", "Mark", "Jack" ]
},
{
"key": "OfcLocation",
"data": [ "USA", "Spain", "Japan" ]
},
{
"key": "IPAddress",
"data": [ "XX.XXX.X.XX", "XX.XXX.X.XX", "XX.XXX.X.XX" ]
},
{
"key": "Port",
"data": [ "4300", "4080", "9200" ]
},
{
"key": "Role",
"data": [ "Admin", "Limited", "Admin" ]
}
];
const result = response.reduce((acc, cur) => {
cur.data.forEach((item, index) => {
if (acc.length <= index) {
acc.push({
[cur.key]: item
});
} else {
acc[index][cur.key] = item;
}
});
return acc;
}, []);
console.log(result);
You need to switch the inner and outer loop and put let rowArr = {}; inside the outer loop
let formatedData = [];
let rowArr = {};
for (let j = 0; j < response[0].data.length; j++) {
let rowArr = {};
for (let i = 0; i < response.length; i++) {
rowArr[response[i].key] = response[i].data[j];
}
formatedData.push(rowArr);
}
Make an array of empty objects and put the data in them...
let sortedArray = [];
for(let i=0 ; i< response[0].data.length ; i++){
sortedArray.push(new Object);
}
function sort() {
response.forEach((el) => {
el.data.forEach((elem, i) => {
sortedArray[i][el.key] = elem;
});
});
}
sort();
console.log(sortedArray);

Underscore Convert array to object keys

I am trying to convert this array to an object. Using underscore, I want to convert this array :
[
{
"id": "parentA",
"children": [
{
"name": "name1"
},
{
"name": "name2"
},
{
"name": "name3"
}
]
},
{
"id": "parentB",
"children": [
{
"name": "name4"
},
{
"name": "name5"
},
{
"name": "name6"
}
]
}]
into an object that looks like this:
{
"name1": "parentA",
"name2": "parentA",
"name3": "parentA",
"name4": "parentB",
"name5": "parentB",
"name6": "parentB"
}
I'm really just looking for the cleanest/simplest way possible.
Here's a fairly short way to do it with two reduce:
var data = [
{
"id": "parentA",
"children": [
{
"name": "name1"
},
{
"name": "name2"
},
{
"name": "name3"
}
]
},
{
"id": "parentB",
"children": [
{
"name": "name4"
},
{
"name": "name5"
},
{
"name": "name6"
}
]
}];
var out = _.reduce(data, function(result, parent) {
_.reduce(parent.children, function(r, child) {
r[child.name] = parent.id;
return r;
}, result);
return result;
}, {});
document.write(JSON.stringify(out));
<script src="http://underscorejs.org/underscore-min.js"></script>
var a = [{
"id": "parentA",
"children": [{
"name": "name1"
}, {
"name": "name2"
}, {
"name": "name3"
}]
}, {
"id": "parentB",
"children": [{
"name": "name4"
}, {
"name": "name5"
}, {
"name": "name6"
}]
}];
var new_obj = {};
var len = a.length;
for (j = 0; j < len; j++) {
var c = $.extend({}, a[j]);
var children_length = (c.children).length;
for (i = 0; i < children_length; i++) {
var temp = ((a[j].children)[i]).name;
new_obj[temp] = c.id;
}
}
document.write(JSON.stringify(new_obj));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You'll only need to use underscore if you're supporting browsers without native reduce and forEach array methods, but you can do it like this.
var result = _.reduce(array, function(memo, entry) {
_.each(entry.children, function(child) {
memo[child.name] = entry.id;
});
return memo;
}, {});
function expand(list){
return _.reduce(list,function(a,b) {
_.each(b.children, function(c) {
a[c.name] = b.id;
});
return a;
},{});
}
Check the output for your sample here

Categories

Resources