Nested array recursion - NodeJS - javascript

I have a nested array containing children on a dynamic amount of levels, i want to generate a conditional tree based on this array.
An example of the array:
[
{
condition: 'conditionA',
children: [
{
condition: 'conditionA_1',
children: [
...
]
},
]
},
{
condition: 'conditionB',
children: [
...
]
}
]
I would like to generate a string that hold following conditional statement
if (conditionA) {
if (conditionA_1) {
...
}
} else if (conditionB) {
...
}
Does anybody have any ideas how to handle this properly?
Thanks in advance.

Without indentation:
Just map each node in the array to if(condition) { ... } (recursively), then join the resulting blocks with " else ":
function makeSource(arr) {
return arr.map(function(node) {
return "if (" + node.condition + ") { " + (node.children? makeSource(node.children): "") + " }";
}).join(" else ");
}
Demo:
function makeSource(arr) {
return arr.map(function(node) {
return "if (" + node.condition + ") { " + (node.children? makeSource(node.children): "") + " }";
}).join(" else ");
}
var array = [ { condition: 'conditionA', children: [ { condition: 'conditionA_1'} ] }, { condition: 'conditionB' } ];
var source = makeSource(array);
console.log(source);
With indentation:
To achieve the indentation we'll need a variable that holds the depth of the current block. Just repeat the space character before every line in the resulting string depending on the depth variable. Increate depth at each recursive call:
function makeSource(arr, depth = 0) {
return arr.map(function(node) {
var str = " ".repeat(depth * 2) + "if (" + node.condition + ") {\n";
if(node.children) {
str += makeSource(node.children, depth + 1);
} else {
str += " ".repeat((depth + 1) * 2); // unecessary, it just indents the empty line to where the code should be
}
return str + "\n" + " ".repeat(depth * 2) + "}";
}).join(" else ");
}
The * 2 parts represent the indentation number. If you want to indent by 4 spaces instead, then replace them with * 4.
Demo:
function makeSource(arr, depth = 0) {
return arr.map(function(node) {
var str = " ".repeat(depth * 2) + "if (" + node.condition + ") {\n";
if(node.children) {
str += makeSource(node.children, depth + 1);
} else {
str += " ".repeat((depth + 1) * 2);
}
return str + "\n" + " ".repeat(depth * 2) + "}";
}).join(" else ");
}
var array = [ { condition: 'conditionA', children: [ { condition: 'conditionA_1'} ] }, { condition: 'conditionB' } ];
var source = makeSource(array);
console.log(source);

const input = [
{
condition: 'conditionA',
children: [
{
condition: 'conditionA_1',
children: [
]
},
]
},
{
condition: 'conditionB',
children: [
]
}
]
const createSource = arr =>{
let source = "";
if(arr.length === 0){
return source;
}
arr.forEach((value,index) =>{
source+="if( "+value.condition+" ){";
if(value.children){
source+=createSource(value.children)
}
source+="}" + (index+1 < arr.length ? " else " : "");
})
return source;
}
console.log(createSource(input))

Related

How to make variables optional while concatenation of string?

I am manipulating string to display in UI, Data is being dynamically with below code sometime i don't get header and details so how to make IHeader and IResponse optional for the string concatenation below.
if i dont have IHeader it will break at IHeader.id and throw exception i want to display whatever data is available to render.
main.js
const data = [{
id: "header",
name: "IHeader"
}, {
id: "param",
name: "IParams"
}, {
id: "details",
name: "IResponse"
}]
function buildText(data) {
var IParams;
var IResponse;
var IHeader;
for (var item of data) {
if (item.id === "param") {
IParams = item;
} else if (item.id === "header") {
IHeader = item;
} else if (item.id === "details") {
IResponse = item;
}
}
var text = '';
text += app + '.setConfig({\n' + "env:" + getEnv() + '\n});' + '\n\n';
text += 'let param:' + IParams.name + ' ' + '=' + '' + JSON.stringify(request, null, 4) + ';\n\n';
text += ref + '(' + 'param,(result:' + ' ' + '{' + '\n' + IHeader.id + ':' + IHeader.name + '\n' + IResponse.id + ':' + IResponse.name + '\n' + '})' + ' ' +
' => {\n console.log(result); \n});';
}
1 - You can try to create an object with empty values. That'll prevent the exception.
emptyObject = {id: ""} // more empty keys, if there is
IParam = (item.id === "param") ? item : emptyObject
2 - Or ignore that concatenation of the variable if undefined or null.
if (Iparam) {
// concatenation ..
}

