Binding an Array of Arrays with ChartJS

03 November 2016

When I write applications these days, they usually use structured data (compare yesterday's post). This happens because it is very natural - my data is mostly retrieved from some kind of data storage, and whether that's a relational database that runs a query, or a document-based non-relational system (that's politically correct for NoSql), there is usually some structure involved. However, I have seen questions about using less formally structured data, namely arrays and arrays of arrays. The one advantage of this data structure that I can come up with is that it is very compact - certainly not easier to read for a human, but potentially faster to transfer if the data volume is large. Interestingly, some of our - ahem - competitors seem to focus on such data structures to the extent of excluding almost everything else. If you have existing chart setups that use array data, this post might also be interesting to you.


As an example, here is an "array of arrays" data structure:

const data = [[114, 8], [84, 17], [54, 31], [24, 17], [7, 2]];


The problem you face when trying to bind this data to a ChartJS chart is that our binding system uses field names to determine which data elements are used for arguments and values (and potentially other series options). To bind the above data, you need to transform it into a shape that supplies field names. Fortunately this is not really hard to do, and I have written a flexible utility function for the purpose:

function transformArrayOfArrays(source, ...names) {
  const defaultName = i => "field" + i;
  return source.map(a => {
     return a.reduce((r, v, i) => {
       r[i < names.length ? names[i] : defaultName(i)] = v;
       return r;
     }, {});
  });
}

The function accepts a source array as input, as well as - optionally - a list of field names. In the absence of field names, or if the nested arrays contain more values than there are specified names, the "defaultName" function is used to generate field names "field1", "field2" and so on. The function now generates its result by iterating over the top-level array elements. For each such element, the "reduce" function is used to create a new object and add the values from the nested array to this object, using the supplied or generated field names.

Note: I had a question about the "new" ECMAScript 6 syntax I'm using in my sample code. I recommend you look into this if you write code in JavaScript! If you have trouble interpreting a piece of code I'm showing, I recommend checking out the CodePen samples I'm posting. If you follow the link to "edit on CodePen" and then click the "down-arrow" button in the upper right corner of the "JS (Babel)" panel, you have the option to see the compiled (or rather transpiled) JavaScript code. This can be very helpful when you get started with ES6.

Using my sample data, I can now call the function like this:

transformArrayOfArrays(data, "days", "percent")

This sample call would transform my data to this structure:

[{ days: 114, percent: 8 }, 
 { days: 84, percent: 17 },
 { days: 54, percent: 31 },
 { days: 24, percent: 17 }, 
 { days: 7, percent: 2 }]

Of course this data can now be bound to a chart easily, using the same field names I supplied to the transformation function call:

  $("#chart").dxChart({
    dataSource: transformArrayOfArrays(data, "days", "percent"),
    series: [{
      argumentField: "days",
      valueField: "percent",
      type: "spline"
    }],
    .....


I have created a sample that shows the use of the transformation helper in conjunction with a chart setup:


no comments
No Comments

Please login or register to post comments.