我正在使用 D3.js 实现 map 可视化。我有许多 x csv 文件(矩阵)。我想加载它们并对所有文件中的所有值求和。
我在名称数组上实现了 for 循环,并使用 d3.text 加载和解析数据,但由于异步行为,我找不到方法来执行此操作(我得到首先来自 console.log
的 undefined
,然后是 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);
...
...
我怎样才能实现这个目标?谢谢。
最佳答案
我建议您使用递归函数来完成此操作。
在下面的示例中,您可以像 d3.text()
一样使用 loadFilesAndCalculateSum()
,但它不是一个字符串,而是一个字符串数组,并且回调获取计算出的总和而不是文件的文本。
/**
* 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);
});
为了澄清这一点。您必须在回调函数内部对总和进行处理,我在其中添加了注释对总和进行处理
。该函数仍在异步执行。这意味着,您在 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
函数。
关于javascript - 使用 D3.js 加载和解析多个 csv 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30766696/