Angular - building JSON dynamically for use with chartist - javascript

I have an ionic project and am using the following library:
http://gionkunz.github.io/chartist-js/index.html
Actually drawing the chart is achieved with the following:
var chart = new Chartist.Line('.ct-chart', {
series: [
{
name: 'series-1',
data: [
{x: new Date(143134652600), y: 53},
{x: new Date(143234652600), y: 40},
{x: new Date(143340052600), y: 45},
{x: new Date(143366652600), y: 40},
{x: new Date(143410652600), y: 20},
{x: new Date(143508652600), y: 32},
{x: new Date(143569652600), y: 18},
{x: new Date(143579652600), y: 11}
]
},
{
name: 'series-2',
data: [
{x: new Date(143134652600), y: 53},
{x: new Date(143234652600), y: 35},
{x: new Date(143334652600), y: 30},
{x: new Date(143384652600), y: 30},
{x: new Date(143568652600), y: 10}
]
}
]
}, {
axisX: {
type: Chartist.FixedScaleAxis,
divisor: 5,
labelInterpolationFnc: function(value) {
return moment(value).format('MMM D');
}
}
});
With a DIV:
<div class="ct-chart ct-perfect-fourth"></div>
Instead of having a hardcoded array for the series as shown above, I would like to build this dynamically through a function call.
Any example of how I might do that?
Thanks!

You could generate the data with a little randomness and some fixed variables using a generate function. It's probably also nicer to parametize the creation of your chart for easier re-creation later. Chartist also has a update() function that lets you hand it new data and options, so is especially useful for this.
JSFIDDLE
Javascript
var button = document.getElementById('button');
var options = {
axisX: {
type: Chartist.FixedScaleAxis,
divisor: 5,
labelInterpolationFnc: function(value) {
return moment(value).format('MMM D');
}
}
};
var chart; // initialise the chart variable
function createChart(){
chart = new Chartist.Line('.ct-chart', changeData(), options);
}
function updateChart(){
chart.update(changeData());
}
function changeData(){
var series = [];
// set up series ranges
var numberOfSeries = 2;
var numberOfItems = 8;
var startDate = 143134652600;
var endDate = 143579652600;
var minY = 11;
var maxY = 53;
// creates the 'step' each x-axis item should take
var dateDiff = Math.floor((endDate - startDate) / numberOfItems);
// alternatively set the step to a whole day etc. (makes endDate unnecessary)
// var dateDiff = 24 * 60 * 60 * 1000;
for(var x = 0; x < numberOfSeries; x++){
var seriesData = [];
for(var y = 0; y < numberOfItems; y++){
seriesData.push({
x: new Date(startDate + (dateDiff*y)),
y: getRandomInt(minY, maxY)
})
}
series.push({
name: 'series-'+ (x+1),
data: seriesData
});
}
// return the data to display in the chart
return {series:series};
}
/**
* Returns a random integer between min (inclusive) and max (inclusive)
* Using Math.round() will give you a non-uniform distribution!
*/
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
button.addEventListener('click', updateChart);
createChart(); // generate chart initially
HTML
<button id="button">
Change Data
</button>
<div class="ct-chart ct-perfect-fourth"></div>

This is a rough example; you could replace the input series1, series2 with an array of arrays and make the lower for-loops two wrapped loops to handle multiple series. This would also entail adding the objects to the series array in the outer loop.
For now, try something like this:
function generateJSON(series1, series2) {
var chartInternal = {
series: [
{
name: 'series-1',
data: []
},
{
name: 'series-2',
data: []
}
]
}, {
axisX: {
type: Chartist.FixedScaleAxis,
divisor: 5,
labelInterpolationFnc: function(value) {
return moment(value).format('MMM D');
}
}
};
for (var i = 0, len = series1.length; i < len; i++) {
chartInternal.series[0].data.push({x: new Date(series1[i].date), y: series1[i].y});
}
for (var i = 0, len = series2.length; i < len; i++) {
chartInternal.series[1].data.push({x: new Date(series2[i].date), y: series2[i].y});
}
return chartInternal;
}
Usage:
var series1 = [
{ date: 1234567, y:52 },
... more
];
var series2 = [
{ date: 7654321, y:52 },
... more
];
var chart = new Chartist.Line('.ct-chart', generateJSON(series1, series2))

