I'm populating a series with "date-value pairs like this:
series: [{
name: "Price",
data:[
[1486684800000, 38],
[1486771200000, 0],
[1486857600000, 0],
[1486944000000, 0],
[1487030400000, 0],
[1487116800000, 58]]
},
But, when for instance I'm populating the series array with "missing dates" like this:
series: [{
name: "Price",
data:[
[1486684800000, 38],
[1487116800000, 58]]
},
ApexCharts will automatically fill the line, and will not fill the missing dates with zero values.
Any ideas on how to force ApexCharts to show missing dates with zero values and not "ignore" them?
I know this question is 2 years old, but I had simillar problem. My backend returns date formatted like YYYY-MM-DD, but you can change this pretty easy.
var startDate = new Date()
startDate.setDate(startDate.getDate() - 30)
var getDaysArray = function (start, end) {
for (var arr = [], dt = new Date(start); dt <= new Date(end); dt.setDate(dt.getDate() + 1)) {
const currDate = new Date(dt).toISOString().split('T')[0]
var amount = response.data.monthly_logs.find(monthlyLog => monthlyLog.created_at__date === currDate) || 0
if (amount) {
amount = amount.price
}
arr.push({ x: new Date(dt), y: amount })
}
return arr
}
var result = getDaysArray(startDate, new Date())
Related
So i have a frontend with a Piechart and want to show the percentage of the ages in my customers (a database table). I've stored the age of each customer so i have an array like this.
const ages = [12,42,23,42,12,65,75,12,43,54,12,53,24,23,54,64,76,12,42];
Given these values, i want to end up having somenthing like this
const data = {
labels: ['12-20', '21-40', '41-60', '61-76']
dataSet: [4, 6, 2, 5] // This is the amount of ages between each range. The sum of these must be equivalent of the length of the array
}
This is what i've tried so far
const ages = [12, 42, 53, 12, 32, 12, 52, 66, 76, 87, 23, 12, 43, 12, 43, 54, 65].sort((a, b) => a - b);
const minAge = Math.min(...ages);
const maxAge = Math.max(...ages);
const q1 = ages[Math.floor(ages.length / 4)];
const q2 = ages[Math.floor(ages.length / 2)];
const q3 = ages[Math.floor(ages.length * 3 / 4)];
let firstRangeCount = 0;
let secondRangeCount = 0;
let thirdRangeCount = 0;
let fourthRangeCount = 0;
for (const age of ages) {
if (age) {
if (age <= q1) {
firstRangeCount++;
} else if (age <= q2) {
secondRangeCount++;
} else if (age <= q3) {
thirdRangeCount++;
} else {
fourthRangeCount++;
}
}
}
const data = {
labels: [
`${minAge} - ${q1}`,
`${q1} - ${q2}`,
`${q2} - ${q3}`,
`${q3} - ${maxAge}`,
],
datasets: {
label: 'Ages',
data: [firstRangeCount, secondRangeCount, thirdRangeCount, fourthRangeCount],
}
}
But the problem with this solution that it isnt dynamic. If the ages array contains less data, 4 ranges wouldn´t be appropiated.
How can i make this ranges "dynamic"?. I've read some about interquartile range but it didn´t help me much
Try this (Descriptive comments has been added in the below code snippet) :
// Input array
const ages = [12,42,23,42,12,65,75,12,43,54,12,53,24,23,54,64,76,12,42];
// data object with range array
const data = {
labels: ['12-20', '21-40', '41-60', '61-76'],
dataSet: []
}
// declare a variable which will contain the count of occurance based on the range
const obj = {};
// logic to find out the counts and pushed into an range array in an object.
data.labels.forEach(label => {
const splittedLabel = label.split('-')
const filteredAges = ages.filter(e => e >= splittedLabel[0] && e <= splittedLabel[1]);
obj[label] = [];
obj[label].push(...filteredAges);
});
// getting the length
const dataSet = Object.values(obj).map(arr => arr.length);
data.dataSet = dataSet;
// Result
console.log(data);
I'mm trying to calculate sum of serviceCost from Firestore. I managed to get all the docs but it seems that the query calculated only the first cost entered of given month.
Code:
firebase
.firestore()
.collection("users")
.doc(uid)
.collection("confirmed-appointments")
.get()
.then((querySnapshot) => {
let serviceCostTotal = 0; //Will hold currentMonth Total Income.
let monthNumber = 0;
let array = [];
querySnapshot.forEach((doc) => {
monthNumber = parseInt(doc.data().month, 10);
serviceCostTotal =
serviceCostTotal + parseInt(doc.data().serviceCost, 10); //Calculate Total Month income using this formula
array[monthNumber - 1] = serviceCostTotal; //Push the income of month X to array in X place
serviceCostTotal = 0; // after pushing, initialize the sum to 0
});
For example:
I want to calculate the total serviceCost for month 10.
Firestore looks like this:
In the loop I'm trying to take the serviceCost of each doc, and push it to array[monthNumber].
The problem is: if i have 2 docs that their value is the same "month: xx" , the loop calculate only the serviceCost value of the first doc.
Meaning that if my sum needs to be 6000, it will be only 2500.
its calculating only this:
while i have this also:
You're explicitly overwriting each element. Try adding to it instead.
array[monthNumber - 1] = serviceCostTotal
// Should be
array[monthNumber - 1] = (array[monthNumber - 1] || 0) + serviceCostTotal
Edit:
As others have said this code works
let monthNumber = 0;
let array = [];
[{ month: 1, serviceCost: 15 },
{ month: 1, serviceCost: 4 },
{ month: 1, serviceCost: 9 },
{ month: 2, serviceCost: 1 },
{ month: 3, serviceCost: 100 }].forEach((doc) => {
monthNumber = parseInt(doc.month, 10);
serviceCostTotal =
serviceCostTotal + parseInt(doc.serviceCost, 10); //Calculate Total Month income using this formula
array[monthNumber - 1] = (array[monthNumber - 1] || 0) + serviceCostTotal; //Push the income of month X to array in X place
serviceCostTotal = 0; // after pushing, initialize the sum to 0
});
console.log(array);
You have to show us how you are validating this and what the actual input and outputs are.
As you said you already have the data in the array index so you can do like this
const querySnapshot = [{
month: 10,
serviceCost: 2500
}, {
month: 10,
serviceCost: 3500
}, {
month: 11,
serviceCost: 1000
}];
const array = [];
querySnapshot.forEach((doc) => {
const monthNumber = parseInt(doc.month, 10);
array[monthNumber - 1] = (array[monthNumber - 1] || 0) + parseInt(doc.serviceCost, 10);
});
console.log(array);
You basically check if whether there is something in that index of that month and add it or use the serviceCostTotal that you calculated
I am trying to group timeslots by overlap but I can't figure out how to do it exactly.
I have a pretty simple array in the form of [{start_at: Date, end_at: Date, etc.etc. }]
And I lay them out in my view like this
<---slot1----><----slot5----><--slot6-->
<--slot2-><--slot4---> <--slot7-->
<----slot3---->
Finding directly overlapping slots isn't that hard, I just compare a slot with the next one with (StartA <= EndB) and (EndA >= StartB)
from here.
Now I want to group my overlapping slots (slot 1, 2, 3, 4 and 5) but not include slot 6 and 7, and put those two in their own group. into something like [[Slot (has 1 through 5)][Slot (has 6 and 7)]]
I am kind of lost with this problem right now and I hope anybody here can help me.
I'd suggest creating a Slot object that holds:
an array of items in the slot,
the earliest start_at date of those items,
the latest end_at of those items.
By keeping an up to date slot-range, you don't have to compare a new item to each of the slot's items. You'll only have to compare to the slot itself.
Now, you'll have to sort your items by start_at. You can then reduce the array by:
Create a Slot for the first item
Set the Slot's start_at and end_at to mimic those of the first item
Go to the second item, check for overlap with the first Slot
If it overlaps,
push the second item to the Slot's items array, and
Set start_at to the minimum of Slot.start_at and item2.start_at
Do the same (max) for end_at
If it does not overlap,
Create a new Slot for the second item, repeat with this Slot and item3 (et cetera)
A sample implementation (I'd advice you to rewrite it based on your personal preferences. I didn't make any neat classes/prototypes/etc., nor did I test it thoroughly)
function createSlot(initialItem) {
var slot = {
items: [initialItem],
start: initialItem.start,
end: initialItem.end
};
slot.addItem = function(item) {
slot.items.push(item);
slot.start = Math.min(slot.start, item.start);
slot.end = Math.max(slot.end, item.end);
}
return slot;
};
function itemsOverlap(item1, item2) {
return item1.start <= item2.end &&
item1.end >= item2.start;
};
var slots = [];
var items = randomItems(10);
items.slice(1).reduce(function(currentSlot, item) {
if (itemsOverlap(currentSlot, item)) {
currentSlot.addItem(item);
return currentSlot;
}
slots.push(currentSlot);
return createSlot(item);
}, createSlot(items[0]));
console.log(
slots.map(function(slot) { return slot.items.length; }));
// Create random data
function randomItems(n) {
var arr = [];
for (var i = 0; i < n; i += 1) {
arr.push(generateRandomItem());
}
return arr.sort(function(a, b) { return a.start - b.start; });
};
function randomHourTimespan() {
return Math.random() * 60 * 60 * 1000;
};
function randomHalfDayTimespan() {
return randomHourTimespan() * 12;
};
function generateRandomItem() {
var start = Date.now() + randomHalfDayTimespan();
var end = start + randomHourTimespan();
return { start: new Date(start), end: new Date(end) };
}
I implemented a simple algorithm to group the slots regarding to the start and end values.
Here is a working fiddle https://jsfiddle.net/LeoAref/gg6q0mby/, and you will find a visual presentation for the grouping.
var timeSlots = [
{start: 0, end: 3},
{start: 1, end: 2},
{start: 2, end: 4},
{start: 4, end: 6},
{start: 4, end: 8},
{start: 5, end: 6}
];
timeSlots.forEach((slot, index) => {
var slotElem = document.createElement('div');
slotElem.classList.add('slot');
slotElem.style.top = index * 25 + 'px';
slotElem.style.left = slot.start * 30 + 'px';
slotElem.style.width = (slot.end - slot.start) * 30 + 'px';
document.body.appendChild(slotElem);
});
var groups = [];
timeSlots.forEach(slot => {
added = false;
if (groups.length) {
var index = 0;
do {
group = groups[index];
if (slot.start >= group.start && slot.start < group.end ||
slot.end <= group.end && slot.end > group.start
) {
group.slots.push(slot);
group.start = Math.min(slot.start, group.start);
group.end = Math.max(slot.end, group.end);
added = true;
}
} while (!added && ++index < groups.length);
if (!added) {
groups.push({start: slot.start, end: slot.end, slots: [slot]});
}
} else {
groups.push({start: slot.start, end: slot.end, slots: [slot]});
}
})
groups.forEach(group => {
var groupElem = document.createElement('div');
groupElem.classList.add('group');
groupElem.style.left = group.start * 30 + 'px';
groupElem.style.width = (group.end - group.start) * 30 - 2 + 'px';
document.body.appendChild(groupElem);
})
#user3297291's description/algorithm of a time interval grouping function is really good. Here's a function that was created/posted on GitHub by the user 'blaston' from several years ago that follows the algorithm. I'm posting it here in case the content/link disappears. I started with blaston's function for its simplicity to follow and swapped array groups in blaston's function for slot objects from #user3297291's post.
// Group all overlaping intervals
// * * * * * * *
// This is an approach to a problem the engineers at Google Calandar/ Outlook probably faced.
// You have events that may overlap and you want to display them in such a way that
// they don't overlap with each other. One approach is to distribute them into columns.
// Each column has events that don't overlap with each other.
// Cost: O(n*log n) if the interval aren't sorted by the starting time,
// O(n) otherwise.
// Sample run: groupOverlapingIntervals([ [2, 5], [5, 6],[3, 4] ])
// Output: [ [ [2, 5], [3, 4], [5, 6] ] ]
function groupOverlapingIntervals(intervals) {
intervals.sort(function(a, b) {
return a[0] - b[0];
});
var groups = [
[intervals[0]]
];
var j = 0;
var end = intervals[0][1];
for (var i = 1; i < intervals.length; i++) {
if (intervals[i][0] <= end) {
if (intervals[i][1] > end) {
end = intervals[i][1];
}
groups[j].push(intervals[i]);
} else {
groups.push([intervals[i]]);
j++;
end = intervals[i][1];
}
}
return groups;
}
var intervals = [
[2, 5],
[5, 6],
[3, 4],
[7, 8],
[6.5, 9],
[10, 11.5]
];
var groups = groupOverlapingIntervals(intervals);
console.log(groups);
So I have json response containing data formatted for creating a chart (using Canvas.js), it's an array of objects, each of which contains y value (which is int) and label (which is SQL date, like 2016-02-06).
What I need is to group these values by dates and get average from them. Data are sorted by date. The problem is, there can be random number of points for one day, and the other day may not have any points at all. So, I need kind of week index (calculating the week of the year is not a solution, because there can be multiple years). For example, I could have the following array:
[0] {y: 2; label: 2016-04-01} // this is week 1 from the start of the array
[2] {y: 6; label: 2016-04-02} // this is still week 1
[3] {y: 1; label: 2016-04-13} // this is week 2
[4] {y: 10; label: 2016-04-28} // this is week 3, if we count weeks only by appearance in the array, not by their actual position
[5] {y: 2; label: 2016-05-01} // this is week 4
[6] {y: 4; label: 2016-05-02} // still week 4
So I need to get these weeks somehow, and then find the average y value, so from the array above I would get:
[0] {y: 4; label: 2016-04-01}
[2] {y: 1; label: 2016-04-13}
[3] {y: 10; label: 2016-04-28}
[4] {y: 3; label: 2016-05-01}
How could this be handled? I suppose I should use PHP methods and give up trying make it on front-end, or may be there are more simple ways to handle this? Or maybe there are js chart plugins that allow automatic grouping? I would highly appreciate any possible help!
In Javascript, you could use an object for collecting and an array for the result.
This proposal utilised the answer of RobG to Get week of year in JavaScript like in PHP.
The later code is a grouping part, which takes the year and the week number and collect the data for the average calculation.
// taken from https://stackoverflow.com/a/6117889/1447675
Date.prototype.getWeekNumber = function () {
var d = new Date(+this);
d.setHours(0, 0, 0);
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
return Math.ceil((((d - new Date(d.getFullYear(), 0, 1)) / 8.64e7) + 1) / 7);
};
var data = [{ y: 2, label: '2016-04-01' }, { y: 6, label: '2016-04-02' }, { y: 1, label: '2016-04-13' }, { y: 10, label: '2016-04-28' }, { y: 2, label: '2016-05-01' }, { y: 4, label: '2016-05-02' }],
avg = [];
data.forEach(function (a) {
var year = a.label.slice(0, 4),
week = new Date(a.label).getWeekNumber(),
key = year + '|' + week;
if (!this[key]) {
this[key] = { count: 0, sum: 0, result: { y: 0, label: a.label } };
avg.push(this[key].result);
}
this[key].count++;
this[key].sum += a.y;
this[key].result.y = this[key].sum / this[key].count;
}, Object.create(null));
console.log(avg);
You can try something like this:
$weeks = array();
foreach ($myData as $info) {
$info = json_decode($info, true);
$dateTime = new DateTime($info['label']);
$weeksKey = $dateTime->format('W-Y');
if (!isset($weeks[$weeksKey])) {
$weeks[$weeksKey] = array('y' => $info['y'], 'label' => $dateTime, 'count' => 1);
} else {
$weeks[$weeksKey]['y'] += $info['y'];
$weeks[$weeksKey]['count']++;
$weeks[$weeksKey]['label'] = $weeks[$weeksKey]['label'] > $dateTime
? $dateTime
: $weeks[$weeksKey]['label'];
}
}
$weeks = array_map(function($week) {
return json_encode(array(
'y' => $week['y'] / $week['count'],
'label' => $week['label']->format('Y-m-d')
));
}, $weeks);
var_dump($weeks); // or var_dump(array_values($weeks));
I'm trying to update 2d array but it's not working here. Please check the below code
var ksatodayvalue = [];
function pad2(n) { return n < 10 ? '0' + n : n }
var dt = new Date();
currentyear = dt.getFullYear().toString();
currentmonth = pad2(dt.getMonth() + 1);
currentday = pad2(dt.getDate());
$.getJSON(jsonrequesturl, function (data) {
$.each(data, function (index, d) {
ksatodayvalue.push("[Date.UTC("+currentyear+", "+currentmonth+", "+currentday-1+", "+d.time.split(':')[0]+", "+d.time.split(':')[1]+", "+d.time.split(':')[2]+"),3]");
});
I want array like that.
var ksatodayvalue = [[Date.UTC(2014, 03, 18, 23, 45, 00),3],[Date.UTC(2014, 03, 18, 23, 30, 00),4],[Date.UTC(2014, 03, 18, 23, 15, 00),6],[Date.UTC(2014, 03, 18, 23, 00, 00),8]];
a) You are creating a string, and probably want an object. b) You probably want values from the incoming data, and not from local variables. Try something along these lines:
ksatodayvalue.push([Date.UTC(this.currentyear, this.currentmonth, this.currentday-1, d.time.split(':')[0], d.time.split(':')[1], d.time.split(':')[2]),3]);
var ksatodayvalue = [];
$.getJSON(jsonrequesturl, function(data) {
$.each(data, function(index, d) {
ksatodayvalue.push([
Date.UTC(
this.currentyear,
this.currentmonth,
this.currentday-1,
d.time.split(':')[0],
d.time.split(':')[1],
d.time.split(':')[2]
), 3
]);
});
}).done(function() {
console.log(ksatodayvalue);
});