How can I build some chart with Chart.js and ng2-charts using an array of revenus & budgets per month date as Data source? - arrays

I want to build some charts using Chart.js and ng2-charts. I have an array of data (week). I would like to know how to build an array of revenu-values and budget-values according to each date. These values will be shown on the Y-axis of the graph and the X-axis contains the labels ( [01, 02, 03, 04, 05, ..., 31] ) which are the month dates :
week = [
{
date = "2019-03-01",
revenu = 1000,
budget = 800
},
{
date = "2019-03-02",
revenu = 1000,
budget = 800
},
{
date = "2019-03-03",
revenu = 1000,
budget = 800
},
{
date = "2019-03-04",
revenu = 1000,
budget = 800
},
...
];
public monthChartLabels= [01, 02, 03, 04, 05, ..., 31];
public monthChartData = [
{
data: [ ? ] // Array of revenu-values according to each date
},
{
data: [ ? ] // Array of budget-values according to each date
}
];
<canvas
baseChart
[chartType]=" 'line' "
[datasets]="monthChartData"
[labels]="monthChartLabels"
[options]="monthLabels"
[legend]="true"
(chartHover)="onChartHover($event)">
</canvas>

Shalom Eli, you should map your original data to the chart's data arrays: week.map(e => e.revenu) for the revenu dataset, and week.map(e => e.budges) for the budget dataset.
Update
Based on your comment, you want to convert your week array to a map keyed by the day of the month of each record:
week.reduce((acc,v)=> ({
...acc,
[new Date(v.date).getDate()]: v
}), {})
And then use your labels array to fetch each value from this map:
revenuData = labels.map(r => this.weekMap[+r] && this.weekMap[+r].revenu);
budgetData = labels.map(r => this.weekMap[+r] && this.weekMap[+r].budget);
See this updated stackblitz: https://stackblitz.com/edit/ng2-charts-line-template-3dfmxn

Related

Is there an issue while the time conversion happens?

