Using D3 to read data into associative arrays - javascript

I'm trying to loop through a set of values and read in separate files (using d3.js) into javascript associate arrays , but the argument is not being passed properly into the internal function. For example:
var reg_list = [];
var all_region_data = [];
reg_list[0]="region1";
reg_list[1]="region2";
// Function to read in data from all regions in reg_list.
function read_regional_data(region_list) {
for (i=0;i<reg_list.length;i++) {
region = region_list[i];
console.log("Read data for " + region) // THIS RETURNS REGION1, THEN
// REGION2
all_region_data[region]=new Array()
d3.csv('filepath' + region + '.csv', function(csv){
console.log('reading for ' + region) // THIS RETURNS REGION2 TWICE
csv.map(function(x) {
all_region_data[region].push(x.cause);
})
console.log("Finished Reading Data for " + region)
})
}
}
When I execute this, I iterate through both regions in the loop, but region1 is never passed into the d3.csv function. This may have something to do with the d3.csv being run asynchronously? Any thoughts on how to avoid or improve this are appreciated.

Use recursion instead of the loop to iterate over the regions --
var reg_list = [],
all_region_data = [];
reg_list[0]="region1";
reg_list[1]="region2";
function read_regional_data(i) {
var region = reg_list[i];
console.log("Read data for " + region);
all_region_data[region]=new Array();
d3.csv('filepath' + region + '.csv', function(csv){
console.log('reading for ' + region);
csv.map(function(x) {
all_region_data[region].push(x.cause);
});
console.log("Finished Reading Data for " + region);
if(i < reg_list.length) {
read_regional_data(i+1);
}
});
}
read_regional_data(0);

Related

How to prevent duplicate rows from being displayed in html?

I've followed and completed this tutorial https://github.com/dappuniversity/election/tree/2019_update. However, duplicate rows show up at the end when I'm adding new votes in (shown in picture).
I'm not familiar with dApps, web development, or javascript so I don't know where my error is.
Code from https://github.com/dappuniversity/election/tree/2019_update.
I don't know where adding the new rows came in and I'm trying to prevent it.
the problem is in the asynchronous nature of JavaScript the app is not waiting for the response of the blockchain before removing the old so what happens is that the data get inserted to the dom two times, the fix is to handle the promises differently. Group all the promise calls to get candidates to an array, then waiting until all of them are resolved to add them to the dom.
App.contracts.Election.deployed()
.then(function(instance) {
electionInstance = instance;
return electionInstance.candidatesCount();
})
.then(function(candidatesCount) {
const promises = [];
// Store all prosed to get candidate info
for (var i = 1; i <= candidatesCount; i++) {
promises.push(electionInstance.candidates(i));
}
// Once all candidates are received, add to dom
Promise.all(promises).then(candidates => {
var candidatesResults = $("#candidatesResults");
candidatesResults.empty();
var candidatesSelect = $("#candidatesSelect");
candidatesSelect.empty();
candidates.forEach(candidate => {
var id = candidate[0];
var name = candidate[1];
var voteCount = candidate[2];
// Render candidate Result
var candidateTemplate =
"<tr><th>" +
id +
"</th><td>" +
name +
"</td><td>" +
voteCount +
"</td></tr>";
candidatesResults.append(candidateTemplate);
// Render candidate ballot option
var candidateOption =
"<option value='" + id + "' >" + name + "</ option>";
candidatesSelect.append(candidateOption);
});
});
return electionInstance.voters(App.account);
})

Access JSON kind of data from a div?