Looping over JavaScript object and adding string to end if not the last item

{
field_country: ["England", "Netherlands", "India", "Italy"],
field_continent: ["Europe"],
field_group: ["Building", "People", "Landscape"
}
I want to loop over each item and return the key and the array together with ending 'OR' for example:
field_country: "England" OR field_country: "Netherlands"
The last item should not end with 'OR' in the loop. I am not sure what the best process is for this using vanilla JS. So far my code is as follows:
Object.keys(facets).forEach(function(facetKey) {
if (facets[facetKey].length > 1) {
facetResults = facets[facetKey];
for (var i = 0; i < facetResults.length; i ++) {
if (i == 1) {
filter = "'" + facetKey + "'" + ":'" + facetResults[i] + " OR";
return filter;
} else {
filter = "'" + facetKey + "'" + ":'" + facetResults[i];
}
}
} else {
filter = "'" + facetKey + "'" + ": " + facets[facetKey] + "'";
return filter;
}
});
I would be very grateful for any assistance.
Thanks in advance.
You can do something like this with Object.entries and Array.reduce if you would like to get the final result in the form of an object:
const data = { field_country: ["England", "Netherlands", "India", "Italy"], field_continent: ["Europe"], field_group: ["Building", "People", "Landscape"] }
const result = Object.entries(data).reduce((r, [k, v]) => {
r[k] = v.join(' OR ')
return r
}, {})
console.log(result)
It is somewhat unclear what is the final format you need to result in but that should help you to get the idea. If ES6 is not an option you can convert this to:
const result = Object.entries(data).reduce(function(r, [k, v]) {
r[k] = v.join(' OR ')
return r
}, {})
So there are is no arrow function etc.
The idea is to get the arrays into the arrays of strings and use the Array.join to do the "replacement" for you via join(' OR ')
Here's the idea. In your code you are appending " or " at the end of your strings starting at index 0. I suggest you append it at the the beginning starting at index 1.
var somewords = ["ORANGE", "GREEN", "BLUE", "WHITE" ];
var retval = somewords[0];
for(var i = 1; i< somewords.length; i++)
{
retval += " or " + somewords[i];
}
console.log(retval);
//result is: ORANGE or GREEN or BLUE or WHITE
Your conditional expression if (i == 1) would only trigger on the second iteration of the loop since i will only equal 1 one time.
Try something like:
if (i < (facetResults.length - 1)) {
// only add OR if this isn't the last element of the array
filter = "'" + facetKey + "'" + ":'" + facetResults[i] + " OR";
return filter;
}
Here's your updated code:
Object.keys(facets).forEach(function(facetKey) {
if (facets[facetKey].length > 1) {
facetResults = facets[facetKey];
for (var i = 0; i < facetResults.length; i ++) {
if (i < (facetResults.length - 1)) {
filter = "'" + facetKey + "'" + ":'" + facetResults[i] + " OR";
return filter;
} else {
filter = "'" + facetKey + "'" + ":'" + facetResults[i];
}
}
} else {
filter = "'" + facetKey + "'" + ": " + facets[facetKey] + "'";
return filter;
}
});

How to use assignment instead of setting value?

I'm trying to create a markdown editor. Now in this function I'm working on B (bold) button which is toggle. It should be noted that I use this library to get/set highlighted text from textarea.
Here is my function: (It works as well, all fine)
function toggleText(before, after) {
var $textarea = $('#qandatextarea');
var textarea = $textarea[0];
var sel = $textarea.getSelection();
var val = textarea.value;
var posStart = sel.start;
var posEnd = posStart + sel.length;
var posBefore = posStart - before.length;
if (val.substr(posBefore, before.length) == before && val.substr(sel.end, after.length) == after) {
textarea.value = val.slice(0, posBefore) + sel.text + val.slice(sel.end + after.length);
$textarea.setSelection(posBefore, posBefore + sel.length);
} else {
$textarea.surroundSelectedText(before, after);
while(val.substr(posStart, 1) == ' ') {
posStart++;
$textarea.val( textarea.value.replaceAt(posStart + 1, "*").replaceAt(posStart - 1, " ") );
}
while(val.substr(posEnd - 1, 1) == ' ') {
$textarea.val( textarea.value.replaceAt(posEnd + 1, "*").replaceAt(posEnd + after.length + 1, " ") );
posEnd--;
}
// set new highlighted-text
$textarea.setSelection(posStart + before.length, posEnd + before.length);
} // else
} // function
So for making it more optimize, I have used assignment instead of $textarea.val( in the loop, but now it doesn't work correctly, it replaces wrong characters and handles spaces badly.
.
.
.
else {
var txtval;
$textarea.surroundSelectedText(before, after);
while(val.substr(posStart, 1) == ' ') {
posStart++;
txtval = textarea.value.replaceAt(posStart + 1, "*").replaceAt(posStart - 1, " ");
}
while(val.substr(posEnd - 1, 1) == ' ') {
txtval = textarea.value.replaceAt(posEnd + 1, "*").replaceAt(posEnd + after.length + 1, " ");
posEnd--;
}
// set textarea value
$textarea.val(txtval);
$textarea.setSelection(posStart + before.length, posEnd + before.length);
}
.
.
.
It seems that there are two problems with your code:
1) If the character ("index, 1" or "index - 1, 1") is not a space then the while-loop will be bypassed/skipped and no further replacements will be done.
2) You never update the value of the textarea OR the assigned variable ("val") meaning you are working with the same string without actually replacing anything. You should probably be using "val = ..." in stead of "txtval = ..." inside your while-loops and in the final "set textarea value": $textarea.val(val);
// ... code ...
while(val.substr(posStart, 1) == ' ') {
posStart++;
val = val.replaceAt(posStart + 1, "*").replaceAt(posStart - 1, " ");
}
while(val.substr(posEnd - 1, 1) == ' ') {
val = val.replaceAt(posEnd + 1, "*").replaceAt(posEnd + after.length + 1, " ");
posEnd--;
}
// set textarea value
$textarea.val(val);
// ... code...