Related

HightCharts Renko chart disapear after zoom

I used this example in the HightCharts library to make a Renko chart, Everything works fine with this example. But when I use my own data to show a chart It works fine but when I zoom the chart it disappears. I don't know what the problem the data is the same as the example.
The example with my data
https://jsfiddle.net/aypx6nfo/
Before zoom.
After zoom
MY CODES
function linearDataToRenko(data, change) {
var renkoData = [],
prevPrice = data[0][1],
prevTrend = 0, // 0 - no trend, 1 - uptrend, 2 - downtrend
length = data.length,
i = 1;
for (; i < length; i++) {
if (data[i][1] - data[i - 1][1] > change) {
// Up trend
if (prevTrend === 2) {
prevPrice += change;
}
renkoData.push({
x: data[i][0],
y: prevPrice,
low: prevPrice,
high: prevPrice + change
});
prevPrice += change;
prevTrend = 1;
} else if (Math.abs(data[i][1] - data[i - 1][1]) > change) {
if (prevTrend === 1) {
prevPrice -= change;
}
// Down trend
renkoData.push({
x: data[i][0],
y: prevPrice,
low: prevPrice - change,
high: prevPrice,
color: 'black'
});
prevPrice -= change;
prevTrend = 2;
}
}
return renkoData;
}
$.getJSON(`https://api.twelvedata.com/time_series?symbol=AAPL&interval=1day&apikey=MY-API-KEY&outputsize=500`, function(data) {
let tempData = [];
for (var i = 0; i < data.values.length; i++) {
tempData.push([
new Date(data.values[i].datetime),
parseFloat(data.values[i].volume),
]);
}
// Create the chart
Highcharts.stockChart('container', {
rangeSelector: {
selected: 1
},
title: {
text: 'AAPL Stock Price'
},
series: [{
name: 'AAPL',
type: 'columnrange',
fillColor: 'transparent',
turboThreshold: 0,
groupPadding: 0,
pointPadding: 0,
borderWidth: 1,
data: linearDataToRenko(tempData, 1),
dataGrouping: {
enabled: false
},
tooltip: {
valueDecimals: 2
}
}]
});
});
You need to use timestamps in milliseconds as x values and sort your data.
let tempData = [];
for (var i = 0; i < data.values.length; i++) {
tempData.push([
new Date(data.values[i].datetime).getTime(),
parseFloat(data.values[i].volume),
]);
}
tempData.reverse();
Live demo: https://jsfiddle.net/BlackLabel/f8sex6zj/
API Reference: https://api.highcharts.com/highstock/series.columnrange.data

How to create multiple objects at once and push inside an array

I wanted objects like this:
[{ age: 3, area: 5 },
{ age: 4, area: 15 },
{ age: 19, area: 3 },
{ age: 16, area: 11 },
{ age: 20, area: 4 },
{ age: 6, area: 9 }]
The approach was to create new objects each time and push it inside an array.
function Numstuff(age,area) {
this.age = age,
this.area = area
}
var numObjArray = []
var createObj = new Numstuff (Math.floor(Math.random() * 20),
Math.floor(Math.random() * 20))
numObjArray.push(createObj)
But This pushes only one. How to create multiple objects and push inside an array?
You can make an array directly with Array.from() and fill it with your objects by passing a function:
function Numstuff(age,area) {
this.age = age,
this.area = area
}
// make 10 numstuffs
const num = 10
let arr = Array.from({length: num}, () => new Numstuff (Math.floor(Math.random() * 20), Math.floor(Math.random() * 20)))
console.log(arr)
What about this:
var array = [];
var obj = {};
for(var i=0;i<=10;i++){
obj = {age: i, area: Math.random()}
array.push(obj);
}
console.log(array)
Maybe with a for loop. Using you own code:
for (var i = 0; i < 9; i++) {
var createObj = new Numstuff (Math.floor(Math.random() * 20),
Math.floor(Math.random() * 20))
numObjArray.push(createObj)
}

Generate collision objects from multidimensional array for HTML5 tile map

