c3js: Uncaught TypeError: c.forEach is not a function - c3.js

I'm trying to create a simple line-graph of points using a timeseries axis, but the chart doesn't render and my browser console shows the error:
c3.min.js Uncaught TypeError: c.forEach is not a function
Here's my javascript:
var chart = c3.generate({
bindto: '#chart',
data: {
json: [
{date: "2016-01-01", pageviews: 1},
{date: "2016-01-02", pageviews: 2},
{date: "2016-01-03", pageviews: 3},
{date: "2016-01-04", pageviews: 4},
{date: "2016-01-05", pageviews: 5}],
keys: {
x: 'date',
value: 'pageviews'
}
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d'
}
}
}
});
And here's a jsfiddle which uses the un-minified sources for D3 and C3 and gives the following browser console error: Uncaught TypeError: targetKeys.forEach is not a function

The data.keys.value is expecting an array, so simply wrap 'pageviews' in square brackets like so:
var chart = c3.generate({
bindto: '#chart',
data: {
json: [
{date: "2016-01-01", pageviews: 1},
{date: "2016-01-02", pageviews: 2},
{date: "2016-01-03", pageviews: 3},
{date: "2016-01-04", pageviews: 4},
{date: "2016-01-05", pageviews: 5}],
keys: {
x: 'date',
value: ['pageviews'] // needs to be an array
}
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y-%m-%d'
}
}
}
});
Working jsfiddle for completeness

Related

How to configure linear labels in Time Cartesian Chartjs?

I need to show labels on the x-axis every 2 hours (0h, 2h, 4h...). What am I missing?
<script>
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
//labels: ['0h', '2h', '4h', '6h', '8h', '10h', '12h', '14h', '16h', '18h', '20h', '22h', '0h'],
datasets: [{
label: 'AAA1111',
//xAxisID: 'Hora',
//yAxisID: 'Velocidade',
data: [{
t: new Date("2015-3-15 12:30"),
y: 12
},
{
t: new Date("2015-3-15 14:40"),
y: 45
},
{
t: new Date("2015-3-15 17:50"),
y: 77
}
],
borderColor: 'rgba(255, 0, 0, 1)',
borderWidth: 4,
fill: false,
lineTension: 0,
lineJoint: "round",
spanGaps: true
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
distribution: 'linear',
time: {
unit: 'hour',
//stepSize: 24??
},
ticks: {
source: 'data'
}
}]
}
}
});
</script>
The chart plots Time x Velocity.
Two small changes will achieve the desired result:
set stepSize: 2 to 'show labels on X axis every 2 hours (0h, 2h, 4h...)'
remove ticks: { source: 'data' } as that:
generates ticks from data (including labels from data {t|x|y} objects)"
Here's a working example based on the posted code:
var ctx = document.getElementById('myChart').getContext('2d');
var myChart = new Chart(ctx, {
type: 'line',
data: {
//labels: ['0h', '2h', '4h', '6h', '8h', '10h', '12h', '14h', '16h', '18h', '20h', '22h', '0h'],
datasets: [{
label: 'AAA1111',
//xAxisID: 'Hora',
//yAxisID: 'Velocidade',
data: [{
t: new Date("2015-3-15 12:30"),
y: 12
},
{
t: new Date("2015-3-15 14:40"),
y: 45
},
{
t: new Date("2015-3-15 17:50"),
y: 77
}
],
borderColor: 'rgba(255, 0, 0, 1)',
borderWidth: 4,
fill: false,
lineTension: 0,
lineJoint: "round",
spanGaps: true
}]
},
options: {
scales: {
xAxes: [{
type: 'time',
distribution: 'linear',
time: {
unit: 'hour',
stepSize: 2
}
}]
}
}
});
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.9.3/dist/Chart.bundle.min.js"></script>
<canvas id="myChart"></canvas>

Multiple layers with dc.js series charts

