Html generator from json appendchild to the wrong parent - javascript

I am coming to you today,
I tried to make an html generator from a json file in javascript.
The problem being that arrived at a certain moment the children "appendChild" to the previous element instead of "appendChild" to the parent
here is my code :
$(document).ready(function(){
$.getJSON('THE JSON FILE FROM URL', function(data){
const components = data.htmlComponents.tableSuiviCommandes.components;
createHtmlFromJson(components)
}).fail(function( jqxhr, textStatus, error ) {
var err = textStatus + ", " + error;
console.log( "Request Failed: " + err );
});
function createHtmlFromJson(json, parent){
json.forEach(jsonElement => {
// log("jsonElemement", jsonElement)
createdElement = document.createElement(jsonElement.balise);
if(jsonElement.content) createdElement.innerHTML = jsonElement.content;
jsonElement.attributes?.forEach(element => {
createdElement.setAttribute(element.name, element.content);
});
if(!parent){
document.body.appendChild(createdElement);
}else {
parent.appendChild(createdElement);
}
jsonElement.childs?.forEach(element => {
let theParent = createdElement;
if(element != typeof Array) element = [element];
createHtmlFromJson(element, theParent);
});
});
}
function log(name, value){
console.log("----------");
console.log(name);
console.log(value);
console.log("----------")
}
});
the json file :
{
"htmlComponents": {
"tableSuiviCommandes": {
"components": [
{
"balise": "div",
"attributes": [
{
"name": "class",
"content": "containerTable"
}
],
"childs": [
{
"balise": "table",
"childs": [
{
"balise": "thead",
"childs": [
{
"balise": "tr",
"childs": [
{
"balise": "th",
"content": "Id"
},
{
"balise": "th",
"content": "Statut"
},
{
"balise": "th",
"content": "Voir la demande"
},
{
"balise": "th",
"content": "Objet"
},
{
"balise": "th",
"content": "Projet/Application"
},
{
"balise": "th",
"content": "Demandeur(s)"
},
{
"balise": "th",
"content": "Site"
},
{
"balise": "th",
"content": "Expression de besoin"
},
{
"balise": "th",
"content": "Référence interne"
},
{
"balise": "th",
"content": "CDC retenu"
},
{
"balise": "th",
"content": "N°Cotation"
},
{
"balise": "th",
"content": "N°Devis"
},
{
"balise": "th",
"content": "Date devis"
},
{
"balise": "th",
"content": "N°DEMAP"
},
{
"balise": "th",
"content": "N°CMD"
},
{
"balise": "th",
"content": "N°GAFI"
},
{
"balise": "th",
"content": "Date prévisionnelle de livraison"
},
{
"balise": "th",
"content": "Date de livraison"
},
{
"balise": "th",
"content": "Commentaire"
}
]
}
]
},
{
"balise": "tbody",
"attributes": [
{
"name": "id",
"content": "tableauSuivi"
}
]
}
]
}
]
}
]
}
}
}
the html generate :
I tried with the debbuger to make step by step, but I really don't understand why theParent is not really the parent so that the children are children of the previous element

// You try this code
$(document).ready(function () {
createHtmlFromJson(JSON.parse(json).htmlComponents.tableSuiviCommandes.components)
function createHtmlFromJson(json, parent) {
// console.log(json);
json.forEach(jsonElement => {
// log("jsonElemement", jsonElement)
createdElement = document.createElement(jsonElement.balise);
if (jsonElement.content) createdElement.innerHTML = jsonElement.content;
jsonElement.attributes?.forEach(element => {
createdElement.setAttribute(element.name, element.content);
});
if (!parent) {
document.body.appendChild(createdElement);
} else {
parent.appendChild(createdElement);
}
let theParent = createdElement;
jsonElement.childs?.forEach(element => {
// console.log(createdElement);
if (element != typeof Array) element = [element];
// console.log(theParent);
createHtmlFromJson(element, theParent);
});
});
}
});

