Is there an issue while the time conversion happens? - reactjs

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]}
]

Related

Updating array of objects with timestamps

I have a data structure like this :
var records = [
{
firstName: "Akira",
id: "0543236543",
},
{
firstName: "Harry",
id: "0994372684",
},
{
firstName: "Sherlock",
id: "0487345643",
},
{
firstName: "Kristian",
id: "04873452342",
},
];
I want to create a function that would add the date property (adjusted for hours) to each of object in the array depending on the length of the array.
Here's my code so far
function updateObject(obj) {
let timeNow = new Date();
let addHours = (h) => timeNow.setHours(timeNow.getHours() + h);
const ids = obj.map((record) => record.id);
const lastNames = obj.map((record) => record.id);
const time = obj.map(
(record, index) => (record["time"] = new Date(addHours(index)))
);
return obj;
}
This returns:
[
{
firstName: 'Akira',
id: '0543236543',
time: 2021-05-18T12:25:59.366Z
},
{
firstName: 'Harry',
id: '0994372684',
time: 2021-05-18T13:25:59.366Z
},
{
firstName: 'Sherlock',
id: '0487345643',
time: 2021-05-18T15:25:59.366Z
},
{
firstName: 'Kristian',
id: '04873452342',
time: 2021-05-18T18:25:59.366Z
}
]
First issue: I want to update the time by adding 0,1,2,3 hours to the current time.
Currently it:
adds 0 to time and sets is a current time
adds 1 to updated time and set it a as a current time and so on.
Second:
I want to modify the function so it takes an add additional argument, "time increment" that if set to x will update the time property for first x records for 0 hours and the next x records for 1 hour.
Hope that makes sense :)
First issue can be solved by changing addHours to be a pure function that does not mutate timeNow:
const timeNow = Date.now();
const addHours = (h) => new Date(timeNow + h * 60 * 60 * 1000);
Second issue can be solved by looking at the index in the map:
const time = obj.map(
(record, index) => (record["time"] = new Date(addHours(index > increment ? 1 : 0))));
And in general, I would advise against mutating objects/arrays. Pure functions are usually easier to reason about and offer fewer surprises.
I would write the updateObject function so it does not mutate the original, but returns a new array with new objects.
Thanks! What if the increment parameter defines how many first x records should have the time property increased by 1h
For example my contacts array length is 4.
function updateObject(records, 1) {
\\code here
}
The first object would have the time property increased by 0h.
The second object would have the time property increased by 1h.
The third object would have the time property increased by 2h.
The fourth object would have the time property increased by 3h.
function updateObject(records, 3) {
\\code here
}
The first 3 objects would have the time property increased by 0h.
The fourth object would have the time property increased by 1h.
Do I need to slice the array into sub arrays first?

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

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

Angular 6 compare two JSON Arrays

I am fetching notifications using API. Now here is the scenario:
1- I called the API, got 2 records and stored them in local variable (notifications) to show in view.
[{"id": 1, "notification": "bla"}, {"id": 2, "notification": "bla bla"}]
2- I am calling the same API to check for new notification after every 5 seconds. This time I need to compare the API response with the local variable, so if no new notification (different id) is in the record, don't push in local variable, other push.
I have tried something like this:
var val = JSON.parse(data);
if( val.length > 0 ) {
for( var i = 0; i < val.length; i++ ) {
this.notifications.forEach(element => {
if( element.id != val[i].id ) {
this.notifications.push(val[i]);
}
});
}
}
But its adding duplicate records. Any help would be appreciated.
Thanks
You need to use Array.find() to find the duplicate object in the val array.
var notifications = [{"id": 1, "notification": "bla"}, {"id": 2, "notification": "bla bla"}];
var data = `[{"id": 1, "notification": "bla"}, {"id": 4, "notification": "bla bla bla"}]`
var val = JSON.parse(data);
if( val.length > 0 ) {
val.forEach((obj)=>{
var existNotification = notifications.find(({id}) => obj.id === id);
if(!existNotification){
notifications.push(obj);
}
});
}
console.log(notifications);
Currently, you are getting duplicated element because the id value is compared against all the existing id values in notifications array. So, if one of the id do not match with another, it is pushed immediately into the notifications array. So, the better way and simple way is to use a find operation on the array to check the existing object.
A more robust way to handle this could be by using map in JS.
Rather than iterating the notifications (size = m) for every object in the data (size = n) would result in more time complexity of O(m x n).
Hence it could be done in O(n) as below:-
var notifications = new Map();
// assuming you get this at the beginning
var data1 = `[{"id": 1, "notification": "bla"}, {"id": 2, "notification": "bla bla"}]`;
checkAndAddNotification(data1);
// assuming you get this at the second time
var data2 = `[{"id": 1, "notification": "bla"}, {"id": 4, "notification": "bla bla bla"}]`
checkAndAddNotification(data2);
function checkAndAddNotification(data){
var val = JSON.parse(data);
if (val){
val.forEach((obj)=>{
var existNotification = notifications.get(obj.id);
if(!existNotification){
notifications.set(obj.id, obj);
}
});
}
}
console.log(notifications);
notifications.forEach((notification) => console.log(notification));
Please open the browser console as well while running the code.
Even if you plan to iterate over the map, the order would be preserved in the order the objects were inserted. Please look here for more.

How to convert datetime in seconds ANGULAR JS

I am fetching data from JSON in my controller and need to convert it into seconds.
[
{
"Id": "0",
"Name": "Subscriber",
"ItemsCount": 5,
"ItemsFailedCount": 6,
"ExecutionStart": "2015-08-01T08:01:00.9748076+01:00",
"ExecutionEnd": "2015-08-01T08:01:00.9748076+01:00"
}
]
What I am trying to achieve is getting the difference between "ExecutionStart" and "ExecutionEnd" in seconds.
I have looked upon for the solution of this on internet and couldn't find any solution unfortunately. So I have nothing to show that I've tried.
Kindly help.
First create Date objects from the date strings:
var executionStart = new Date(data[0].ExecutionStart);
var executionEnd = new Date(data[0].ExecutionEnd);
Then subtract the dates and divide by 1000 because difference will be in milliseconds and there are 1000 milliseconds in a second.
var diffSeconds = (executionEnd - executionStart) / 1000;
JsFiddle
solution using date.parse();
var data = [
{
"Id": "0",
"Name": "Subscriber",
"ItemsCount": 5,
"ItemsFailedCount": 6,
"ExecutionStart": "2015-08-01T08:01:00.9748076+01:00",
"ExecutionEnd": "2015-08-01T08:01:10.9748076+01:00"
}
];
var executionStarts = Date.parse(data[0].ExecutionStart);
var executionEnds = Date.parse(data[0].ExecutionEnd);
var diffSeconds = (executionEnds - executionStarts)/1000;
alert(diffSeconds);

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