I'm struggling to get a dc.js plot to work. The data object contains 2 value fields, and I need to designate val to scatterPlot and smooth to lineChart. Example below and jsfiddle here.
<div id="series-chart"></div>
<style>div{ display: inline; }</style>
<script>
var datum = [
{date: "2018-01-01T00:00:00Z", val: 0, smooth: 3, type: "set1"},
{date: "2018-01-02T00:00:00Z", val: 7, smooth: 4, type: "set1"},
{date: "2018-01-03T00:00:00Z", val: 1, smooth: 6, type: "set1"},
{date: "2018-01-04T00:00:00Z", val: 4, smooth: 8, type: "set1"},
{date: "2018-01-05T00:00:00Z", val: 8, smooth: 11, type: "set1"},
{date: "2018-01-06T00:00:00Z", val: 15, smooth: 14, type: "set1"},
{date: "2018-01-07T00:00:00Z", val: 12, smooth: 17, type: "set1"},
{date: "2018-01-08T00:00:00Z", val: 20, smooth: 20, type: "set1"},
{date: "2018-01-01T00:00:00Z", val: 15, smooth: 12, type: "set2"},
{date: "2018-01-02T00:00:00Z", val: 10, smooth: 11, type: "set2"},
{date: "2018-01-03T00:00:00Z", val: 14, smooth: 9, type: "set2"},
{date: "2018-01-04T00:00:00Z", val: 7, smooth: 7, type: "set2"},
{date: "2018-01-05T00:00:00Z", val: 12, smooth: 5, type: "set2"},
{date: "2018-01-06T00:00:00Z", val: 8, smooth: 4, type: "set2"},
{date: "2018-01-07T00:00:00Z", val: 8, smooth: 3, type: "set2"},
{date: "2018-01-08T00:00:00Z", val: 5, smooth: 2, type: "set2"}
];
var data = crossfilter(datum);
var typeSeriesDimension = data.dimension(function(d){ return [d.type, new Date(d.date)]; });
var totalGroupRaw = typeSeriesDimension.group().reduceSum(function(d){ return d.val; });
var totalGroupSmooth = typeSeriesDimension.group().reduceSum(function(d){ return d.smooth; });
var composite = dc.compositeChart("#series-chart");
composite
.width(400)
.height(400)
.x(d3.time.scale().domain([new Date("2018-01-01T00:00:00Z"), new Date("2018-01-08T00:00:00Z")]))
.dimension(typeSeriesDimension)
.compose([
dc.lineChart(composite)
.dimension(typeSeriesDimension)
.group(totalGroupSmooth)
// .seriesAccessor(function(d) {return d.key[0];})
.keyAccessor(function(d) {return d.key[1];})
.valueAccessor(function(d) {return d.value;}) ,
dc.scatterPlot(composite)
.dimension(typeSeriesDimension)
.group(totalGroupRaw)
// .seriesAccessor(function(d) {return d.key[0];})
.keyAccessor(function(d) {return d.key[1];})
.valueAccessor(function(d) {return d.value;})
])
.legend(dc.legend().x(100).y(10).itemHeight(20).gap(5));
dc.renderAll();
</script>
The categorical colour mapping doesn't work without seriesAccessor, and uncommented these lines cause script to fail with:
Uncaught TypeError: dc.lineChart(...).dimension(...).group(...).seriesAccessor is not a function
Also the lines are for some reason I can't establish plotting in the wrong order.
All round fail. Any assistance much appreciated!
That dimension makes sense for the scatter plot, but it doesn't make sense for the line chart.
A line chart expects to have simple scalar keys like dates. But the scatter plot expects to have 2D keys like [type, Date].
Luckily the composite chart doesn't really mind if its child charts use different dimensions.
So define a new time dimension and use that for the line chart's group:
var timeSeriesDimension = data.dimension(function(d) { return new Date(d.date);})
var totalGroupSmooth = timeSeriesDimension.group().reduceSum(function(d){ return d.smooth; });
And drop your key and value accessors from the line chart, because the default behavior is fine now:
//.keyAccessor(function(d) {return d.key[1];})
//.valueAccessor(function(d) {return d.value;}) ,
As for seriesAccessor, that's only used with dc.seriesChart, so I'm afraid it won't help you here. It's probably possible to combine a scatter plot with a series chart, because the series chart is just a specialization of the composite chart, but I haven't tried that - and I'd rather leave that for another question if you want to try that out.

Generate an array of charts