How do I get values from an array?

I have an array which is in the following format:
RBS: [ {
"RegExp": "",
"Type": ""
} ],
PAN: [ {
"RegExp": "Date",
"Type": "Date"
} ]
Now I want to pass the value PAN to a method and it should get the count of PAN length 1 and get PAN of regex values and type value. How can I do this? I formed an array like this: Name holds RBS and PAN:
var Regexp = [];
RegExpr.push(Name + ":" + Regexp);
function Check(test) {
//test will be RBS /PAN
}
var obj = {
RBS:[{"RegExp":"","Type":""}],
PAN:[{"RegExp":"Date","Type":"Date"}]
};
function getTypeAndValue( obj, value )
{
var output;
var keys = Object.keys( obj );
if ( obj [value ] )
{
output = obj[ value ][ 0 ];
}
return output;
}
var value = getTypeAndValue( obj, "PAN" );
if ( value )
{
console.log( "type is " + value.Type + " and RegExp is " + value.RegExp );
}
else
{
console.log( "this value doesn't exists" );
}
You mean something like this?
HTML
<div id="count"></div>
<div id="detail"></div>
JAVASCRIPT
var countEl = document.getElementById("count");
var detailEl = document.getElementById("detail");
function Check(test){
var count = test.length;
countEl.innerHTML = "Count: " + count;
detailEl.innerHTML = "";
for (var i = 0 ; i < count ; i++){
var values = test[i];
detailEl.innerHTML +=
"<br/>" + (i +1) + ":<br/>" +
"RegExp: " + values["RegExp"] + "<br/>" +
"Type: " + values["Type"] + "<br/>";
}
}
var Name = {
RBS: [ {
"RegExp": "",
"Type": ""
} ],
PAN: [ {
"RegExp": "Date",
"Type": "Date"
} ]
};
Check(Name.PAN);
DEMO
Here's a JSFiddle.