I am stuck on this error for about 4 hours now and i tried different solutions
I have a date in an class stored inside of an array, then i tried to get the date from the start of the week, because i wanted to achieve a graph that list the duration of events for the entire week. i have a for loop that iterates through all the dates and compares if there is one or multiple events at that day. I also created a second for loop to iterate through all events at that day if there are any.
Information: recharts needs an array of data to work properly
What i think
I think the issue lays at the part where i try to compare the date of the class which is a normal jscript date object to the moment date. For the comparison I used the isSame function.
Versions
I am using:
moment 2.29.4
react 18.2.0
recharts 2.2.0
I have three script i will provide them down below but cut them down to the relevant parts
Scripts:
MySchedule.js (This is where the chart gets rendered)
<BarChart className="BarChartHours" width={300} height={250} data={formattedData}>
<XAxis dataKey="name" />
<YAxis />
<Legend />
<Bar dataKey="Stunden" fill="#00C49F" radius={[5,5,0,0]} />
</BarChart>
events.js (This is where the class gets created and the array of events is stored)
...
export class Event {
#id;
#title;
#allDay;
#start;
#end;
#personalnumber;
#tablenr;
constructor(id, title, allDay, start, end, personalnumber, tablenr) {
this.#id = id;
this.#title = title;
this.#allDay = allDay;
this.#start = start;
this.#end = end;
this.#personalnumber = personalnumber;
this.#tablenr = tablenr;
}
...
let persID = JSON.parse(localStorage.getItem("currentUser"));
const event1 = new Event(1, "Belegt von John Doe", false, new Date("2023","1","2", "10","0","0"), new Date("2023", "1", "2", "14","0","0"), 1234, 1);
const event2 = new Event(2, "Belegt von Mark Etzel", false, new Date("2023","1","3", "10","0","0"), new Date("2023", "1", "3", "12","0","0"), 1234, 2);
export const eventsArray = [event1, event2];
export const personalEvents = eventsArray.filter(event =>event.personalnumber == persID)
ChartData.js (This is where the array with the values gets created)
const MomentDate = moment();
const currentIsoWeekday = MomentDate.isoWeekday();
// Calculate the date of the first day of the week
const firstWeekDay = MomentDate.clone().subtract(currentIsoWeekday - 1, 'days');
// Set all values to zero
export const RawWorkHours = [0, 0, 0, 0, 0, 0, 0];
for (let i = 0; i < 7; i++) { // Iterate through all days
const neededWeekDay = firstWeekDay.clone().add(i, 'days'); // add i days to iterate through every date of the week
const eventsOnDay = personalEvents.filter(event => moment(event.start).isSame(neededWeekDay, 'day')); // <----- i think the issue lies here
let totalHours = 0; // resets the total hours after every iteration
for (let j = 0; j < eventsOnDay.length; j++) { //Check if there is more than one event per day
const fromTime = moment(eventsOnDay[j].start); // saves the start time in an variable
const toTime = moment(eventsOnDay[j].end); // saves the end time in an variable
const duration = moment.duration(toTime.diff(fromTime)); // calculate the duration of one event
totalHours += duration.asHours(); // add up the duration of the events for each day
}
RawWorkHours[i] = totalHours; // Saves the duration inside of the array
}
console.log(RawWorkHours); // Expected Output: [0, 0, 0, 4, 2, 0, 0]
export const formattedData = [
{"name": "Mo", "Stunden": RawWorkHours[0]},
{"name": "Di", "Stunden": RawWorkHours[1]},
{"name": "Mi", "Stunden": RawWorkHours[2]},
{"name": "Do", "Stunden": RawWorkHours[3]}, // Created so the formatting of the chart works properly
{"name": "Fr", "Stunden": RawWorkHours[4]},
{"name": "Sa", "Stunden": RawWorkHours[5]},
{"name": "So", "Stunden": RawWorkHours[6]}
]

How to map/filter data correctly with luxon (DateTimeNow) and ReactJs

I would like to explain my problem of the day.
I have a array which looks like this
[{status: "closed",createdAt: "2022-01-13T15:28:25.239Z"},{status: "closed",createdAt: "2022-01-10T15:28:25.239Z"},{status: "open",createdAt: "2021-11-25T15:28:25.239Z"}]
I filter to retrieve only the data with the status "closed"
const countData = data?.filter(
(item) => item.status === "closed"
);
const count = countData?.length;
this works well, it correctly returns the number to me.
I would like to add a new filter with Luxon.
I would like to take the date of today with luxon, and, as a result, display the objects that correspond to this date
const dateNow = DateTime.now().toISO();
console.log(dateNow)
2022-01-13T16:23:44.873+01:00
How can I fix this issue?
If you want to check if a given Luxon DateTime object represent the same day of today, you can use hasSame passing date as second parameter
Return whether this DateTime is in the same unit of time as another DateTime. Higher-order units must also be identical for this function to return true. Note that time zones are ignored in this comparison, which compares the local calendar time. Use DateTime#setZone to convert one of the dates if needed.
In your case, you can have something like the following code:
const DateTime = luxon.DateTime;
const input = [{
status: "closed",
createdAt: "2022-01-13T15:28:25.239Z"
}, {
status: "closed",
createdAt: "2022-01-10T15:28:25.239Z"
}, {
status: "open",
createdAt: "2021-11-25T15:28:25.239Z"
}, {
status: "closed",
createdAt: new Date().toISOString()
}];
const todayItems = input.filter(item => {
return DateTime.fromISO(item.createdAt).hasSame(DateTime.now(), 'day') && item.status == "closed";
});
console.log(todayItems)
<script src="https://cdn.jsdelivr.net/npm/luxon#2.3.0/build/global/luxon.js"></script>
Luxon has a function that let's you get a date at certain point of a parameter that's called startOf. So you can do something like:
const todayDate = DateTime.now().startOf("day");
So your todayDate variable will be your current date, but at 00:00:00 time.
And you can make the transformation of the elements date during your filter function to compare them to todayDate like this:
//Please consider that the example for today was at 2022-01-13
const array = [
{
name: "this is for today",
date: "2022-01-13T15:28:25.239Z"
},
{
name: "this was for yesterday",
date: "2022-01-12T15:28:25.239Z"
}];
const todayDate = DateTime.now().startOf("day");
const todayElements = array.filter((element) => {
return DateTime.fromISO(element.date).startOf("day").equals(todayDate);
});

Date sorting is not working in muidatatable

https://codesandbox.io/s/stupefied-volhard-jntyu?fontsize=14&hidenavigation=1&theme=dark
this is my codesand box code. on date column result is not showing correctly.
I think the problem with the sort behaviour is that you are trying to sort strings of dates where the year is the last element.
In case you could edit your data directly, try putting the year, the month and so on. Something like:
const data = [
...,
[
"Mason Ray",
"Computer Scientist",
"San Francisco",
39,
142000,
"2019-10-03"
]
Here is an example using date-fnslibrary to parse your input data.
A detail of how is done:
const data = [...].map(info =>
info.map((val, index) => {
const date = new Date(val)
return index === 5
? isValid(date)
? format(date, 'yyyy-MM-dd')
: val
: val
})
)

Using lodash to prepare data for "last day user registration" chart

I want to use lodash to prepare data for "last day user registration" chart.
Here is my users array:
{
name: toto,
creationDate: 11/11/2016 11:00:00
},
{
name: tata,
creationDate: 11/11/2016 10:00:00
},
{
name: titi,
creationDate: 09/11/2016 10:00:00
}
I need to create a chart with ChartJS that repesent the users registrations over th last day (24 hours).
If I use groupBy :
var grouped = _.groupBy(users, function (user) {
return moment(user.creationDate).get('hour');
});
I can't have the "empty" hours, when there is no user registration, in the final array.
The aim is to have this (let's say it is 18h o'clock) :
labels = ['18', '17', '16', ... '18']
data = [['1', '1', '12, ... '3']]
Could you please help ?
You can create a defauls object with empty arrays and apply _.defaults:
var hours = _.range(0,24);
var defaultHours = _.zipObject(hours, _.map(hours, function(){return [];}));
_.defaults(grouped, defaultHours);
After that, grouped will hold the values you found, or empty array for hours that had no registrations.

Mongodb Mapreduce join array

I have a big collection of songs and want to get most played songs per week, in a array. as example:
{
"_id" : {
"title" : "demons savaites hitas",
"name" : "imagine dragons"
},
"value" : {
"weeks" : [
{
"played" : 56,
"week" : 9,
"year" : 2014
}
]
}
}
It sometimes becomes:
{
"_id" : {
"title" : "",
"name" : "top 15"
},
"value" : {
"played" : 1,
"week" : 8,
"year" : 2014
}
}
The collection which i get the data from is named songs and new fields get added all the time when a songs get added. No unique artistnames or songtitles and every document in the collection looks like this:
{
"_id" : ObjectId("530536e3d4ca1a783342f1c8"),
"week" : 8,
"artistname" : "City Shakerz",
"songtitle" : "Love Somebody (Summer 2012 Mix Edit)",
"year" : 2014,
"date" : ISODate("2014-02-19T22:57:39.926Z")
}
I now want to do a mapreduce which add the new week to the array. It now overwrites it.
I also noted when trying to change to a array, not all the played get counted, with the new mapreduce.
The new mapreduce not working, with weeks:
map = function () {
if (this.week == 9 && this.year == 2014) emit({title:this.songtitle.toLowerCase(), name:this.artistname.toLowerCase()}, {played:1, week:this.week, year:this.year});
}
reduce = function(k, values) {
var result = {};
result.weeks = new Array();
var object = {played:0, week: 0, year: 0};
values.forEach(function(value) {
object.played += value.played;
object.week = value.week;
object.year = value.year;
});
result.weeks.push(object);
return result;
}
db.songs.mapReduce(map,reduce,{out: {reduce:"played2"}})
This is the old one i'm using with is a new field in the collection per week and song:
map = function () {
if (this.week == 10 && this.year == 2014) emit({title:this.songtitle.toLowerCase(), name:this.artistname.toLowerCase(), week:this.week, year:this.year}, {count:1});
}
reduce = function(k, values) {
var result = {count: 0,};
values.forEach(function(value) {
result.count += value.count;
});
return result;
}
db.songs.mapReduce(map,reduce,{out: {merge:"played"}})
I get the information fro the toplist right now from played2 like this:
db.played2.find({'_id.week': 9,'_id.year': 2014}).sort(array("value.count" => -1)).limit(50)
Above line can include any typo because i use mongoclient for php and needed to change it to javascript syntax for you.
What am I doing wrong?
I found out that I could do mapreduce as the code snippet above and then just get this week in a query and another one for previous week and do simple double for with a if to update this week with previous week place.
I made the script in python, which i run also for my mapreduce as a cronjob. As example:
if len(sys.argv) > 1 and sys.argv[1] is not None:
week = int(sys.argv[1])
else:
week = (datetime.date.today().isocalendar()[1]) - 1
year = datetime.date.today().year
previous_week = week - 1
client = MongoClient()
db = client.db
played = db.played
print "Updating it for week: " + str(week)
previous = played.find({"_id.week": previous_week, "_id.year": year}).sort("value.count", -1).limit(50)
thisweek = played.find({"_id.week": week, "_id.year": year}).sort("value.count", -1).limit(50)
thisplace = 1
for f in thisweek:
previous.rewind() # Reset second_collection_records's iterator
place = 1
if previous.count() > 0:
checker = bool(1)
for s in previous:
if s["_id"]["name"] == f["_id"]["name"] and s["_id"]["title"] == f["_id"]["title"]:
result = played.update({"_id.week": f["_id"]["week"], "_id.year": f["_id"]["year"], "_id.title": f["_id"]["title"], "_id.name": f["_id"]["name"]}, {"$set": {"place.previous_week":place, "place.this_week":thisplace}})
checker = bool(0)
print result
place = place + 1
if checker is True:
result = played.update({"_id.week": f["_id"]["week"], "_id.year": f["_id"]["year"], "_id.title": f["_id"]["title"], "_id.name": f["_id"]["name"]}, {"$set": {"place.previous_week":0, "place.this_week":thisplace}})
print result
else:
result = played.update({"_id.week": f["_id"]["week"], "_id.year": f["_id"]["year"], "_id.title": f["_id"]["title"], "_id.name": f["_id"]["name"]}, {"$set": {"place.previous_week":0, "place.this_week":thisplace}})
print result
thisplace = thisplace + 1
print "done."
This seems to work very good. Hopefully mongodb adds support to just update a field or anything in mapreduce to add information to a document without overwrite it.
I'm taking a stab at the structure of your collection based on your input fields, but I don't think mapReduce is the tool you want. Your apparent desired output can be achieved using aggregate :
db.collection.aggregate([
// Match a specific week and year if you want - remove if you want all
{ "$match": { "year": inputYear, "week": inputWeek } },
// Group to get the total number of times played
{ "$group": {
"_id": {
"title": { "$toLower": "$songtitle" },
"name": { "$toLower": "$artistname" },
"week": "$week",
"year": "$year"
},
played: { "$sum": 1 }
}},
// Sort the results by the most played in the range
{ "$sort": { "year": -1, "week": -1, "played": -1 } },
// Optionally limit to the top 15 results
{ "$limit": 15 }
])
That basically is what you appear to be trying to do. So this sums up the "number of appearances" as the number of times played. Then we take the additional steps of sorting the results, and optionally (if you can live with looking for one week at a time) limits the results to a set number. Those last two steps you won't get with mapReduce.
If you are ultimately looking for the "top ten" for each week, as a single query result, then you can look at this for a discussion (and methods to achieve) what we call the "topN" results problem.

Resources