recursively build a tree from an array javascript - javascript

From a .csv file I need to build a tree structure using three columns below.
Column state should be the parent node, id is a child node of state and ndi is a child of id node.
Currently my program can read the .csv file and store it in a two dimensional array.
Here is my code: Link to .csv file
<body>
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>
<script>
function handleFileSelect(evt) {
var files = evt.target.files;
for (var i = 0, f; f = files[i]; i++) {
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var string = e.target.result;
console.log(string);
var array1=string.split("\n");
alert(array1.length)
var array2 = new Array();
for(var i=0; i< array1.length; i++){
array2[i] = array1[i].split(",");
}
//completed upto saving as 2D array
};
})(f);
reader.readAsText(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>
I want to build a tree structure out of it.

This should do it, replace your second for with the code below:
// We cannot define our own keys for Javascript arrays, so we are using Object
var result = new Object();
var salesIndex = 8;
var idIndex = 0;
var ndiIndex = 7;
for(var i=0; i< array1.length; i++){
// array containing all line's columns
var columns = array1[i].split(",");
// especific columns that is of our interest
var rowSale = columns[salesIndex];
var rowId = columns[idIndex];
var rowNdi = columns[ndiIndex];
// If sale key doesn't exists in the array, add it
if (!(rowSale in columns)) {
result.rowSale.rowId = new Array();
result.rowSale.rowId.push(rowNdi);
} else if (!(rowId in columns.rowSale)) {
// If id key doesn't exists in the array, add it
result.rowSale.rowId = new Array();
result.rowSale.rowId.push(rowNdi);
} else if (!columns.rowSale.rowId.indexOf(rowNdi)) {
// If ndi isn't in the array, add it
result.rowSale.rowId.push(rowNdi);
}
}

If I did'nt missunderstand you, this should do the trick:
function buildTree(input) {
var output = {};
for (var i in input) {
var state = input[0];
var id = input[1];
var ndi = input[2];
if (output[state] === undefined) output[state] = {};
if (output[state][id] === undefined) output[state][id] = [];
output[state][id].push(ndi);
};
return output;
};

Related

How to make a simple catalog that is updated using external file?

I want to make product catalog, suppose 1-4 products in a row, and each product will be in a box with its details and price mentioned. I would like to be able to easily create the catalog based on a XML/CSV File which I can update.. Can any one point me in the right direction on how to achieve it. I am currently familiar with HTML/CSS.. I can pick up on Javascript if required.
Once finished the page will have a header, and then products displayed below.. I intend to print the page to PDF to share with other users..
First of all you should read your file and load from javascript.
Then once loaded, you parse it into JSON object
After this, you can preview the contents inside the HTML
(this is important because we'll use this html table to generate the pdf)
Using the library jsPDF and it's plugin AutoTable we generate a pdf file with the HTML table we generated previously.
This are the sample CSV file I used to test the example
Product,Price,Barcode
Sample product 1,100,802760000926
Sample product 2,95,802760000926
Sample product 3,20,802760000926
You can try it in my fiddle:
https://jsfiddle.net/rogeliomonter/2f9m0qse/
let myList = {};
/*Function to load from CSV file*/
function openFile(event) {
var input = event.target;
var node = document.getElementById('output');
node.innerText = '';
var reader = new FileReader();
reader.onload = function () {
text = reader.result;
//set to myList variable to be used later
myList = JSON.parse(csvJSON(reader.result));
buildHtmlTable('#output');
};
reader.readAsText(input.files[0]);
};
/*this function generates the HTML table*/
function buildHtmlTable(selector) {
var columns = addAllColumnHeaders(myList, selector);
for (var i = 0; i < myList.length; i++) {
var row$ = $('<tr/>');
for (var colIndex = 0; colIndex < columns.length; colIndex++) {
var cellValue = myList[i][columns[colIndex]];
if (cellValue == null) cellValue = "";
row$.append($('<td/>').html(cellValue));
}
$(selector).append(row$);
}
}
/*Supports the function that generates the HTML table*/
function addAllColumnHeaders(myList, selector) {
var columnSet = [];
var headerTr$ = $('<tr/>');
for (var i = 0; i < myList.length; i++) {
var rowHash = myList[i];
for (var key in rowHash) {
if ($.inArray(key, columnSet) == -1) {
columnSet.push(key);
headerTr$.append($('<th/>').html(key));
}
}
}
$(selector).append(headerTr$);
return columnSet;
}
/*Converts CSV values into JSON object*/
function csvJSON(csv) {
var lines = csv.split("\n");
var result = [];
var headers = lines[0].split(",");
for (var i = 1; i < lines.length; i++) {
var obj = {};
var currentline = lines[i].split(",");
for (var j = 0; j < headers.length; j++) {
obj[headers[j]] = currentline[j];
}
result.push(obj);
}
//return result; //JavaScript object
return JSON.stringify(result); //JSON
}
/*Uses jsPDF libary to generate a PDF File from the HTML table*/
function download() {
// Default export is a4 paper, portrait, using millimeters for units
const doc = new jsPDF();
doc.text("My List", 10, 10);
var columns = ["Product", "Price", "Barcode"];
//Here we use the id of the table
doc.autoTable({ html: '#output' })
doc.save("myList.pdf");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.4/jspdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.6/jspdf.plugin.autotable.min.js"></script>
<input type='file' accept='text/csv' onchange='openFile(event)'>
<br>
<!-- the HTML table that will have the csv table -->
<table id='output' border="1"></table>
<br>
<button onclick="download()">Generate PDF</button>

XML data to HashMap

I need to store XML data to a hashmap, I'm using nodejs and xmldom npm module to parse the XML.
I'm trying to store the testsuite name, testcase name and dt_value in a hashmap.
here is my XML code
<testscenario>
<testsuite name="com.edge.route">
<testcase name="tc_Login">dt_Login</testcase>
<testcase name="tc_Logout">dt_Logout</testcase>
</testsuite>
<testsuite name="com.edge.beacon">
<testcase name="tc_Channel">dt_Channel,dt_Logout</testcase>
</testsuite>
</testscenario>
Here's what I have tried so far
var DOMParser = require('xmldom').DOMParser;
var parser = new DOMParser();
var HashMap = require('hashmap');
var fs = require('fs');
module.exports = {
testScenario: function() {
var suiteName;
var data;
var map = new HashMap();
//read the testscenario.xml
data = fs.readFileSync("./testscenario.xml", "utf8");
var dom = parser.parseFromString(data);
var testSuiteList = dom.getElementsByTagName("testsuite");
//loop through all the test suites
for (i = 0; i < testSuiteList.length; i++) {
//select the test suite with the given name
suiteName = testSuiteList[i].getAttribute("name");
var tcList = testSuiteList[i].getElementsByTagName("testcase");
var dtList = testSuiteList[i].getElementsByTagName("testcase")[0].childNodes[0].nodeValue;
console.log(dtList)
//get the row count
tcLength = tcList.length;
//push column headers as the key in the hashmamp
var testCaseList = [];
for (x = 0; x < tcList.length; x++) {
testCaseList.push(tcList[x].getAttribute("name"));
}
console.log(testCaseList)
var dataTableList = [];
for (i = 0; i < tcLength; i++) {
dataTableList += tcList[i].childNodes[0].nodeValue;
}
console.log("dtlist = " + dataTableList);
//push the row values as an array to the hashmap
map.set(suiteName, testCaseList);
}
return [map]
}
};
I'm able to get the key, value pair for testsuite and testcase but I also need to get the dt_name. how can I modify this code to store the dt_name along with testsuite and testcase names in that hashmap?
Alright figured it out. This is how I did it. I have used a hashmap within a hashmap
//XML Reader
var DOMParser = require('xmldom').DOMParser;
var parser = new DOMParser();
var HashMap = require('hashmap');
var fs = require('fs');
module.exports={
testScenario: function ()
{
var suiteName;
var data;
var map = new HashMap();
//read the testscenario.xml
data=fs.readFileSync("./testscenario.xml","utf8");
var dom = parser.parseFromString(data);
var testSuiteList = dom.getElementsByTagName("testsuite");
//loop through all the test suites
for (i=0;i< testSuiteList.length; i++) {
//select the test suite with the given name
suiteName = testSuiteList[i].getAttribute("name");
var tcList = testSuiteList[i].getElementsByTagName("testcase");
//get the row count
Length=tcList.length;
//push column headers as the key in the hashmamp
var testCaseList = new HashMap();
for(x=0;x<Length;x++)
{
testCaseList.set(tcList[x].getAttribute("name"),tcList[x].childNodes[0].nodeValue);
}
//push the row values as an array to the hashmap
map.set(suiteName,testCaseList);
}
return [map]
}
};

How to work with FileList (from <input type="file">) in Javascript?

In this W3schools example, console.log on the input element reveals a FileInput object:
FileList {0: File, 1: File, length: 2}
How can I work with this? The example demonstrates accessing the file, but every time a user selects new files, the old files disappear. How can I create a new empty FileList and copy it over, so that a user can add more files to the FileList?
I tried this, but it results in two FileList objects, rather than one FileList with all the files:
var fileStore = x.files;
function myFunction(){
var txt = "";
if ('files' in x) {
if (x.files.length == 0) {
txt = "Select one or more files.";
} else {
fileStore += x.files;
console.log(x.files);
console.log(fileStore);
Untested, but this should work
var fileStore = [];
function myFunction(){
var txt = "";
if ('files' in x) {
if (x.files.length == 0) {
txt = "Select one or more files.";
} else {
fileStore.push.apply(fileStore,x.files);
console.log(x.files);
console.log(fileStore);
More on Function::apply
More on Array::push
It is not possible to add File objects to FileList. You can use FormData to append Files to a single object.
var data = new FormData();
document.querySelector("input[type=file]")
.addEventListener("change", function(event) {
for (var i = 0, files = event.target.files; i < files.length; i++) {
data.append("file-" + [...data.keys()].length, files[i], files[i].name)
}
})
An array is fine for holding onto the File instances, but FormData is better if you want to upload them somewhere. If you want to log out or view the FormData, turning it into a Map is an option. Keep in mind that FormData is iterable.
var formData = new FormData();
var index = 0;
function onDrop(event)
{
var dt = event.dataTransfer;
var files = dt.files;
var count = files.length;
output("File Count: " + count + "\n");
for (var i = 0; i < files.length; i++) {
formData.append(files[i].name, files[i]);
}
}
function output(text)
{
document.getElementById("output").textContent += text;
console.dir(new Map(formData));
}
See this JSBin.
it is possible to add files using the datatransfer class
export const makeFileList = files => {
const reducer = (dataTransfer, file) => {
dataTransfer.items.add(file);
return dataTransfer;
}
return files.reduce(reducer, new DataTransfer()).files;
}

FileReader output saved to Array

I would like to ask you, if it is possible to iterate through list of file inputs and save the results in one array. Here is bit of my code.
Also I use AngularJS if it helps...
HTML
<input type="file" id="file1">
<input type="file" id="file2">
<button id="saveBtn">Save</button>
JAVASCRIPT
results = []; // array for storing results
/*file inputs*/
inputs = [document.getElementById('file1'), document.getElementById('file2')];
function readFile() {
for (var i = 0; i < inputs.length(); i++) {
if (inputs[i].files && inputs[i].files[0]) {
var FR = new FileReader();
FR.onload = function (event) {
results[i] = event.target.result; //array where I would like to store results
};
FR.readAsDataURL(inputs[0].files[0]);
}
}
}
//here I would like to write down all results
var btn = document.getElementById('saveBtn');
btn.onclick = function() {
readFile();
for(var i=0; i < inputs.length(); i++) {
console.log(results[i]);
}
}
When I run this code, as result I get 'undefined'.
Do you guys have any ideas how to achieve this? Thank you
Besides the point that Niek Vandael makes in his answer; the fact that you are trying to show information before it has completed loading, there are a few other issues with your code.
input.lengths()
should be
input.lengths
lengths it's not a method. That causes an error when I test the code (in Chrome).
You also read the same data over and over;
FR.readAsDataURL(inputs[0].files[0]);
should probably be
FR.readAsDataURL(inputs[i].files[0]);
So, here's another take on it. I added a couple of vars to keep track of items loaded and how many there are to load, then call the function to display the data once it has been loaded.
results = []; // array for storing results
/*file inputs*/
inputs = [document.getElementById('file1'), document.getElementById('file2')];
//here I would like to write down all results
var btn = document.getElementById('saveBtn');
var inputCount;
var filesLoaded = 0;
function readFile() {
inputCount = Number(inputs.length);
for (var i = 0; i < inputCount ; i++) {
if (inputs[i].files && inputs[i].files[0]) {
var FR = new FileReader();
FR.onload = function (event) {
results.push(event.target.result); //array where I would like to store results
filesLoaded++;
if (filesLoaded == inputCount) {
showResult();
}
};
FR.readAsDataURL(inputs[i].files[0]);
}
}
}
btn.onclick = function () {
readFile();
}
function showResult() {
for (var i = 0; i < inputs.length ; i++) {
console.log(results[i]);
}
}
There are a few more things to think about, such as validating that files have been selected etc, but I guess it's kind of out of the scope here.
onLoad gets executed when the FileReader has loaded, so after you write your log statements.
Try this:
https://jsfiddle.net/s17Lmc21/1/
results = []; // array for storing results
/*file inputs*/
inputs = [document.getElementById('file1'), document.getElementById('file2')];
function readFile() {
for (var i = 0; i < inputs.length; i++) {
if (inputs[i].files && inputs[i].files[0]) {
var FR = new FileReader();
FR.onload = function (event) {
results[i] = event.target.result; //array where I would like to store results
console.log(results[i]);
};
FR.readAsDataURL(inputs[0].files[0]);
}
}
}
//here I would like to write down all results
var btn = document.getElementById('saveBtn');
btn.onclick = function() {
readFile();
}

parsing xml in javascript

I need to create custom objects based on an XML input, the rule is that for every node if it has a direct child node is named EndNode and the text value of which is 1, then I create a leaf object. So for every node, I need to check the direct child with name EndNode and its value. It's not so easy with the DOM API and DOM selector (in this case I use Ext.DomQuery) doesn't have a way to select direct child of the root node... Below is my attempt for using DOM selector, I need to wrap the node around with another level of the node for the selector to work. But I can't just say new Node(), it silently fails.
I guess I have to walk through n.childNodes, but it is complicated to do it this way to check the rule I described above. Any solution?
Ext.each(node.childNodes, function(n){
if (n.nodeType == this.XML_NODE_ELEMENT) {
var tmp=new Node();
console.log('hi');
tmp.appendChild(n);
console.log(Ext.DomQuery.select(n.tagName+">EndNode", tmp));
}
}
I did an xml parser. It is quite easy with Dojo's library. Here ya are. When you're done with it though I recommend exporting the var to JSON and using it as cache.
dojo.require("dojox.xml.parser");
var parser = dojox.xml.parser;
function crules() {
this.rules = new Array();
this.xml = Object;
}
xml = '';
crules.prototype.load = function(file){
var xmlget = dojo.xhrGet({
url: file,
handleAs: "xml",
load: function(data){
xml = data;
},
error: function (error) {
console.error ('Error: ', error);
},
sync: true
}
);
this.xml = xml;
}
crules.prototype.buildout = function (){
var rules = this.xml.getElementsByTagName('ruleset');
//dojo.byId('jsloading').innerHTML = 'Loading Javascript';
for(var i=0; i<rules.length; i++){
//dojo.byId('jsloading').innerHTML += ' .';
r = new cruleset();
r.name = xtagvalue(rules[i],'name');
base = xtag(rules[i],'base');
textcustom = xtag(rules[i],'textcustom');
r.textcustomy = xtagvalue(textcustom[0],'y');
r.textcustomx = xtagvalue(textcustom[0],'x');
for(var j=0; j<base.length; j++){
r.bases[j] = new cbase();
r.bases[j].imgsrc = xtagvalue(base[j],'imgsrc');
r.bases[j].color = xtagvalue(base[j],'color');
r.bases[j].coloropts = new Array();
var copts = xtag(rules[i],'option');
for(var k=0; k<copts.length;k++){
var cc = new Object();
cc.color = xtagvalue(copts[k],'color');
cc.imgsrc = xtagvalue(copts[k],'imgsrc');
r.bases[j].coloropts.push(cc);
}
}
zones = xtag(rules[i],'zone');
for(var j=0; j<zones.length; j++){
z = new czone();
z.name =xtagvalue(zones[j],'name');
zoneconfigs = xtag(zones[j],'zoneconfig');
for(var n=0; n<zoneconfigs.length; n++){
zc = new czoneconfig();
zc.name = z.name;
zc.x1 =xtagvalue(zones[j],'x1');
zc.y1 =xtagvalue(zones[j],'y1');
zc.w =xtagvalue(zones[j],'w');
zc.h =xtagvalue(zones[j],'h');
hotspots = xtag(zoneconfigs[n],'hotspot');
for(var k=0; k<hotspots.length; k++){
h = new chotspot();
h.name = xtagvalue(hotspots[k],'name');
h.x =xtagvalue(hotspots[k],'x');
h.y =xtagvalue(hotspots[k],'y');
h.nameyoffset = xtagvalue(hotspots[k],'nameyoffset');
h.accessoryonly = xtagvalue(hotspots[k],'accessoryonly');
if(h.accessoryonly == null){
h.accessoryonly = 0;
}
var showname = xtag(hotspots[k],'showname');
if(!isEmpty(showname)){
h.showname = xtagvalue(hotspots[k],'showname');
}
/*h.itemset =xtagvalue(hotspots[k],'itemset');*/
items = xtag(hotspots[k],'item');
if(items){
for(var l=0;l<items.length;l++){
t = new citem();
t.id = xtagvalue(items[l],'id');
h.items[h.items.length] = t;
}
}
zc.hotspots[zc.hotspots.length] = h;
}
z.zoneconfigs[z.zoneconfigs.length] = zc;
}
r.zones[r.zones.length] = z;
}
this.rules[this.rules.length] = r;
}
/*xmltext = parser.innerXML(xml);
dojo.byId('cwindow').innerHTML = xmltext;*/
}
function xtag(e,tag){
var n=null;
n = e.getElementsByTagName(tag);
if(n.length>=1){
return e.getElementsByTagName(tag);
}
else return null;
}
function xtagvalue(e,tag){
var n=null;
n = e.getElementsByTagName(tag);
if(n.length>=1){
//console.log(tag,'here',n[0],parser.textContent(n[0]));
return parser.textContent(n[0]);
}
else return null;
}

Categories