I want to generate an array of charts and insert them into the DOM.
(I want to use these charts as templates and insert them at the right time)
like that:
Var array = [];
array[0].kendoChart ({// .........});
array[1].kendoChart ({// .........});
array.forEach (function (el) {
$('body').append(el);
})
Tell me how to be?
I would keep an array of the data used to generate the charts, and then generate them at the time of insert.
In HTML provide a container for the charts to be inserted (could be body as in your example):
<div id="chartsContainer" ></div>
Then in script have an array of the data needed to generate the multiple charts, e.g. :
var charts = [
{
chartid: "id1", title: "Chart 1",
data: [{ x: "item1", y: 2}, { x: "item2", y: 4},{ x: "item3", y: 1}]
},
{
chartid: "id2", title: "Chart 2",
data: [{ x: "item1", y: 8}, { x: "item2", y: 3},{ x: "item3", y: 7},{ x: "item4", y: 2}]
},
{
chartid: "id3", title: "Chart 3",
data: [{ x: "item1", y: 1}, { x: "item2", y: 2},{ x: "item3", y: 4} ]
},
];
Finally loop through the array, insert DIVs in the container and create a chart in each DIV:
$("#chartsContainer").empty();
var arrayLength = charts.length;
for (var i = 0; i < arrayLength; i++) {
var div = $('<div id="' + charts[i].chartid + '"></div>');
$("#chartsContainer").append(div);
div.kendoChart({
theme: "Flat",
chartArea: { height: 200 },
dataSource: {data: charts[i].data },
title: { text: charts[i].title},
seriesDefaults: {type: "column"},
series: [{field: "y" }],
categoryAxis: {
field: "x",
majorGridLines: {visible: false },
}
});
}
Working DEMO

pie chart from json using c3 js

The code is taken as an example..
I need to generate a pie chart having 4 divisions (site1,site2...)each division correspond to its respective upload value.
In the above code i am not able to achieve this(I have specified value:['upload'])...
what is the exact value i have to specify?
Thanks..
chart = c3.generate({
data: {
json: [
{name: 'www.site1.com', upload: 200},
{name: 'www.site2.com', upload: 100},
{name: 'www.site3.com', upload: 300},
{name: 'www.site4.com', upload: 400},
],
keys: {
// x: 'name', // it's possible to specify 'x' when category axis
value: ['upload'],
},
type:'pie'
},
axis: {
x: {
// type: 'category'
}
}
});
The pie chart maps each property to a pie sector. You could reformat your JSON like
var jsonData = [
{name: 'www.site1.com', upload: 200},
{name: 'www.site2.com', upload: 100},
{name: 'www.site3.com', upload: 300},
{name: 'www.site4.com', upload: 400}
]
var data = {};
var sites = [];
jsonData.forEach(function(e) {
sites.push(e.name);
data[e.name] = e.upload;
})
chart = c3.generate({
data: {
json: [ data ],
keys: {
value: sites,
},
type:'pie'
},
});
Working fiddle - http://jsfiddle.net/2nf9a7x4/

Working With Filters in Crossfilter

I have just started working with crossfilter and d3.js ... I'm trying some snippets given in the API reference... I Have the following data
var payments = crossfilter([
{date: "2011-11-14T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:20:19Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:28:54Z", quantity: 1, total: 300, tip: 200, type: "visa"},
{date: "2011-11-14T16:30:43Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:48:46Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:53:41Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:54:06Z", quantity: 1, total: 100, tip: 0, type: "cash"},
{date: "2011-11-14T16:58:03Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:07:21Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:22:59Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:25:45Z", quantity: 2, total: 200, tip: 0, type: "cash"},
{date: "2011-11-14T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "visa"}
]);
I can create dimension via type as
var paymentsByTotal = payments.dimension(function(d) { return d.type; });
My question is that how can i filter an array of strings. I tried:
paymentsByTotal.filterRange(["cash","visa"]);
But I didn't get the expected result!
Any suggestions?
With the sourcecode in the master branch of Crossfilter.js, there is no provision for union of filters, you have to get the code from Jason Davies' union branch.
Then, you should be able to do paymentsByTotal.filter("cash","visa"); and get the desired output.
Appears that a filterFunction(function) has been added since the previous response, so that you can now do this with:
paymentsByTotal.filterFunction(function(d) { return d === "visa" || d === "cash" });
If you have an array of values, and you don't want to specify the explicit logic (i.e. d === "visa" ) for each item, then you can extend sai's filterFunction solution to check if the value is included in your array. Doing it this way just makes things easier if your array of items to filter is large or likely to change.
var items = ['Visa', 'Cash'];
paymentsByTotal.filterFunction(function(d) { return items.indexOf(d) > -1;});

Resources