So I have a div which displays the results from Microsoft emotion API such as:
anger: 0.28446418
contempt: 0.00341128884
disgust: 0.000332433876
fear: 0.009447911
happiness: 0.02609423
neutral: 0.6288482
sadness: 0.00180563633
surprise: 0.04559612
Here is the code for displaying the data:
.done(function(data) {
// Get face rectangle dimensions
var faceRectangle = data[0].faceRectangle;
var faceRectangleList = $('#faceRectangle');
var data1="";
// Append to DOM
for (var prop in faceRectangle) {
data1 += "<li> " + prop + ": " + faceRectangle[prop] + "</li>";
}
faceRectangleList.html(data1);
// Get emotion confidence scores
var scores = data[0].scores;
var scoresList = $('#scores');
var data2="";
// Append to DOM
for(var prop in scores) {
data2 += "<li> " + prop + ": " + scores[prop] + "</li>";
}
scoresList.html(data2);
}).fail(function(err) {
alert("Error: " + JSON.stringify(err));
});
});
}
function make_graph(){
}
Now I need to plot a line from these scores.Can you tell me how to access each separate value of these scores in another function so that I can plot them as points in my graph?
Just call make_graph from inside the done callback, passing it the data, like this.
.done(function(data) {
// ... do what you already do
make_graph(data);
}).fail(function (err) {
alert("Error: " + JSON.stringify(err));
});
function make_graph(data) {
for (d in data) {
// do stuff with data
}
}
From the given information, you can make a function like this
function drawLine(dataScores) {
for (var d in dataScores) {
// dataScores[d] will give you the score
// rest of the relevent code
}
}
and call it like this
drawLine(scores);
I think it is not good practice to write data in HTML DOM, then read it back.
But if You need this, try something like this
plotData = []
var scoresList = $('#scores')
scoresList.children('li').each(function(i) {
data = i.thml()
plotData.push(i.split(':').map(i => [i[0].trim(), parseFloat(i[1])]))
});
// here You have all data in plotData
// [['anger', 0.28446418], ...]
// Draw Your plotData here
If you want to read it back from div li, here is a jquery function to covert html to jquery data object
$(document).ready(function(){
var dataArr={};
$("#scoresli" ).each(function( index ){
data=$( this ).text().split(":");
dataArr[data[0]]=data[1]
});
alert(JSON.stringify(dataArr));
});
//{"anger":" 0.28446418\n","contempt":" 0.00341128884"}

Simplify nested promises within loops and closures

