add value (nunber) per same year from an array of objects - filter

const db1 = [ { year: "2000", state: "Rio", month: "November", number: 18 }, { year: "2002", state: "Perambuco", month: "February", number: 64 }, { year: "2001", state: "Mato Grasso", month: "March", number: 112 }, { year: "2003", state: "Roraima", month: "January", number: 547 }, { year: "2002", state: "Maranhoo", month: "July", number: 4 }, { year: "2003", state: "Rio", month: "March", number: 9 }, { year: "2000", state: "Roraima", month: "October", number: 25 }, { year: "2001", state: "Paraiba", month: "January", number: 11 }, ];
I have the following array of objects. from db1 I want to filter out only the keys (year, number) and add the number (values) per same respective year ex:
{ year: "2002", number: 68 }, { year: "2000", number: 43 }, { year: "2003", number: 556 }, { year: "2001", number: 123 },
any feedback is appreciated.
const sumArrayVals = (db1) => { return db1.reduce((a, b) => { Object.keys(b).map((c) => (a[c] = (a[c] || 0) + b[c])); return a; }); };)

took a bit but got it.
const sumPerYear = db1.reduce((acc, cur) => {
// increment or initialize to cur.number
acc[cur.year] = acc[cur.year] + cur.number || cur.number;
return acc;
}, {});

Related

Beeswarm plot with force - add links to nodes

