8

I have a CSV file that I would like to use as source data for a jQuery flot graph.

Should I:

  1. Find a jQuery plugin that will load the CSV file directly?
  2. Convert the CSV file into JSON and use that instead?
  3. Do something completely different?

I'm not having much luck finding a jQuery plugin that can cope with an external CSV file, but maybe I'm missing something.

Richard
  • 31,629
  • 29
  • 108
  • 145
  • It looks like converting the CSV to JSON is your best bet here. You could write a server-side parser, if there isn't one already (unlikely). – Bojangles Oct 04 '11 at 21:25
  • @JamWaffles JSON conversion and server-side code are completely unnecessary here. See my answer for the details. – Evan Plaice Sep 05 '12 at 19:29

2 Answers2

11

No server required...

This can be accomplished without the server if you employ a little ingenuity.

You'll need 2 things:

The jquery-csv parser library specializes in parsing RFC 4180 compliant CSV, with many capabilities above and beyond just transforming CSV data into JavaScript data. For the purpose of this demonstration we're going to use two features:

The first is the toArrays() method. It takes a multi-line CSV string and transforms it into a 2D array.

The second is a user-defined parser hook that automatically transforms the output (which is all strings) into scalar (ie integer/float) data. Basically, through the use of a function callback, it's possible to inline additional processing into the parser.

Once you have the CSV data assigned to a string variable the transform to JavaScript data is very simple.

var csv = "" // this is the string containing the CSV data
var data = $.csv.toArray(csv, {
  onParseValue: $.csv.hooks.castToScalar
});

That's all for the CSV parsing step.


Now, what Flot expects is an array of 2D arrays where each 2D array contains an independent dataset.

To build your data create an empty array first:

var flotData = [];

Then for each data set you generate using CSV you simply add the dataset to the collection.

var flotData.push(data);

File handling via HTML5 is a tricky topic because it uses asynchronous callbacks to load files; that means no return statements. To keep things simple, I'll just make the flot plot a global object.

First initialize the plot during the $(document).ready():

var data = [];
flot = $.flot($('#plotdiv'), data, options);

Note: A dummy data object is added so the graph can be initialized.

Then, add a file handler:

// handles csv files
function handleFileSelect(evt) {
  var files = evt.target.files; // FileList object

  // reset the flot dataset
  flot.setData([]);
  for(var i=0, len=files.length; i<len; i++) {
    flotFileData(files[i], i);
  }
}

Assume that this can load one-or-more CSV data files. This gets called after you choose your files from the file dialog. The data gets reset to empty (ie []) because we want to start fresh after every time the file dialog gets executed; otherwise you'll be appending to a previous dataset.

This will cycle through the files and call flotFileData() for each file that was selected in the file dialog..

Here's the code for handling the file open callback:

function flotFileData(file, i) {
  var reader = new FileReader();
  reader.readAsText(file);
  reader.onload = function(event){
    var csv = event.target.result;
    var newData = $.csv.toArrays(csv, {
      onParseValue:$.csv.hooks.castToScalar
    });
    var data = flot.getData();
    data[i] = newData;
    flot.setData(data);
    flot.setupGrid();
    flot.draw();
  };
  reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}

This reads the file as plain-text and makes the contents available via event.target.result. The CSV parser transforms the CSV to scalar data in 2-dimensional array form. To stack multiple datasets we need to append to the data that's already in the plot, so we need to store the existing data first via flot.getData(). Append the new data, set it via flot.setData() and finally re-draw the plot.

Note: If the ranges of the graph don't need to be re-calculated you can skip the flot.setupGrid() call.

Ideally, the redraw should only happen once per file loading phase but this demo hasn't been optimized yet. That means, the graph will be re-drawn for every file that's read (definitely not ideal).

If you want to see it in action, take a look at the 'Flot Demonstration' provided by the jquery-csv project. I suggest you try loading both the analytics1.csv and analytics2.csv datasets so you can see how both are overlaid on the graph.

If you decide to go the server-side route to load the CSV files, there's also a more basic example that demonstrates loading the CSV from a textarea.

Disclaimer: I am the author of jquery-csv.

Update:

Due to the shuttering of Google Code, jquery-csv has been moved to GitHub

Community
  • 1
  • 1
Evan Plaice
  • 13,944
  • 6
  • 76
  • 94
5

Use the jQuery CSV plugin to get an array. Build / sort the array however you need for the chart.

jQuery CSV Plugin

It just occured to me you may be thinking of reading a flat CSV file with jQuery. That's not possible. Giving javascript access to the filesystem sounds like a terrible idea. You can always use $.get() to load a file on a server, though.

Chris G.
  • 3,963
  • 2
  • 21
  • 40
  • OK: so use `$.get()` to get the data, and then convert it into an array using the CSV plugin? – Richard Oct 04 '11 at 21:37
  • $.get() will get you a string containing the CSV data. Passing that to the CSV plugin will give you an array. You can then modify that array however you need so it is in a format your chart script will accept. Most charting scripts will accept data in array format so that part should not be difficult. – Chris G. Oct 04 '11 at 21:38
  • Hm, actually if I try that I get a `text.split is not a function` javascript error - looks like `$.csv` expects a string, but the data returned from `$.get()` is an object. I guess I could try this: http://stackoverflow.com/questions/5612787/converting-javascript-object-to-string but it feels like it's getting messy... – Richard Oct 04 '11 at 21:54
  • http://api.jquery.com/jQuery.get/#jqxhr-object . I wouldn't call it messy, that's how this stuff is done with Javascript. – Chris G. Oct 04 '11 at 21:59
  • sure... No, I meant the converting-object-to-string-but-only-supported-in-certain-browsers part of it. – Richard Oct 04 '11 at 22:12
  • Can you post your code? I'm not sure that you're using $.get right. – Chris G. Oct 05 '11 at 12:03
  • I ended up using Google Fusion Tables, but thanks anyway for the help :) – Richard Oct 11 '11 at 15:43
  • There's no need to make unnecessary road-trips to the server-side. This can be handled in the client alone. – Evan Plaice Sep 05 '12 at 19:25