我正在使用D3.js实现地图可视化。我有许多x csv文件(矩阵)。我想加载它们并汇总所有文件中的所有值。
我在名称数组上实现了for循环,并使用d3.text
加载和解析数据,但是由于异步行为,我找不到这样做的方法(我得到undefined
首先从console.log
开始,然后是for循环的结果)。
我试过使用async.js,但我无法弄清楚如何允许for循环的流程。这是我的代码:
var total=[];
var matrix=[];
for(var i=0; i<filenames.length; i++){
d3.text(filenames[i], function(text){
matrix = d3.csv.parseRows(text).map(function (row){
return row.map(function(value){
return +value;
});
});
});
//sum the matrix pseudocode
for(...){
total = total + matrix;
}
}
//here I need the sum of all files and then do other stuffs with the total:
console.log(total);
...
...
我怎样才能达到这个目的?感谢。
答案 0 :(得分:1)
我建议你用递归函数来做。
在下面的示例中,您可以像loadFilesAndCalculateSum()
一样使用d3.text()
,但它不是一个字符串,而是一个字符串数组,并且回调获取计算的总和而不是文件的文本。
/**
* load all files and calculate the sum of the values
* @param {Array} filenames Array with filenames as string
* @param {Function} cb Callback function, gets the sum as param
* @param {number} sum The initial sum
* @param {number} i Starting index for filenames
* @return {void}
*/
function loadFilesAndCalculateSum(filenames, cb, sum, i) {
sum = sum || 0;
i = i || 0;
d3.text(filenames[i], function(error, text) {
//parse the rows and reduce them
sum += d3.csv.parseRows(text).reduce(function(prev, curr) {
//return previous sum + this rows sum
return prev + d3.sum(curr, function(d){return +d;})
},0);
if(i < filenames.length - 1){
//load next file
loadFilesAndCalculateSum(filenames, cb, sum, i+1);
} else {
//call the callback with the final sum
cb(sum);
}
});
}
var filenames = ["file1.txt", "file2.txt", "file3.txt"];
loadFilesAndCalculateSum(filenames, function(sum){
//do something with the total sum
console.log(sum);
});
澄清这一点。你必须在回调函数里面对sum进行处理,我在其中放置了注释do something with the total sum
。此函数仍在执行异步。这意味着,您在loadFilesAndCalculateSum()
函数之后编写的所有内容都可能在回调内的代码之前执行。您可以找到 async javascript here
//this is executed first
//....
loadFilesAndCalculateSum(filenames, function(sum){
//do something with the total sum
//this is executed third, when all files are loaded and the sum is calculated
console.log(sum);
});
//code after this point is executed second, while the files are being loaded.
如果您已经有一个函数可以对总和执行某些操作,则可以将此函数作为第二个参数传递给loadFilesAndCalculateSum
。这是可能的,因为函数被称为 first class citizens :
var addthis = 5;
function doSomethingWithTheSum(sum) {
//everything you want to do with the sum goes inside this function.
//from here you could call other functions and pass the sum.
soSomethingDifferentWithTheSum(sum);
//or you use the sum inside this function
console.log(sum);
var newsum = sum + addthis;
console.log(sum);
d3.select("whatever")
.data([sum])
.enter()
.append("text")
.text(function(d){ return d;});
}
loadFilesAndCalculateSum(filenames, doSomethingWithTheSum);
您可以像传递任何其他变量一样传递函数。在第一个例子中,我调用了loadFiles...
函数cb
的第二个参数,它是callback
的通常缩写。如文档注释中所述,此参数应为Function
类型。
在loadFiles...
函数的末尾,回调函数由
....
//call the callback with the final sum
cb(sum);
....
这里总和作为第一个参数给予回调函数。因此,如果您传递一个函数,它应该采用单个参数,如第一个示例中的匿名函数或上例中的doSomethingWithTheSum
函数。