I am creating an HTML5 platform game using objects for collision detection and using a 2d tile map to render the level. That is all working.
I want to use the same 2d array to build the object array dynamically to allow the player to build maps as required and also for ease of creating the maps in the first place. When hardcoding the object array, everything works so I know that the collision detect and game engine work.
While I can create objects for each individual array element, I am looking to build objects that have width based on the number of matching elements in the array, (each element is 25x25) i.e. if 3 array elements are 1,1,1 then the object will have a width of 75. Maybe some code will help explain:
The following tile array
var arr1 = [
[0,0,0,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,2,2,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[3,3,3,0,0,0,0,0,0,0]
];
should produce the following object array:
[
{x: 75, y: 0, width: 100, height: 25, value: 1},
{x: 75, y: 50, width: 50, height: 25, value: 2},
{x: 0, y: 100, width: 75, height: 25, value: 3}
]
but it instead it is producing the following:
[
{x: 75, y: 0, width: 25, height: 25, value: 1},
{x: 100, y: 0, width: 25, height: 25, value: 1},
{x: 125, y: 0, width: 25, height: 25, value: 1}
]
My logic is obviously wrong but I can't for the life of me get it.
Example code is below:
Any help really appreciated:
var tmpObj = {};
var objArr = [];
var arr1 = [
[0,0,0,1,1,1,1,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[0,0,0,2,2,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0],
[3,3,3,0,0,0,0,0,0,0]
];
for (let i=0; i<arr1.length; i++) {
for (let j=0; j<arr1[i].length; j++) {
if (arr1[i][j] > 0 && arr1[i][j] < 6) { // platform blocks only 1 - 5
if (tmpObj.x === undefined) {
tmpObj = {
x: j * 25,
y: i * 25,
width: 25,
height: 25,
value: arr1[i][j]
}
} else if (arr1[i][j] == arr1[i][j-1] && arr1[i][j] == tmpObj.v) {
tmpObj.w += 25;
} else if (arr1[i][j] !== tmpObj.v) { // new tile type
objArr.push(tmpObj);
tmpObj = {
x: j * 25,
y: i * 25,
width: 25,
height: 25,
value: arr1[i][j]
}
} else {
objArr.push(tmpObj);
tmpObj = {};
}
}
}
}
console.log(objArr);
Looking at what you are trying to do your implementation is way too complicated. Rather than hunt down the bug (for which I would have used devTools and stepped through the code line by line to find where the problem was.) I rewrote the function using a while loop to find the width of joined tiles.
I took liberty with the object property names but I am sure you can change it to your needs.
const objArr = [];
const arr1 = [
[0,0,0,1,1,1,1,0,1,0],
[2,0,0,0,0,0,0,0,0,3],
[0,0,0,4,4,0,4,4,4,4],
[0,0,0,0,0,0,0,0,0,0],
[3,3,3,5,5,4,0,0,0,0]
];
const tileSize = 25;
for (let i = 0; i < arr1.length; i++) {
const row = arr1[i]
for (let j = 0; j < row.length; j++) {
if (row[j] > 0 && row[j] < 6) {
let count = j + 1;
while (count < row.length && row[count] === row[j]) { count += 1 }
objArr.push({
x: j * tileSize,
y: i * tileSize,
w: tileSize * (count - j),
h: tileSize,
tile: row[j]
});
j = count - 1;
}
}
}
// show results
objArr.forEach(log);
// unrelated to answer. (I hate the console SO has for snippets.)
function log(data){
show.appendChild(
Object.assign(
document.createElement("div"), {
textContent :
Object.keys(data).reduce((str, key) => {
return str + (" " + key+ ": " + data[key]).padEnd(8,".");
}, ""
)
}
)
);
}
<code id="show"></code>

Highcharts data set array

I have a chart in highcharts and i'm experiencing trouble passing through data. Right now the problem is the graph is completely vertical, and it is receiving the wrong points for the graph. Its saying its totals are 80, and 2011. The totals are supposed to be 50, and 80.
var myarrays = [2011, 1, 12, 50, 2011, 2, 13, 80];
data:
(function () {
var data = [];
for (var i = 0; i < myarrays.length; i++)
{
data.push(myarrays[i], myarrays[i + 1], myarrays[i+2], myarrays[i+3]);
i+2;
}
return data;
}())
The out put is supposed to return, two sets because the series takes in 4 parameters,
[2011, 1, 12, 50]
[2011, 2, 13, 80]
Also when i manually pass in variables, the format that seems to work is
[[Date.UTC(2011, 7, 11), 101]
So is there any way to turn the return to the above format?
var myarrays = [2011, 1, 12, 50, 2011, 2, 13, 80];
$(function() {
$('#container').highcharts({
chart: 'spline',
},
series: [{
name: 'MySeries',
data:
(function() {
var data = [];
for (var i = 0; i < myarrays.length; i++) {
data.push(myarrays[i], myarrays[i + 1], myarrays[i + 2], myarrays[i + 3]);
i +=3;
}
return data;
}]
});
)]);
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="container" style="height: 400px"></div>
A data can be provided to the spline series in the following forms...
data = [y1, y2, ..., yn];
data = [[x1, y1], [x2, y2], ..., [xn, yn];
data = [[name1, y1], [name2, y2], ..., [namen, yn]];
data = [{x: x1, y: y1}, {x: x1, y: y2}, ... {x: xn, y: y2}];
where x and y values are numbers and name values are strings. If x values are not included then the x values are automately calculated starting at 0 and incrementing by 1.
In your example, the myarray has the form [year1, month1, day1, total1, year2, month2, day2, total2, ...] where every four values represent a single data point in the chart. You need to combine the year, month, and day values into a single number or string value which will then be used as the x or name value for the data point. The total value will be used as the y value for the data point. The code might then look something like...
var myarrays = [2011, 1, 12, 50, 2011, 2, 13, 80];
$(function() {
$('#container').highcharts({
chart: {
type: 'spline'
},
series: [{
name: 'MySeries',
data: (function() {
var data = [];
for (var i = 0; i + 3 < myarrays.length; i += 4) {
var name = myarrays[i] + "/" + myarrays[i + 1] + "/" + myarrays[i + 2];
var y = myarrays[i+3];
data.push([name, y]);
}
return data;
})()
}]
});
});

Array of objects to line chart series? (+ for Crossfilter solution)

In JavaScript, how do I transform a "long table format" to a data structure suitable for a line chart:
var data = [
{"type":"A"," year":2000," value":50},
{"type":"A"," year":2001," value":51},
{"type":"A"," year":2002," value":52},
{"type":"B"," year":2000," value":60},
{"type":"B"," year":2001," value":55},
{"type":"B"," year":2002," value":57}
]
=>
var series = [
{type: "A", values : [{x: 2000, y: 50}, {x: 2001, y: 52},] },
{type: "B", values : [{x: 2000, y: 60}, {x: 2001, y: 55},] },
]
A vanilla JavaScript solution as well as a solution with the Crossfilter library - that can work on any dimension of the data - would be valuable:
https://github.com/square/crossfilter/wiki/API-Reference
Good question, but not staightforward ; )
Have a look at https://github.com/NickQiZhu/dc.js/pull/91
you'll have something like :
cr = crossfilter(data);
typeDim=cr.dimension(function(d) {return d.type});
typeGroup=typeDim.group();
yearDim=cr.dimension(function(d) {return d.year});
valDim=cr.dimension(function(d) {return d.value});
yearTypeGroup = dateDimension.group();
yearTypeGroup.reduce(
// add
// pivot[0].idx()[i] returns the index of statusGroup
// the reduce function hence construct a key-value object, keys being indexes of the pivot group.
function(p, v, i, pivot) {
++p[pivot[0].idx()[i]].count;
p[pivot[0].idx()[i]].value += +v.value;
return p;
},
//remove
function(p, v,i,pivot) {
--p[pivot[0].idx()[i]].count;
p[pivot[0].idx()[i]].value -= +v.value;
return p;
},
//init
function(pivot) {
var l, i, obj ={};
if(l = pivot[0].all().length){
for(i=0; i<l; ++i) {
obj[i] = {count:0, value:0}
}
}
return obj
}
);
yearTypeGroup.pivot(typeGroup)
Good luck ; )
C.

Categories

Resources