javascript convert JSON string to JSON object

I al using javascript and looping through the values of a submitted form and trying to build a son object out of the form values.
This is an example of the final object I need:
{
"DataObject": {
"user": { "-name": "username" },
"contentFile": {
"-filename": "Breaking_News",
"lock": { "-fileIsBeingEdited": "false" },
"content": {
"line": [
{
"-index": "1",
"-text": "this is the header"
},
{
"-index": "2",
"-text": "this is the first line"
},
{
"-index": "3",
"-text": "this is the second line"
}
]
}
}
}
}
So far i am adding all of this data to a string as that seems to be the only way i can insert the form values (the line array) into the middle of the object.
var jsonStr = '{'
+ 'iceteaDataObject: {'
+ 'user: {"-name": "hindsc52"},'
+ 'contentFile: {'
+ '"-filename": "Ticker",'
+ 'lock: { "-fileIsBeingEdited": "false" },'
+ 'content: {'
+ 'line: ['
for(var i = 0; i < elem.length; i++) {
if(!elem[i].value == '') {
jsonStr += '{'
jsonStr += "-index: " + i + ',';
jsonStr += "-text: " + elem[i].value;
jsonStr += '},'
}
}
jsonStr += ']}}}}';
console.log(JSON.parse(jsonData));
however when running this I get the error: unexpected token 'i'.
I have tried to use stringily but then just outputs the entire sting again.
You don't need or want JSON for this, just build the object:
// Sample data
var elem = [{
value: "one"
}, {
value: "two"
}];
// Build the object
var obj = {
"DataObject": {
"user": {
"-name": "username"
},
"contentFile": {
"-filename": "Breaking_News",
"lock": {
"-fileIsBeingEdited": "false"
},
"content": {
"line": []
}
}
}
};
var line = obj.DataObject.contentFile.content.line;
elem.forEach(function(entry, index) {
if (entry.value != '') {
line.push({
"-index": index,
"-text": entry.value
});
}
});
// Show result:
document.body.innerHTML =
"<pre>" +
JSON.stringify(obj, null, 2) +
"</pre>";
Side note: You don't check for blank strings like this:
if (!entry.value == '') { // <== Incorrect
You can use:
if (entry.value != '') {
or:
if (entry.value) {
You shouldn't build JSON like this, but use JSON.stringify() (see MDN doc) instead:
var myObject={foo:"bar"};
var myJSON=JSON.stringify(myObject);
console.log(myJSON); //echo {"foo":"bar"}
Here is an alternative way:
var json = {
iceteaDataObject: {
"-name": "hindsc52"
},
contentFile: {
"-filename": "Ticker",
lock: { "-fileIsBeingEdited": "false" },
content: {line: []}
}
}
for(var i = 0; i < elem.length; i++) {
if(!elem[i].value == '') {
json.contentFile.content.line.push({"-index": i,"-text": elem[i].value }
}
}
var jsonStr = JSON.stringify(json);
You need to put all your keys in quotes for this to work. As the others have pointed out though, you are not really supposed to do this.
If you still want to do it your way, try this:
var jsonStr = '{'
+ '"iceteaDataObject": {'
+ '"user": {"-name": "hindsc52"},'
+ '"contentFile": {'
+ '"-filename": "Ticker",'
+ '"lock": { "-fileIsBeingEdited": "false" },'
+ '"content": {'
+ '"line": ['
for(var i = 0; i < elem.length; i++) {
if(!elem[i].value == '') {
jsonStr += '{'
jsonStr += '"-index": ' + i + ',';
jsonStr += '"-text": ' + '"' + elem[i].value + '"';
jsonStr += '},'
}
}
jsonStr += ']}}}}';

Categories

Resources