I've created a Beeswarm plot with d3v4 and d3.forceSimulation, and the points are where I want them to be:
var data = [
{ country: "Algeria", amount: 22, year: 2000 },
{ country: "Argentina", amount: 49, year: 1990 },
{ country: "Armenia", amount: 3, year: 1990 },
{ country: "Australia", amount: 9, year: 2010 },
{ country: "Austria", amount: 1, year: 2010 },
{ country: "Bahamas", amount: 5, year: 2018 },
{ country: "Bahrain", amount: 22, year: 2018 },
{ country: "Belarus", amount: 9, year: 2010 },
{ country: "Belgium", amount: 46, year: 2018 },
{ country: "Brazil", amount: 79, year: 1990 },
{ country: "Canada", amount: 12, year: 2000 },
{ country: "China", amount: 26, year: 2018 },
{ country: "Colombia", amount: 9, year: 2010 },
{ country: "Croatia", amount: 8, year: 2000 },
{ country: "Cuba", amount: 14, year: 1990 },
{ country: "Czech Republic", amount: 11, year: 2018 },
{ country: "Denmark", amount: 125, year: 2010 },
{ country: "Canada", amount: 124, year: 2018 },
{ country: "Bahrain", amount: 39, year: 2010 },
{ country: "Estonia", amount: 141, year: 2018 },
{ country: "Ethiopia", amount: 38, year: 1990 },
{ country: "France", amount: 4, year: 2018 },
{ country: "Germany", amount: 15, year: 2000 },
{ country: "Greece", amount: 16, year: 2010 },
{ country: "Grenada", amount: 241, year: 2010 },
{ country: "Hungary", amount: 135, year: 1990 },
{ country: "India", amount: 22, year: 1990 },
{ country: "Indonesia", amount: 31, year: 1990 },
{ country: "Iran", amount: 88, year: 2010 },
{ country: "Ireland", amount: 12, year: 2018 },
{ country: "Italy", amount: 128, year: 2000 },
{ country: "Jamaica", amount: 1, year: 2018 },
{ country: "Japan", amount: 41, year: 1990 },
{ country: "Jordan", amount: 137, year: 2010 },
{ country: "Iran", amount: 13, year: 1990 },
{ country: "Malaysia", amount: 25, year: 2018 },
{ country: "Mexico", amount: 59, year: 2010 },
{ country: "Moldova", amount: 71, year: 2000 },
{ country: "Mongolia", amount: 22, year: 2018 },
{ country: "Morocco", amount: 131, year: 1990 },
{ country: "Netherlands", amount: 129, year: 2018 },
{ country: "New Zealand", amount: 148, year: 2018 },
{ country: "Niger", amount: 1, year: 2010 },
{ country: "Nigeria", amount: 41, year: 1990 },
{ country: "Norway", amount: 14, year: 2010 },
{ country: "Philippines", amount: 15, year: 2018 },
{ country: "Poland", amount: 12, year: 2010 },
{ country: "Portugal", amount: 31, year: 2000 },
{ country: "Puerto Rico", amount: 51, year: 2000 },
{ country: "Romania", amount: 15, year: 2000 },
{ country: "Serbia", amount: 18, year: 2000 },
{ country: "South Africa", amount: 14, year: 2010 },
{ country: "Sweden", amount: 11, year: 2018 },
{ country: "Switzerland", amount: 7, year: 2010 },
{ country: "Thailand", amount: 61, year: 2018 },
{ country: "Trinidad and Tobago", amount: 12, year: 2018 },
{ country: "Tunisia", amount: 34, year: 2010 },
{ country: "Turkey", amount: 28, year: 2010 },
{ country: "Ukraine", amount: 11, year: 2010 },
{ country: "Uzbekistan", amount: 123, year: 2018 },
{ country: "Venezuela", amount: 23, year: 2018 },
{ country: "Iran", amount: 13, year: 2018 }
];
var width = 1000,
height = 500;
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height);
var x = d3.scaleLinear()
.range([95, 650]);
var y = d3.scaleLinear()
.range([100, 450]);
data.forEach(d => {
d.amount = +d.amount;
});
var sort = data.sort((a, b) => d3.descending(a, b));
y.domain(d3.extent(data, function(d) {
return d.amount;
}));
x.domain(d3.extent(data, function(d) {
return d.year;
}));
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return x(d.year);
}).strength(3))
.force("y", d3.forceY(function(d) {
return y(d.amount)
}).strength(2))
.force("collide", d3.forceCollide(7).strength(7))
.stop();
for (var i = 0; i < data.length * 2; ++i) simulation.tick();
var circles = svg.selectAll(".circles")
.data(data);
var circlesEnter = circles.enter()
.append("circle");
circlesEnter.attr("r", 4)
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.attr("fill", function(d) {
if (d.country == "Iran") {
return "#FF0044"
} else if (d.country == "Canada") {
return "#00A9E9"
} else if (d.country == "Bahrain") {
return "#6BF4C6"
} else {
return '#333'
}
})
.attr('class', function(d) {
return d.amount + ' ' + d.year + ' ' + d.country
})
// connector lines
var byCountry = d3.nest()
.key(function(d) {
return d.country;
})
.entries(data);
var countryNames = d3.values(byCountry).map(function(d) {
return d.values.map(function(v) {
return v.country;
}).join(', ');
});
for (i = 0; i < countryNames.length; i++) {
eaco = countryNames[i].split(',')[0]
const filterByCountry = (country, data) => item => item.country === country
connectData = data.filter(filterByCountry(eaco))
var linesGroup = svg.append("g")
.attr("class", "connectors");
var linec = d3.line()
.x(function(d) {
return x(d.year)
})
.y(function(d) {
return y(d.amount)
})
// using below as the points does not work
// .x(function(d) { return x(d.x)})
// .y(function(d) { return y(d.y)})
var lineGraph = linesGroup.selectAll('.connect')
.data(connectData)
.enter()
.append("path")
.attr('class', function(d) {
return d.amount + ' ' + d.year + ' ' + d.country
})
.attr("d", linec(connectData))
.attr("stroke", function(d) {
if (d.country == "Iran") {
return "#FF0044"
} else if (d.country == "Canada") {
return "#00A9E9"
} else if (d.country == "Bahrain") {
return "#6BF4C6"
} else {
return '#333'
}
})
.attr("stroke-width", 1)
.attr("fill", "none")
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<body><div id="chart"></div></body>
The x axis shows years.
The y axis shows amount.
Each point is a country.
I'd like to add connecting lines between points that are the same country over different years. I've color-coded these to clarify.
The problem is, I can't get the x/y to match up with the points based on the force. I commented out what I thought would work. Any ideas?
Once circles are created, it is possible to retrieve their exact coordinates. With these circle coordinates, it becomes very easy to set the extremities of the lines:
var data = [
{ country: "Algeria", amount: 22, year: 2000 },
{ country: "Argentina", amount: 49, year: 1990 },
{ country: "Armenia", amount: 3, year: 1990 },
{ country: "Australia", amount: 9, year: 2010 },
{ country: "Austria", amount: 1, year: 2010 },
{ country: "Bahamas", amount: 5, year: 2018 },
{ country: "Bahrain", amount: 22, year: 2018 },
{ country: "Belarus", amount: 9, year: 2010 },
{ country: "Belgium", amount: 46, year: 2018 },
{ country: "Brazil", amount: 79, year: 1990 },
{ country: "Canada", amount: 12, year: 2000 },
{ country: "China", amount: 26, year: 2018 },
{ country: "Colombia", amount: 9, year: 2010 },
{ country: "Croatia", amount: 8, year: 2000 },
{ country: "Cuba", amount: 14, year: 1990 },
{ country: "Czech Republic", amount: 11, year: 2018 },
{ country: "Denmark", amount: 125, year: 2010 },
{ country: "Canada", amount: 124, year: 2018 },
{ country: "Bahrain", amount: 39, year: 2010 },
{ country: "Estonia", amount: 141, year: 2018 },
{ country: "Ethiopia", amount: 38, year: 1990 },
{ country: "France", amount: 4, year: 2018 },
{ country: "Germany", amount: 15, year: 2000 },
{ country: "Greece", amount: 16, year: 2010 },
{ country: "Grenada", amount: 241, year: 2010 },
{ country: "Hungary", amount: 135, year: 1990 },
{ country: "India", amount: 22, year: 1990 },
{ country: "Indonesia", amount: 31, year: 1990 },
{ country: "Iran", amount: 88, year: 2010 },
{ country: "Ireland", amount: 12, year: 2018 },
{ country: "Italy", amount: 128, year: 2000 },
{ country: "Jamaica", amount: 1, year: 2018 },
{ country: "Japan", amount: 41, year: 1990 },
{ country: "Jordan", amount: 137, year: 2010 },
{ country: "Iran", amount: 13, year: 1990 },
{ country: "Malaysia", amount: 25, year: 2018 },
{ country: "Mexico", amount: 59, year: 2010 },
{ country: "Moldova", amount: 71, year: 2000 },
{ country: "Mongolia", amount: 22, year: 2018 },
{ country: "Morocco", amount: 131, year: 1990 },
{ country: "Netherlands", amount: 129, year: 2018 },
{ country: "New Zealand", amount: 148, year: 2018 },
{ country: "Niger", amount: 1, year: 2010 },
{ country: "Nigeria", amount: 41, year: 1990 },
{ country: "Norway", amount: 14, year: 2010 },
{ country: "Philippines", amount: 15, year: 2018 },
{ country: "Poland", amount: 12, year: 2010 },
{ country: "Portugal", amount: 31, year: 2000 },
{ country: "Puerto Rico", amount: 51, year: 2000 },
{ country: "Romania", amount: 15, year: 2000 },
{ country: "Serbia", amount: 18, year: 2000 },
{ country: "South Africa", amount: 14, year: 2010 },
{ country: "Sweden", amount: 11, year: 2018 },
{ country: "Switzerland", amount: 7, year: 2010 },
{ country: "Thailand", amount: 61, year: 2018 },
{ country: "Trinidad and Tobago", amount: 12, year: 2018 },
{ country: "Tunisia", amount: 34, year: 2010 },
{ country: "Turkey", amount: 28, year: 2010 },
{ country: "Ukraine", amount: 11, year: 2010 },
{ country: "Uzbekistan", amount: 123, year: 2018 },
{ country: "Venezuela", amount: 23, year: 2018 },
{ country: "Iran", amount: 13, year: 2018 }
];
var width = 1000,
height = 500;
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height);
var x = d3.scaleLinear()
.range([95, 650]);
var y = d3.scaleLinear()
.range([100, 450]);
data.forEach(d => {
d.amount = +d.amount;
});
var sort = data.sort((a, b) => d3.descending(a, b));
y.domain(d3.extent(data, function(d) {
return d.amount;
}));
x.domain(d3.extent(data, function(d) {
return d.year;
}));
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return x(d.year);
}).strength(3))
.force("y", d3.forceY(function(d) {
return y(d.amount)
}).strength(2))
.force("collide", d3.forceCollide(7).strength(7))
.stop();
for (var i = 0; i < data.length * 2; ++i) simulation.tick();
var circles = svg.selectAll(".circles").data(data);
var circlesEnter = circles.enter().append("circle");
circlesEnter.attr("r", 4)
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("fill", function(d) {
if (d.country == "Iran") {
return "#FF0044"
} else if (d.country == "Canada") {
return "#00A9E9"
} else if (d.country == "Bahrain") {
return "#6BF4C6"
} else {
return '#333'
}
})
.attr('class', function(d) {
return d.country + '-' + d.year + '-' + d.amount
});
// connector lines
var byCountry = d3.nest().key(function(d) { return d.country; }).entries(data);
var countryNames = d3.values(byCountry).map(function(d) {
return d.values.map(function(v) {
return v.country;
}).join(', ');
});
for (i = 0; i < countryNames.length; i++) {
eaco = countryNames[i].split(',')[0]
const filterByCountry = (country, data) => item => item.country === country
connectData = data.filter(filterByCountry(eaco))
if (connectData.length >= 2) {
var linesGroup = svg.append("g")
.attr("class", "connectors");
var linec = d3.line()
.x(d => d3.select("circle." + d.country + "-" + d.year + "-" + d.amount).attr("cx"))
.y(d => d3.select("circle." + d.country + "-" + d.year + "-" + d.amount).attr("cy"));
linesGroup
.datum(connectData)
.append("path")
.attr('class', d => d[0].amount + '-' + d[0].year + '-' + d[0].country)
.attr("d", linec)
.attr("stroke", function(d) {
if (d[0].country == "Iran") return "#FF0044";
else if (d[0].country == "Canada") return "#00A9E9";
else if (d[0].country == "Bahrain") return "#6BF4C6";
else return '#333';
})
.attr("stroke-width", 1)
.attr("fill", "none");
}
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<body><div id="chart"></div></body>
This way, lines extremities exactly match the coordinates of the circles.
To do so, we can select the circles corresponding to the extremities of the line in question using:
d3.select("circle." + d.country + "-" + d.year + "-" + d.amount)
on which we can retrieve the cx and cy attributes (x and y position of the circle):
d3.select("circle." + d.country + "-" + d.year + "-" + d.amount).attr("cx")
to finally create the line with these coordinates:
var linec = d3.line()
.x(d => d3.select("circle." + d.country + "-" + d.year + "-" + d.amount).attr("cx"))
.y(d => d3.select("circle." + d.country + "-" + d.year + "-" + d.amount).attr("cy"));
Side notes:
Notice how I modified the classes you used to name circles. As the class names contained spaces, we couldn't select them; I used - instead. In addition we apparently can't select a class which starts with a number, so I changed the class names to start with the country instead of the amount.
Since, here, classes are used to uniquely define circles and lines, it would probably make more sense to set ids rather than classes.
As noticed by #Gerardo, each line is actually created several times (once per circle; such that a line joining 3 circles would be created 3 times). Here is one possible way you can fix that:
var linec = d3.line()
.x(d => d3.select("circle." + d.country + "-" + d.year + "-" + d.amount).attr("cx"))
.y(d => d3.select("circle." + d.country + "-" + d.year + "-" + d.amount).attr("cy"));
linesGroup.datum(connectData).append("path").attr("d", linec)...

grid populated from a dataSource using JSON and a database. How to make parse: work and create a new field?

I'm using javascript and kendo ui.
I have a grid and populate it with
a local datasource
ie:
var dsDataSource = new kendo.data.DataSource({
data:
[
{ // 1
title: "Star Wars: A New Hope",
date5: new Date(2001,8,8,21,46,0,0), // 9/8/2001 09:46:00.000 PM
},
{ // 2
title: "Star Wars: The Empire Strikes Back",
date5: new Date(2004,10,9,6,33,0,0), // 11/9/2004 06:33:00.000 AM
},
{ // 3
title: "Star Wars: Return of the Jedi",
date5: new Date("2004/11/09 12:00 AM"), // 11/9/2004 12:00:00.000 AM
},
{ // 4
title: "Star Wars: The Phantom Menace",
date5: new Date(2008,0,10,4,20,0,0), // 1/10/2008 04:20:00.000 AM
},
{ // 5
title: "Star Wars: Revenge of the Sith",
date5: new Date("2004/11/09 06:30 AM"), // 11/9/2004 06:30:00.000 AM
}
],
schema:
{
model:
{
id: "myGridID",
fields:
{
title: { type: "string" },
date5: { type: "date" },
date6: { type: "date" }, // Field not in data: above
// // but generated below in
// // parse:
}
},
},
// Set the data in
// date6 to date5 yyyy, MM, dd, HH, mm and strip off Sec and Ms
// This will make the filter work
// This changes the "data" instead of the "presentation" so the filter will work
parse: function(d) {
$.each(d, function(idx, elem) {
elem.date6 =
new Date(
elem.date5.getFullYear(), // 20yy
elem.date5.getMonth(), // MM 00 - 11 for jan to dec
elem.date5.getDate(), // dd 00 - 31
elem.date5.getHours(), // HH 00 - 23
elem.date5.getMinutes() // mm 00 - 59
// // Sec 0
// // Ms 0
);
});
return d;
}
});
I then create a grid:
// Createe a Kendo grid in DIV
$("#grid").kendoGrid({
dataSource: dsDataSource,
...
columns: [
{ field: "title", title: "Title" , width: "270px" },
{ field: "date5", title: "D 5" , width: "230px", filterable: false, format:"{0:MM/dd/yyyy hh:mm tt}", },
{
// Bind to date6 (rounded), but display date5
field: "date6",
title: "Date5DateTime (no D6 data uses D5)",
width: "330px",
template: "#: kendo.toString(date5, 'yyyy-MM-dd HH:mm:ss.fff') #",
filterable: { ui: DateTimeFilter },
...
}
],
...
I got this to work and am now integrating it into our production application.
The problem I am having is that the production application uses Json/database
so when I try to add the "date6" field into the "model: ... fields: ..." section
it complains about the "date6" field not existing in the Json/database.
Without the "date6" field in the "model..." I can't use the parse: code to strip off
the Sec and Ms.
Is there some other way to fill in the "date6"
without using "parse:" so I don't have to add "date6" to the "model: ... fields: ..."
section?
You don't have to define date6 in the model, it should work perfectly well. The problem is that you defined parse out of schema when it should be inside and then it actually does not get called.
Your dataSource definition should be:
var dsDataSource = new kendo.data.DataSource({
data:
[
{ // 1
title: "Star Wars: A New Hope",
date5: new Date(2001,8,8,21,46,0,0), // 9/8/2001 09:46:00.000 PM
},
{ // 2
title: "Star Wars: The Empire Strikes Back",
date5: new Date(2004,10,9,6,33,0,0), // 11/9/2004 06:33:00.000 AM
},
{ // 3
title: "Star Wars: Return of the Jedi",
date5: new Date("2004/11/09 12:00 AM"), // 11/9/2004 12:00:00.000 AM
},
{ // 4
title: "Star Wars: The Phantom Menace",
date5: new Date(2008,0,10,4,20,0,0), // 1/10/2008 04:20:00.000 AM
},
{ // 5
title: "Star Wars: Revenge of the Sith",
date5: new Date("2004/11/09 06:30 AM"), // 11/9/2004 06:30:00.000 AM
}
],
schema:
{
model:
{
id: "myGridID",
fields:
{
title: { type: "string" },
date5: { type: "date" },
date6: { type: "date" }, // Field not in data: above
// // but generated below in
// // parse:
}
},
// Set the data in
// date6 to date5 yyyy, MM, dd, HH, mm and strip off Sec and Ms
// This will make the filter work
// This changes the "data" instead of the "presentation" so the filter will work
parse: function(d) {
$.each(d, function(idx, elem) {
elem.date6 =
new Date(
elem.date5.getFullYear(), // 20yy
elem.date5.getMonth(), // MM 00 - 11 for jan to dec
elem.date5.getDate(), // dd 00 - 31
elem.date5.getHours(), // HH 00 - 23
elem.date5.getMinutes() // mm 00 - 59
// // Sec 0
// // Ms 0
);
});
return d;
}
}
});
and check it here running
$(document).ready(function() {
var dsDataSource = new kendo.data.DataSource({
data:
[
{ // 1
title: "Star Wars: A New Hope",
date5: new Date(2001,8,8,21,46,0,0), // 9/8/2001 09:46:00.000 PM
},
{ // 2
title: "Star Wars: The Empire Strikes Back",
date5: new Date(2004,10,9,6,33,0,0), // 11/9/2004 06:33:00.000 AM
},
{ // 3
title: "Star Wars: Return of the Jedi",
date5: new Date("2004/11/09 12:00 AM"), // 11/9/2004 12:00:00.000 AM
},
{ // 4
title: "Star Wars: The Phantom Menace",
date5: new Date(2008,0,10,4,20,0,0), // 1/10/2008 04:20:00.000 AM
},
{ // 5
title: "Star Wars: Revenge of the Sith",
date5: new Date("2004/11/09 06:30 AM"), // 11/9/2004 06:30:00.000 AM
}
],
schema:
{
model:
{
id: "myGridID",
fields:
{
title: { type: "string" },
date5: { type: "date" },
date6: { type: "date" }, // Field not in data: above
// // but generated below in
// // parse:
}
},
// Set the data in
// date6 to date5 yyyy, MM, dd, HH, mm and strip off Sec and Ms
// This will make the filter work
// This changes the "data" instead of the "presentation" so the filter will work
parse: function(d) {
$.each(d, function(idx, elem) {
elem.date6 =
new Date(
elem.date5.getFullYear(), // 20yy
elem.date5.getMonth(), // MM 00 - 11 for jan to dec
elem.date5.getDate(), // dd 00 - 31
elem.date5.getHours(), // HH 00 - 23
elem.date5.getMinutes() // mm 00 - 59
// // Sec 0
// // Ms 0
);
});
return d;
}
}
});
$("#grid").kendoGrid({
dataSource: dsDataSource,
columns: [
{ field: "title", title: "Title" , width: "270px" },
{ field: "date5", title: "D 5" , width: "230px", filterable: false, format:"{0:MM/dd/yyyy hh:mm tt}" },
{
// Bind to date6 (rounded), but display date5
field: "date6",
title: "Date5DateTime (no D6 data uses D5)",
width: "330px",
template: "#: kendo.toString(date5, 'yyyy-MM-dd HH:mm:ss.fff') #"
}
]
})
});
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1316/styles/kendo.common.min.css">
<link rel="stylesheet" href="http://cdn.kendostatic.com/2014.3.1316/styles/kendo.default.min.css">
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://cdn.kendostatic.com/2014.3.1316/js/kendo.all.min.js"></script>
<div id="grid"></div>

Kendo grid - summary fields in a single row

is it possible to sum fields in a row of kendo grid? I'm able to summary columns in table but not a single rows. I've set aggregation for each field but it works only for "footerTemplate". I hope my question is clear... Thanks for any help!
Example:
User | Jan | Feb | Mar | Sum
John | 1000 | 2000 | 3000 | ???
Peter | 1500 | 2500 | 3500 | ???
footerTemplate SUM | 2500 | 4500 | 6500 | ???
In DataSource I define aggregation and group:
aggregate: [
{ field: "Jan", aggregate: "sum" },
{ field: "Feb", aggregate: "sum" }, ...
],
...
group: {field: "???", aggregates: [ { field: "Jan", aggregate: "sum" }, { field: "Feb", aggregate: "sum" } ]}
...
columns: [
...,
{ "field": "???", "title": "Summary", "format": "{0:n0}", "footerTemplate": "#= kendo.toString(sum, \"C\")#", "groupFooterTemplate": "#= kendo.toString(sum, \"C\")#" },
]
Thanks very much for any answer
The easiest way of getting it is defining an extra column (lets call it Sum) in the Grid that computes the value for you. This extra column uses a template for compute the value and this computation can be either invoking a function in your model (the cleanest) or directly hard code it.
Example:
// DataSource Definition
var ds = new kendo.data.DataSource({
data: [
{ Id:1, User: "John", Jan : 1000, Feb: 2000, Mar: 3000 },
{ Id:2, User: "Peter", Jan : 1500, Feb: 2500, Mar: 3500 }
],
pageSize: 10,
schema: {
model: {
id: "Id",
fields: {
Id: { type: 'number' },
User: { type: 'string' },
Jan: { type: 'number' },
Feb: { type: 'number' },
Mar: { type: 'number' }
},
Sum: function() {
return this.Jan + this.Feb + this.Mar;
}
}
}
});
Where I've defined a Sum function that computes the total for the fields Jan through Mar.
Then the Grid definition would be:
var grid = $("#grid").kendoGrid({
dataSource: ds,
pageable: true,
columns: [
{ field: "User" },
{ field: "Jan" },
{ field: "Feb" },
{ field: "Mar" },
{ title: "Sum", template: "#= Sum() #" }
]
}).data("kendoGrid");
NOTE: I'm not including the aggregates since you do not have problems with this.
As you can see the Sum column computes the sum when invoked from the template. See it here : http://jsfiddle.net/OnaBai/Bz3Y5/
The second approach would be not having Sum function but computing the value in the template.
var grid = $("#grid").kendoGrid({
dataSource: ds,
pageable: true,
columns: [
{ field: "User" },
{ field: "Jan" },
{ field: "Feb" },
{ field: "Mar" },
{ title: "Sum", template: "#= data.Jan + data.Feb + data.Mar #" }
]
}).data("kendoGrid");
See it implemented here: http://jsfiddle.net/OnaBai/Bz3Y5/2/
The disadvantage with this two approaches is that you have to compute the total each time the template is invoked so if you paginate you might have some extra processing. If you want to avoid this, then you can use parse function in the DataSource:
var ds = new kendo.data.DataSource({
data: [
{ Id:1, User: "John", Jan : 1000, Feb: 2000, Mar: 3000 },
{ Id:2, User: "Peter", Jan : 1500, Feb: 2500, Mar: 3500 }
],
pageSize: 10,
schema: {
model: {
id: "Id",
fields: {
Id: { type: 'number' },
User: { type: 'string' },
Jan: { type: 'number' },
Feb: { type: 'number' },
Mar: { type: 'number' }
}
},
parse: function (d) {
$.each(d, function (idx, elem) {
elem.Sum = elem.Jan + elem.Feb + elem.Mar;
});
return d;
}
}
});
This parse function receives the original data and transforms it in whatever you want, adding, removing, transforming any field in the original data and before sending it to the Grid.
You can see this last approach here : http://jsfiddle.net/OnaBai/Bz3Y5/3/

Highchart: Bar Chart with time duration

I want to create a bar chart with time duration on the bottom axis. It' working for decimal numbers, like 300.5 seconds, or 50.3 hours, but i would like to have the following format:
Hours:Minutes:Seconds. I tried some thing with datalabelformat: datetime, but with no success.
The data would be for example:
Member1 - 10:05:18
Member2 - 12:52:20
Member3 - 18:10:30
Member4 - 19:20:10
Member5 - 19:30:45
Anybody got an idea how to realize that?
Thanks in advance!
You need to set min value and tickInterval, then set dateTimeLabels.
dateTimeLabelFormats: {
millisecond: '%H:%M:%S',
second: '%H:%M:%S',
minute: '%H:%M:%S',
hour: '%H:%M:%S',
day: '%H:%M:%S',
week: '%H:%M:%S',
month: '%H:%M:%S',
year: '%H:%M:%S'
}
Example:
http://jsfiddle.net/TynTT/1/
I came here searching for a solution to my problem. Not sure if this is exactly what you want, and I cannot put in a comment due to low reputation. But this is how I was able to solve it.
This provides result in hh:mm. You can modify it to include seconds.
http://jsfiddle.net/TynTT/11/
It has the y axis in hh:mm format and also displays datalabels as hh:mm.
$(function () {
$('#container').highcharts({
chart: {
type: 'column'
},
yAxis: {
type: 'time',
labels: {
formatter: function () {
var time = this.value;
var hours1=parseInt(time/3600000);
var mins1=parseInt((parseInt(time%3600000))/60000);
return (hours1 < 10 ? '0' + hours1 : hours1) + ':' + (mins1 < 10 ? '0' + mins1 : mins1);
}
}
},
series: [{
data: [1000000, 26500000, 17000050, 79000000, 56000000, 124000000],
dataLabels: {
enabled: true,
formatter: function () {
var time = this.y / 1000;
var hours1=parseInt(time/3600);
var mins1=parseInt((parseInt(time%3600))/60);
return (hours1 < 10 ? '0' + hours1 : hours1) + ':' + (mins1 < 10 ? '0' + mins1 : mins1);
}
},
}]
});
});
I improved above configs by reusing the HC methods:
durationbar: {
chart: {
type: 'column'
},
yAxis: {
type: 'datetime',
showFirstLabel: false,
dateTimeLabelFormats: {
millisecond: '%H:%M:%S'
},
units: [[
'millisecond',
[1000]
], [
'second',
[1, 2, 5, 10, 15, 30]
], [
'minute',
[1, 2, 5, 10, 15, 30]
], [
'hour',
[1, 2, 3, 4, 6, 8, 12]
], [
'day',
[1]
], [
'week',
[1]
], [
'month',
[1, 3, 6]
], [
'year',
null
]],
},
tooltip: {
formatter: null,
pointFormat: '{point.name}: <b>{point.dataLabel.text.textStr}</b><br/>'
},
plotOptions: {
series: {
dataLabels: {
enabled: true,
formatter() {
return this.series.chart.time.dateFormat('%H:%M:%S', this.y)
}
}
}
}
},
screenshot

Adding multiple series to Chart

I'm currently trying to build a chart showing number of downloads of a product per date.
A sample of the current code is as follows:
var downloads = [
{ value: 48, date: new Date("2013/11/01") },
{ value: 50, date: new Date("2013/11/02") },
{ value: 55, date: new Date("2013/11/03") },
{ value: 35, date: new Date("2013/11/04") }
];
$("#chart").kendoChart({
dataSource: {
data: downloads
},
series: [{
type: "line",
aggregate: "avg",
field: "value",
categoryField: "date"
}],
categoryAxis: {
baseUnit: "days",
min: new Date("2013/10/31"),
max: new Date("2013/11/10"),
labels: {
dateFormats: {
days: "dd/MM"
}
}
}
});
It works fine if I have to display data for one product only. How would I proceed to display download data for another product, i.e. adding another series to the chart?
Right! I figured it out myself. Here it is:
$("#chart").kendoChart({
seriesDefaults: {
tooltip: {
visible: true,
},
type:"line",
aggregate:"avg",
field:"value",
categoryField:"date"
},
series: [{
name: "Product 1",
data: [{ value: 48, date: new Date("2013/11/01") }, { value: 50, date: new Date("2013/11/02") }]
},
{
name: "Product 2",
data: [{ value: 55, date: new Date("2013/11/03") }, { value: 35, date: new Date("2013/11/04") }]
}],
categoryAxis: {
baseUnit: "days",
min: new Date("2013/10/31"),
max: new Date("2013/11/10"),
labels: {
dateFormats: {
days: "dd/MM"
}
}
}
});

Resources