使用D3.js

时间:2015-06-10 20:29:07

标签: javascript d3.js

我正在使用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);
...
...

我怎样才能达到这个目的?感谢。

1 个答案:

答案 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函数。