I'd suggest extracting a logic of creating a single HTML element into separate function. It may simplify things in terms of understanding. Here is a createElement function based on your createHtmlFromJson function, but it creates only single element (and all of its children of course) and just returns it:
function createElement(component) {
const el = document.createElement(component.balise);
if (component.content) {
const text = document.createTextNode(component.content);
el.appendChild(text);
}
component.attributes?.forEach((attr) => {
el.setAttribute(attr.name, attr.content);
});
component.childs?.forEach((child) => {
// Create all of its children and append them to this element
el.appendChild(createElement(child));
});
return el;
}
Now you can create an array of HTML elements based on your JSON, like so:
const { components } = data.htmlComponents.tableSuiviCommandes;
// Transform each object into the actual HTML element via `.map()`
const arrayOfHTMLElements = components.map(createElement);
And then you can append it to the DOM, like so:
arrayOfHTMLElements.forEach((element) => {
document.body.appendChild(element);
});

Related

How do I find an element of an array in NiFi ExecuteScript (ECMAScript)?

I have a need to get data out of a NiFi flow file with somewhat complex JSON content. I'm planning to use a NiFi ExecuteScript processor because I don't think it can be done with EvaluateJSONPath. The content looks like this (snippet)
...
"segments": [
{
"INS01": "Y",
"INS03": "001",
"INS02": "18",
"INS05": "A",
"id": "INS",
"INS04": "AI",
"INS08": "FT"
},
{
"REF02": "1041558xxxxx",
"REF01": "0F",
"id": "REF"
},
{
"REF02": "ABD",
"REF01": "1L",
"id": "REF"
},
{
"REF02": "106835xxxxx",
"REF01": "23",
"id": "REF"
}
],
...
I want to extract the REF02 property value from the segments array element that has REF01 === '0F'. The array element does not necessarily have a REF02 property. So in the above case, I should get 1041558xxxxx.
Here's my current script:
var flowFile = session.get()
if (flowFile != null) {
var InputStreamCallback = Java.type('org.apache.nifi.processor.io.InputStreamCallback')
var IOUtils = Java.type('org.apache.commons.io.IOUtils')
var StandardCharsets = Java.type('java.nio.charset.StandardCharsets')
try {
var subscriber = null
session.read(flowFile,
new InputStreamCallback(function (inputStream) {
var data = JSON.parse(IOUtils.toString(inputStream, StandardCharsets.UTF_8))
var segment = data.segments.find(function (s) { return s.hasOwnProperty('REF01') && s.REF01 === '0F' })
subscriber = segment ? segment.REF02 : null
}));
session.putAttribute(flowFile, 'subscriber', subscriber ? subscriber : '')
session.transfer(flowFile, REL_SUCCESS)
} catch (e) {
log.error('script failed', e)
session.transfer(flowFile, REL_FAILURE)
}
}
When I execute the above, I get a java.lang.NoSuchMethodException. Also, are anonymous 'arrow' functions allow?
I've tried using an old-school for loop to no avail.
Thanks for your help.
You can add a JoltTransformJSON processor with specification
[
{
"operation": "shift",
"spec": {
"segments": {
"*": {
"REF01": {
"0F": {// conditional to match "REF01" with "0F"
"#2,REF02": ""// go two levels up the three to reach the level of the attributes REF01 or REF02
}
}
}
}
}
}
]
in order to return the result
"1041558xxxxx"
You can use below JSONPath with EvaluateJSONPath processor:
$.segments[?(#.REF01<="0F")]#.REF02
Note: Returned result is in the array, So you can use SplitJSON after that to get your string.
Groovy script:
import org.apache.commons.io.IOUtils
import java.nio.charset.StandardCharsets
import groovy.json.JsonSlurper
flowFile = session.get()
if(!flowFile) return
def jsonSlurper = new JsonSlurper()
def subscriber = ""
flowFile = session.write(flowFile, {inputStream, outputStream ->
input = IOUtils.toString(inputStream, StandardCharsets.UTF_8)
json = jsonSlurper.parseText(input)
segment = json.segments.find{ segment ->
if (segment.keySet().contains('REF01')) {
if (segment.REF01 == '0F') {
return true
} else {
return false
}
} else {
return false
}
}
if (segment) {
subscriber = segment.REF02
}
outputStream.write(input.getBytes(StandardCharsets.UTF_8))
} as StreamCallback)
session.putAttribute(flowFile, 'subscriber', subscriber)
session.transfer(flowFile, REL_SUCCESS)
input:
{
"test": "best",
"segments": [
{
"INS01": "Y",
"INS03": "001",
"INS02": "18",
"INS05": "A",
"id": "INS",
"INS04": "AI",
"INS08": "FT"
},
{
"REF02": "1041558xxxxx",
"REF01": "0F",
"id": "REF"
},
{
"REF02": "ABD",
"REF01": "1L",
"id": "REF"
},
{
"REF02": "106835xxxxx",
"REF01": "23",
"id": "REF"
}
]
}
output (with attribute subscriber: 1041558xxxxx):
{
"test": "best",
"segments": [
{
"INS01": "Y",
"INS03": "001",
"INS02": "18",
"INS05": "A",
"id": "INS",
"INS04": "AI",
"INS08": "FT"
},
{
"REF02": "1041558xxxxx",
"REF01": "0F",
"id": "REF"
},
{
"REF02": "ABD",
"REF01": "1L",
"id": "REF"
},
{
"REF02": "106835xxxxx",
"REF01": "23",
"id": "REF"
}
]
}

How in JS to merge in one object two json objects where the ID of on object correspond on the same ID of the second object

My question relates to the fact I'm querying 2 different objects from DB and the result is in JSON. I need to merge them into one.
The 2 objects have in common this two key/value IRBId = ... and id = ... and they look as an example
OBJ 1
{
"data":{
"IRBs":{
"nodes":[
{
"id":"8",
"name":"Admin ",
},
{
"id":"9",
"name":"Again",
}
],
}
}
}
OBJ 2
{
"data":{
"informedConsentForms":{
"count":3,
"nodes":[
{
"id":"93",
...
"IRBId":"9",
},
{
"id":"92",
...
"IRBId":"8",
},
{
"id":"91",
...
"IRBId":"8",
}
],
}
},
As you will see above OBJ 2 and OBJ 1 corresponding with the same at IRBid and id.
What I need is to merge the two OBJ where IRBId OBJ 2 === id OBJ 1
The result I would expect after the merge is
OBJ merged
{
[{
"id":"93",
...
"IRBId":"9",
"irb": {
"name":"Again ",
...
}
},
{
"id":"92",
...
"IRBId":"8",
"irb": {
"name":"Admin ",
...
}
},
{
"id":"91",
...
"IRBId":"8",
"irb": {
"name":"Admin ",
...
}
],
},
I don't know how to make it looks like this.
Try using Array.reduce
Logic
Loop through second object data nodes
Find the matching nodes from object 1 data nodes.
Push to accumulator with required details. (I have added only the nodes that was mentioned in in Expected resut, you can add asmuch as you need.)
const obj1 = {
"data": {
"IRBs": {
"nodes": [
{
"id": "8",
"name": "Admin ",
},
{
"id": "9",
"name": "Again",
}
],
}
}
}
const obj2 = {
"data": {
"informedConsentForms": {
"count": 3,
"nodes": [
{
"id": "93",
"IRBId": "9",
},
{
"id": "92",
"IRBId": "8",
},
{
"id": "91",
"IRBId": "8",
}
],
}
},
};
const obj1List = obj1.data.IRBs.nodes;
const output = obj2.data.informedConsentForms.nodes.reduce((acc, curr) => {
const matchingNode = obj1List.find((item) => item.id === curr.IRBId);
if (matchingNode) {
acc.push({
id: curr.id,
IRBId: curr.IRBId,
irb: {
name: matchingNode.name
}
})
}
return acc;
}, []);
console.log(output);
You need to use the map function on the nodes in the first object to construct a new object that contains the second and first object's attributes.
const obj1 = {
"data": {
"IRBs": {
"nodes": [{
"id": "8",
"obj1": "one",
"name": "Admin ",
},
{
"id": "9",
"obj1": "two",
"name": "Again",
}
]
}
}
};
const obj2 = {
"data": {
"informedConsentForms": {
"count": 3,
"nodes": [{
"id": "93",
"obj2": "1",
"IRBId": "9",
},
{
"id": "92",
"obj2": "2",
"IRBId": "8",
},
{
"id": "91",
"obj2": "3",
"IRBId": "8",
}
],
}
}
};
const obj1Data = obj1.data.IRBs.nodes;
const obj2Data = obj2.data.informedConsentForms.nodes;
const res = obj2Data.map(item => {
const obj1Item = obj1Data.find(obj1Item => item.IRBId === obj1Item.id);
return obj1Item ? { ...item, "irb": { ...obj1Item}} : { ...item};
});
console.log(res);
i am using nested loop, try this one
const obj2 = {
"data":{
"informedConsentForms":{
"count":3,
"nodes":[
{
"id":"93",
"IRBId":"9",
},
{
"id":"92",
"IRBId":"8",
},
{
"id":"91",
"IRBId":"8",
}
],
}
},
}
const obj1 = {
"data":{
"IRBs":{
"nodes":[
{
"id":"8",
"name":"Admin ",
},
{
"id":"9",
"name":"Again",
}
],
}
}
}
const result = [];
const obj2Nodes = obj2.data.informedConsentForms.nodes;
for(let i = 0; i < obj2Nodes.length; i++) {
const obj1Nodes = obj1.data.IRBs.nodes
for(let j = 0; j < obj1Nodes.length; j++) {
if(obj2Nodes[i].IRBId === obj1Nodes[j].id) {
const {id, ...reObj1Nodes} = obj1Nodes[j];
result.push({
...obj2Nodes[i],
'irb': {
...reObj1Nodes
}
})
}
}
}
console.log(result)

Group and count values in an array

I have an array with objects, like the following.
b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
I want to count how many issues have status close, and how many have backlog. I'd like to save the count in a new array as follows.
a = [
{Name: 'Backlog', count: 1},
{Name: 'close', count: 2}
];
I have tried the following.
b.issues.forEach(function(i) {
var statusName = i.fields.status.name;
if (statusName in a.Name) {
a.count = +1;
} else {
a.push({
Name: statusName,
count: 1
});
}
});
That however doesn't seem to be working. How should I implement this?
This is a perfect opportunity to use Array#reduce. That function will take a function that is applied to all elements of the array in order and can be used to accumulate a value. We can use it to accumulate an object with the various counts in it.
To make things easy, we track the counts in an object as simply {name: count, otherName: otherCount}. For every element, we check if we already have an entry for name. If not, create one with count 0. Otherwise, increment the count. After the reduce, we can map the array of keys, stored as keys of the object, to be in the format described in the question. See below.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var counts = b.issues.reduce((p, c) => {
var name = c.fields.status.name;
if (!p.hasOwnProperty(name)) {
p[name] = 0;
}
p[name]++;
return p;
}, {});
console.log(counts);
var countsExtended = Object.keys(counts).map(k => {
return {name: k, count: counts[k]}; });
console.log(countsExtended);
.as-console-wrapper {
max-height: 100% !important;
}
Notes.
Array#reduce does not modify the original array.
You can easily modify the function passed to reduce to for example not distinguish between Backlog and backlog by changing
var name = c.fields.status.name;
into
var name = c.fields.status.name.toLowerCase();
for example. More advanced functionality can also easily be implemented.
Using ES6 Arrow functions you can do it with minimum syntax
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var countOfBackLog = b.issues.filter(x => {
return x.fields.status.name === "Backlog"
}).length
var countOfClose = b.issues.filter(x => {
return x.fields.status.name === "close"
}).length
a =[{Name: 'Backlog', count : countOfBackLog}, {Name: 'close', count : countOfClose}]
More about arrow functions here
You can write like this. It is dynamic.
var a = {};
for(var key in b["issues"]){
if(!a.hasOwnProperty(b["issues"][key].fields.status.name)){
a[b["issues"][key].fields.status.name] = 1;
}else{
a[b["issues"][key].fields.status.name] = a[b["issues"][key].fields.status.name]+1;
}
}
var c = [];
for(var key1 in a){
c.push({
name : key1,
count : a[key1]
});
}
Something like this should do the trick. Simply iterate over your data, keep 2 counters with the number of each type of issue, and create the data format you want in the end. Try it live on jsfiddle.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var data = [];
for(var issue of b.issues){
var entryFound = false;
var tempObj = {
name: issue.fields.status.name,
count: 1
};
for(var item of data){
if(item.name === tempObj.name){
item.count++;
entryFound = true;
break;
}
}
if(!entryFound){
data.push(tempObj);
}
}
console.log(data);

Accessing second array in a JSON decode using Jquery

I need to access the second array from a JSON decoded string, but I am having no luck.
The entire JSON string is displayed in var RAW00, and then split into var RAW01 & var RAW02.
All 3 of these are for testing - RAW00 is identical to msg
When they are split - I can access either, depending on what variable I start of with, but when I use RAW00 I cannot access the tutor section.
I will provide more detail if required, but my question is:
How do I see and access the tutor array in the second $.each (nested) block??]
Thanks :-)
success: function(msg)
{
var test = "";
var raw00 = {
"allData": [
{
"class2": [
{
"tid": "1",
"name": "Monday 2"
},
{
"tid": "1",
"name": "Monday Test"
}
]
},
{
"tutor": [
{
"fname": "Jeffrey",
"lname": "Kranenburg"
},
{
"fname": "Jeffrey",
"lname": "Kranenburg"
}
]
}
]
};
var raw01 = {
"allData": [
{
"class2": [
{
"tid": "1",
"name": "Monday 2"
},
{
"tid": "1",
"name": "Monday Test"
}
]
}
]
};
var raw02 = {
"allData": [
{
"tutor": [
{
"fname": "Jeffrey",
"lname": "Kranenburg"
},
{
"fname": "Jeffrey",
"lname": "Kranenburg"
}
]
}
]
};
$.each(raw00.allData, function(index, entry)
{
$.each(entry.class2, function (index, data)
{
console.log(this.name);
test += '<tr><td>'+this.name+'</td>';
});
$.each(entry.tutor, function (index, data)
{
console.log(this.fname);
test += '<td>'+this.name+'</td></tr>';
});
$('#all-courses-table-content').html( test );
});
You need to check whether the current element of the array is an object with class2 property or tutor property.
$.each(raw00.allData, function(index, entry) {
if (entry.hasOwnProperty('class2')) {
$.each(entry.class2, function (index, data)
{
console.log(this.name);
test += '<tr><td>'+this.name+'</td>';
});
}
if (entry.hasOwnProperty('tutor')) {
$.each(entry.tutor, function (index, data)
{
console.log(this.fname);
test += '<td>'+this.fname+'</td></tr>';
});
}
$('#all-courses-table-content').html( test );
});
Things would probably be simpler if you redesigned the data structure. It generally doesn't make sense to have an array of objects when each object just has a single key and it's different for each. I suggest you replace the allData array with a single object, like this:
var raw00 = {
"allData": {
"class2": [
{
"tid": "1",
"name": "Monday 2"
},
{
"tid": "1",
"name": "Monday Test"
}
],
"tutor": [
{
"fname": "Jeffrey",
"lname": "Kranenburg"
},
{
"fname": "Jeffrey",
"lname": "Kranenburg"
}
]
}
};

Reading a JSON output in JavaScript with a lot of objects

This is my JSON output:
[
{
"Business": [
{
"id": "5739"
},
{
"userid": ""
},
{
"name": "Ben Electric"
},
{
"description": ""
},
{
"address": ""
},
{
"email": "*****#gmail.com"
},
{
"phone2": "050*****88"
},
{
"phone3": ""
},
{
"mobile": "050****88"
},
{
"opentimes": ""
},
{
"services": ""
},
{
"places": ""
},
{
"logo": null
},
{
"image": null
},
{
"video": ""
},
{
"owner_name": "Ben Brant"
},
{
"owners": "1"
},
{
"userpic": "http://graph.facebook.com/****/picture"
},
{
"circle": "3"
},
{
"fc": "0"
},
{
"rating_friends": ""
},
{
"rating_global": "3.3333"
},
{
"advice": ""
},
{
"subscription": "none"
}
]
},
{
"Business": [
{
"id": "5850"
},
{
"userid": ""
},
{
"name": "Bla Bla"
},
{
"description": ""
},
{
"address": ""
},
{
"email": "*****#gmail.com"
},
{
"phone2": ""
},
{
"phone3": ""
},
{
"mobile": "0*****995"
},
{
"opentimes": ""
},
{
"services": ""
},
{
"places": ""
},
{
"logo": null
},
{
"image": null
},
{
"video": ""
},
{
"owner_name": "Ben VBlooo"
},
{
"owners": "1"
},
{
"userpic": "http://graph.facebook.com/******/picture"
},
{
"circle": "3"
},
{
"fc": "0"
},
{
"rating_friends": ""
},
{
"rating_global": "2.0000"
},
{
"advice": ""
},
{
"subscription": "none"
}
]
},
{
"Info": {
"message": "No user for the business"
}
},
{
"OK": {
"message": "By Circle"
}
}
]
I'm trying to get the objects in javascript in this way but it doesnt work, should i loop through each Business object?? is there a way to access the real data objects directly?
Here's what I'm trying:
$.ajax({
type: 'POST',
url: 'BLABLA',
data: { BLABLA },
dataType: 'json',
success: function( resp ) {
if(resp.length == 0) {
$('.searchol').append('<li>No results found.</li>');
return;
}
$.each(resp, function(index, element) {
$('.searchol').append('Users Picture: '+element.Business.userpic);
But I cant seem to get to the object?
I just tried this code using your sample json like that
$.each(resp, function(index,element){
$.each(element, function(ind,ele){
if(ele.length){
$.each(ele,function(ind,ele){
if(ele.userpic)
console.log(ele.userpic)
})
}
})
})
"Business" is referring to an array (square bracket), so element.Business.userpic does not exist (element.Business[0].userpic exists though). Depending on what you want to achieve, you'll either have to loop through Business or access userpic of a particular array item.
Your business object is a array of object
"Business": [
{
"id": "5850"
},
Check this JSFiddle script on how to read that
Sample output
Picture: undefined (index):192
Picture: http://graph.facebook.com/****/picture
This will help you out
$.each(resp, function(index, element) {
$('.searchol').append('Users Picture: '+element.Business["userpic"]);
Your JSON is weird. Instead of :
Business : [
{ id : 'id1' }
{ name : 'name1' }
]
Business[0].id // access id
Business[1].name // access name
Where you have to remember where each attribute is in the array (or loop over the array to find it), you should have:
Business : {
id : 'id1',
name : 'name1'
}
Business.id // access id
Business.name // access name
If you can't change the JSON, you can use the following 2 methods to quickly get a property of Business:
var propMap = {
id : 0,
userid : 1,
name : 2 // etc
}
function getBusinessProp(business, prop) {
return business[propMap[prop]][prop];
}
// usage :
$('.searchol').append('Users Picture: '+ getBusinessProp(element.Business, 'userpic'));
If your array can be missing some items or the items can be in a different order for each business, then you need to iterate to find the property you're interested in:
function getBusinessProp(business, prop) {
for (var i=0; i<business.length; i++) {
if (business[i].hasOwnProperty(prop)) {
return business[i][prop];
}
}
}
// same usage, no need for the map anymore
The second method is probably better because it won't break if you change the order of the array or add new items in the array, etc and the performance boost given by using the map is probably not enough to justify the added maintenance cost.

Categories

Resources