I wrote a ~50 lines script to perform housekeeping on MySQL databases. I'm afraid my code exhibits anti-patterns as it rapidly escalates to an unreadable mess for the simple functions it performs.
I'd like some opinions for improving readability.
The full script is at the bottom of this post to give an idea.
Spotlight on the problem
The excessive nesting is caused by patterns like this repeated over and over: (snippet taken from script)
sql.query("show databases")
.then(function(rows) {
for (var r of rows) {
var db = r.Database;
(function(db) {
sql.query("show tables in " + db)
.then(function(rows) {
// [...]
}
})(db);
}
});
I'm nesting one promise under the other within both a for loop and a closure. The loop is needed to iterate across all results from sql.query(), and the closure is necessary to pass the value of db to the lower promise; without the closure, the loop would complete even before the nested promise executes at all, so db would always contain only the last element of the loop, preventing the nested promise from reading each value of db.
Full script
var mysql = require("promise-mysql");
var validator = require("mysql-validator"); // simple library to validate against mysql data types
var ignoreDbs = [ "information_schema" ],
multiplier = 2, // numeric records multiplier to check out-of-range proximity
exitStatus = {'ok': 0, 'nearOutOfRange': 1, 'systemError': 2};
(function() {
var sql,
mysqlHost = "localhost",
mysqlUser = "user",
mysqlPass = "";
mysql.createConnection({
host: mysqlHost,
user: mysqlUser,
password: mysqlPass
}).then(function(connection) {
sql = connection;
})
.then(function() {
sql.query("show databases")
.then(function(rows) {
for (var r of rows) {
var db = r.Database;
if (ignoreDbs.indexOf(db) != -1) continue;
(function(db) {
sql.query("show tables in " + db)
.then(function(rows) {
for (var r of rows) {
var table = r["Tables_in_" + db];
(function(table) {
sql.query("describe " + db + "." + table)
.then(function(rows) {
for (var r of rows) {
(function(r) {
var field = r.Field,
type = r.Type, // eg: decimal(10,2)
query = "select " + field + " from " + db + "." + table + " ";
if (table != "nonce") query += "order by date desc limit 1000";
sql.query(query)
.then(function(rows) {
for (var r of rows) {
var record, err;
// remove decimal part, only integer range is checked
record = Math.trunc(r[field]);
err = validator.check(record * multiplier, type);
if (err) {
console.log(err.message);
process.exit(exitStatus.nearOutOfRange);
}
}
});
})(r);
}
});
})(table);
}
});
})(db);
}
});
})
.then(function() {
// if (sql != null) sql.end(); // may not exit process here: sql connection terminates before async functions above
//process.exit(exitStatus.ok); //
});
})();
Trivia
The purpose of the script is to automatically and periodically monitor if any record stored in any row, table and database in MySQL is approaching the out-of-range limit for its specific data type. Several other processes connected to MySQL continuously insert new numeric data with increasing values and nonces; this script is a central point where to check for such numeric limits. The script would then be attached to Munin for continuous monitoring and alerting.
Update: Revised script
As suggested by #Kqcef I modularized the anonymous functions out of the promise nest, and used let to avoid the explicit nesting of an additional function to preserve variable context.
Still this is excessively verbose, previously I wrote the same script in Bash in about 40 lines, but performance was screaming for a port to nodejs.
"use strict";
var mysql = require("promise-mysql");
var validator = require("mysql-validator"); // a simple library to validate against mysql data types
var ignoreDbs = [ "information_schema" ],
multiplier = 2, // numeric records multiplier to check out-of-range proximity
exitStatus = {'ok': 0, 'nearOutOfRange': 1, 'systemError': 2};
var mysqlHost = "localhost",
mysqlUser = "btc",
mysqlPass = "";
// return array of DBs strings
function getDatabases(sql) {
return sql.query("show databases")
.then(function(rows) {
var dbs = [];
for (var r of rows)
dbs.push(r.Database);
return dbs;
});
}
// return array of tables strings
function getTables(sql, db) {
return sql.query("show tables in " + db)
.then(function(rows) {
var tables = [];
for (var r of rows)
tables.push(r["Tables_in_" + db]);
return tables;
});
}
// return array of descriptions
function getTableDescription(sql, db, table) {
return sql.query("describe " + db + "." + table)
.then(function(rows) {
var descrs = [];
for (var r of rows) {
descrs.push({ 'field': r.Field, // eg: price
'type': r.Type}); // eg: decimal(10,2)
}
return descrs;
});
}
// return err object
function validateRecord(record, type) {
var record, err;
if (typeof record != "number") {
console.log("error: record is not numeric.");
process.exit(exitStatus.systemError);
}
// remove decimal part, only integer range is checked
record = Math.trunc(record);
err = validator.check(record * multiplier, type);
return err;
}
(function() {
var sql;
mysql.createConnection({
host: mysqlHost,
user: mysqlUser,
password: mysqlPass
}).then(function(connection) {
sql = connection;
})
.then(function() {
return getDatabases(sql)
})
.then(function(dbs) {
dbs.forEach(function(db) {
if (ignoreDbs.indexOf(db) != -1) return;
getTables(sql, db)
.then(function(tables) {
tables.forEach(function(table) {
getTableDescription(sql, db, table)
.then(function(descrs) {
descrs.forEach(function(descr) {
let field = descr.field,
type = descr.type,
query = "select " + descr.field + " from " + db + "." + table + " ";
if (table != "nonce") query += "order by date desc limit 1000";
sql.query(query)
.then(function(rows) {
rows.forEach(function(row) {
let err = validateRecord(row[field], type);
if (err) {
console.log(err.message);
process.exit(exitStatus.nearOutOfRange);
}
});
});
});
});
});
});
});
});
/*
.then(function() {
//if (sql != null) sql.end();
//process.exit(exitStatus.ok);
});
*/
})();
I agree with Jaromanda in terms of using let in your for loops to block scope the values and avoid your usage of an immediately-invoked function, which, while totally fine in terms of functionality, is decidedly less readable.
In terms of best practices and avoiding anti-patterns, one of the most important things you can strive for in terms of writing 'good' code is building modularized, reusable blocks of code. As it stands, your code has 5 or 6 anonymous functions that exist nowhere but within your chain of promise callbacks. If you were to declare those as functions outside of that chain, not only does that improve the maintainability of your code (you can test each individual one), but, if their names are clearly indicative of their purposes, would make for a very readable promise chain.
(Updated based on User Question)
Rather than leaving inner functions...
function getTableDescription(sql, db, table) {
return sql.query("describe " + db + "." + table)
.then(function(rows) {
var descrs = [];
for (var r of rows) {
descrs.push({ 'field': r.Field, // eg: price
'type': r.Type}); // eg: decimal(10,2)
}
return descrs;
});
}
...you can easily strip that out so that your code is self-documenting:
function collectDescriptionsFromRows(rows) {
var descriptions = [];
for (var row of rows) {
descriptions.push({'field': row.Field, 'type': row.Type});
}
return descriptions;
}
function getTableDescription(sql, db, table) {
return sql.query("describe " + db + "." + table)
.then(collectDescriptionsFromRows);
}
Also, if you ever find yourself doing data collection from one array to another, it's extremely helpful to get used to using built-in higher order functions (map, filter, reduce). Instead of the collectDescriptionsFromRows I just listed, it could be simplified to:
function collectDescriptionsFromRows(rows) {
return rows.map(row => { 'field': row.Field, 'type': row.Type});
}
Much less verbose, much more readable. Your code and promise-chain will shrink and read more like a step-by-step list of instructions if you continue to extract those anonymous functions in the chain. Anywhere you see function(...there is more extracting to do! You can also do some damage (positively) by extracting all the data you need to begin with and use local logic to boil it down to what you need, rather than making several queries. Hope this helps.

Can't create Meteor.js helper based on parse.com query

My meteor app accesses Parse.com to pull in and display data.
I started out integrating the parse.com javascript query directly into the template's rendered function, which worked well.
Now, I want to use the Parse.com query in a helper to pass it over to a meteor {{#each}} loop which lives in my template.
Template.dashboard.helpers({
app: function () {
//init new array
var appsArr = [];
//Create a Parse Query for Post objects
var query = new Parse.Query("Apps");
query.descending("createdAt");
var appsObj = {};
query.find({
success: function(results) {
// Add the returned Parse.Object values to appsArr
for (var i = 0; i < results.length; i++) {
appsObj = {};
appsObj.obid = results[i].id;
appsObj.title = results[i].attributes.title;
appsObj.screenshot1 = results[i].attributes.screenshot1._url;
appsObj.appIcon = results[i].attributes.appIcon._url;
appsArr.push(appsObj);
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
return appsArr
}
});
Every time I try and return my array (appsArr) in the helper I get the error :
"Exception in template helper: undefined". I also can't get my parse objects to output in the console. Again, the same code works in the rendered function.
I am fairly new to Meteor.js and Blaze templates. please help me implement this parse query into the helper correctly so I can {{#each}} in the template.
{{#each app}}
<h3 class="app-title">{{title}}</h3>
{{/each}}
Thanks in advance!
Because the query.find function is asynchronous and non-blocking, you can't just assign variables in the callback and return them outside of the callback -- the callback hasn't run by the time you hit the return statement, so you're returning something that hasn't been defined.
An easy way around this will be to use a reactive variable (a variable whose assignment is watched); you can either use [ReactiveVar][1] or the built-in reactive [Session][2] variable. I typically use Session. A possible implementation would be something like this (apologies for not testing this out ahead of time):
Template.dashboard.onRendered({ // onRendered, calculate appVar
Session.set('appsVar', null); // reset appsVar immediately -- can also do this in onCreated / onDestroyed to clean up
//init new array
var appsArr = [];
//Create a Parse Query for Post objects
var query = new Parse.Query("Apps");
query.descending("createdAt");
var appsObj = {};
query.find({
success: function(results) {
// Add the returned Parse.Object values to appsArr
for (var i = 0; i < results.length; i++) {
appsObj = {};
appsObj.obid = results[i].id;
appsObj.title = results[i].attributes.title;
appsObj.screenshot1 = results[i].attributes.screenshot1._url;
appsObj.appIcon = results[i].attributes.appIcon._url;
appsArr.push(appsObj);
}
Session.set('appsVar', appsVar);
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
}
});
Template.dashboard.helpers({
app: function() { return Session.get('appsVar'); } // This will re-run when Session.appsVar is updated in the callback above.
});

Query with javascript in LightSwitch HTML client

LightSwitch tables in play:
ProductVariant
ProductVariantDetail (many to one ProductVariant)
Code:
myapp.AddEditProduct.Id_render = function (element, contentItem) {
var detailArray = new Array();
//contentItem.screen.getProductVariantDetails().then( function(result) {
// for (var i in result.data) {
// if (result.data[i].ProductVariant.Id == contentItem.value) {
// detailArray.push(result.data[i].InventoryDetail.Title);
// }
// }
// $("<div>" + detailArray.join(" / ") + "</div>").appendTo($(element));
//});
var filter = "ProductVariant eq " + contentItem.value;
myapp.activeDataWorkspace.ApplicationData.ProductVariantDetails.filter(filter).
execute().then(function (result) {
for (var i in result.results) {
detailArray.push(result.results[i].InventoryDetail.Title);
}
}).then(function() {
$("<div>" + detailArray.join(" / ") + "</div>").appendTo($(element));
});
};
The commented out code goes through EVERY ProductVariantDetail and checks if the corresponding ProductVariant matches the custom control. I discovered you can retrieve a query client-side (to save server bandwidth, see uncommented code) but my version never makes it inside filter(filter).execute. I tried "ProductVariant.Id eq" for the filter as well. Does anyone know what I might be doing wrong?
Try using this
var filter = "(ProductVariant.Id eq " + msls._toODataString(contentItem.value,":Int32")+")";

Categories

Resources