I am creating a calendar to view shifts in calendar.I will be getting data`s from backend as array of values
the array will be
var shift = [morning shift,10:00,15:00,10/10/2018,15/10/2018],[[noon shift,10:00,15:00,10/09/2018,15/09/2018]]
which denotes [shift name,shift start time,shift end time,shift start date, shift end date]
I have to pass this value in an object as
var repeatingEvents = [{
title:"morning shift",
start: '10:00', // a start time (10am in this example)
end: '15:00', // an end time (6pm in this example)
ranges: [{
start:new Date(10/10/2018),
end: new Date(15/10/2018),
}],
},
{
title:"morning shift",
start: '10:00', // a start time (10am in this example)
end: '15:00', // an end time (6pm in this example)
ranges: [{
start:new Date(10/09/2018),
end: new Date(15/09/2018),
}],
}],
}];
here i am having a multidimensional object.
for
apiData=[morningShift,10/09/2018,15/09/2018]
i have mapped the values and its working good
events : apiData.map(([start,end,title])=>({
start: new Date(start),
end: new Date(end+'T00:00:00-07:00'),
title,
className: 'bg-primary'})
),
How to do this for multidimentional object.I am a beginner in js and could anyone help me with this ?Thanks in advance
Related
I'm working on a calendar plugin that would show my occupation overview ordered as following:
Now I've got the event on the calendar with moment.js, so they are formatted like this (sorted on Start date):
let events = [{
name: 'Event A',
start: '01-11-2021 00:00',
end: '08-11-2021 00:00',
},{
name: 'Event C',
start: '03-11-2021 00:00',
end: '06-11-2021 00:00',
},{
name: 'Event E',
start: '05-11-2021 00:00',
end: '08-11-2021 00:00',
},{
name: 'Event D',
start: '07-11-2021 00:00',
end: '12-11-2021 00:00',
},{
name: 'Event B',
start: '10-11-2021 00:00',
end: '17-11-2021 00:00',
},]
Expected occupationOverview array would be something like:
let ooArray = [
{ // Longest/bottom bar
start: '01-11-2021 00:00',
end: '17-11-2021 00:00',
group: 1
},
{ // Middle bar 1
start: '03-11-2021 00:00',
end: '08-11-2021 00:00',
group: 2
},
{ // Middle bar 2
start: '10-11-2021 00:00',
end: '12-11-2021 00:00',
group: 2
},
{ // Top bar 1
start: '05-11-2021 00:00',
end: '06-11-2021 00:00',
group: 3
},
{ // Top bar 2
start: '07-11-2021 00:00',
end: '08-11-2021 00:00',
group: 3
},]
I have honestly no clue how to group the calendar events so they give back an array with the start and end times as resulted in the red box.
Anybody that can help me figure this out?
Thanks!
Based on the updated question, sort all the dates keeping the attribute for start and end. Process them in order so that the first date (that must be a start) starts level 1, which is single booked.
If the next date is an end, that ends the bar. However, if the next date is a start, that increases the level (i.e. double booked). The following is an implementation, you might want to sort the bars by level or start date.
The function below firstly gets all the dates sorted in ascending order with their type - start or end. It then processes each date - start dates create a new bar, end dates end the most recent bar. When ended, the last bar is popped off starts and added to bars, which is an array of finished bars.
This depends on the source data being valid, i.e. it must start with a start and end with an end, they must be in the right order and of equal number.
One enhancement would be to ensure that where a start and end have the same date, the start is always sorted before the end so zero length events (milestones?) don't get ordered end-start, which would cause the level to be decremented before it's incremented. There may be other issues with starts and ends that have the same date and time, please test.
// Parse date in D-M-Y H:m format
function parseDMY(s) {
let [D, M, Y, H, m] = s.split(/\D/);
return new Date(Y, M - 1, D, H, m);
}
// Format as DD-MM-YYYY HH:mm
function formatDate(d) {
let z = n => ('0'+n).slice(-2);
return `${z(d.getDate())}-${z(d.getMonth()+1)}-${d.getFullYear()} ` +
`${z(d.getHours())}:${z(d.getMinutes())}`;
}
// Generates "occupation" bars
function calcBookingLevels(events) {
// Get sorted array of [{type, Date}]
let dates = events.reduce( (dates, event) => {
dates.push({type: 'start', date: parseDMY(event.start)},
{type: 'end', date: parseDMY(event.end)});
return dates;
}, []).sort((a, b) => a.date - b.date);
// Process dates to get occupation bars with levels
let bars = [];
let starts = [];
let level = 0;
dates.forEach(date => {
// If it's a start, start a new bar
if (date.type == 'start') {
let bar = {level: ++level, start: formatDate(date.date)};
starts.push(bar);
// Otherwise it's an end, close the most recent bar and
// move to bars array
} else {
let t = starts.pop();
t.end = formatDate(date.date);
--level;
bars.push(t);
}
})
return bars;
}
// Sample data
let events = [{
name: 'Event A',
start: '01-11-2021 00:00',
end: '08-11-2021 00:00',
},{
name: 'Event C',
start: '03-11-2021 00:00',
end: '06-11-2021 00:00',
},{
name: 'Event E',
start: '05-11-2021 00:00',
end: '08-11-2021 00:00',
},{
name: 'Event D',
start: '07-11-2021 00:00',
end: '12-11-2021 00:00',
},{
name: 'Event B',
start: '10-11-2021 00:00',
end: '17-11-2021 00:00',
},];
// Run it...
console.log(calcBookingLevels(events));
If the dates were in YYYY-MM-DD HH:mm format then they could be sorted as strings without converting to Dates.
Looks like you need to get the diff in milliseconds, and sort from greatest to least. You'll have to play with this to see which way it sorts (might need to flip the 'a' and 'b' on the diff statement, as I didn't run it against your array)
const sortedArray = array.sort((a, b) => a.diff(b))
With the help of #RobG I figured it out. Here is my answer for interested people:
// Not needed for answer, but result gave warnings so yeah
moment.suppressDeprecationWarnings = true;
let newItems = []
let dateArray = []
let events = [{
name: 'Event A',
start: '01-11-2021 00:00',
end: '08-11-2021 00:00',
},{
name: 'Event C',
start: '03-11-2021 00:00',
end: '06-11-2021 00:00',
},{
name: 'Event E',
start: '05-11-2021 00:00',
end: '08-11-2021 00:00',
},{
name: 'Event D',
start: '07-11-2021 00:00',
end: '12-11-2021 00:00',
},{
name: 'Event B',
start: '10-11-2021 00:00',
end: '17-11-2021 00:00',
},]
// Make one big array with all start and end dates
events.forEach(event=> {
dateArray.push({
type: 'start',
date: event.start
}, {
type: 'end',
date: event.end
})
})
// Sort the dates from first to last
dateArray = dateArray.sort((left, right) => {
return moment(left.date).diff(moment(right.date))
})
let groupID = -1
// Loop through the array with all dates and add them to a group
// based on if the current date is a start or end
for (let i = 0; i < dateArray.length; i++) {
if (dateArray[i].type === 'start') groupID++
else groupID--
for (let ii = 0; ii <= groupID; ii++) {
if (dateArray[i + 1] &&
!moment(dateArray[i].date).isSame(dateArray[i + 1].date)) {
newItems.push({
group: 1,
start: dateArray[i].date,
end: dateArray[i + 1].date,
subgroup: 'sg_' + ii,
subgroupOrder: ii
})
}
}
}
console.log(newItems)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
I'm working on a calendar app, which has an Agenda from "react-native-calendars". To display dates it needs the following:
items={{'2012-05-22': [{name: 'item 1 - any js object'}], ...}}
In my firebase backend I'm storing the start and end day of an appointment and also an array, which includes every day of it (so if the appointment started yesterday and ends tomorrow, the array stores yesterday, today and tomorrow as dates. The amount of dates in the array varies!)
The api data structure is the following:
apiData = [
{data: {allDays: ["2021-08-18", "2021-08-19", "2021-08-20"],
start: "2021-08-18",
end: "2021-08-20"
},
id: "string"},
{data: {allDays: ["2021-08-20", "2021-08-21", "2021-08-22", "2021-08-23"],
start: "2021-08-20",
end: "2021-08-23"
},
id: "string"},
//more Data
]
I got the following code to map the documents:
let markedDayAll = {};
{apiData.map(({data: {start, end, allDays}}) => (
markedDayAll[alldays] = [{
start: start,
end: end
}]
))};
which outputs in the correct "items" format, but right now, obviously, it is displayed like this:
items={{
'2021-08-18, 2021-08-19, 2021-08-20': [{start: "2021-08-18, end: "2021-08-20}]
}}
But I need the following:
items={{
'2021-08-18': [{start: "2021-08-18, end: "2021-08-20}],
'2021-08-19': [{start: "2021-08-18, end: "2021-08-20}],
'2021-08-20': [{start: "2021-08-18, end: "2021-08-20}]
}}
How do I properly map the data, so I get the desired result?
You can use array#reduce to iterate through each object and then iterate through each date in allDays and generate your object.
const apiData = [ {data: {allDays: ["2021-08-18", "2021-08-19", "2021-08-20"], start: "2021-08-18", end: "2021-08-20" }, id: "string"}, {data: {allDays: ["2021-08-20", "2021-08-21", "2021-08-22", "2021-08-23"], start: "2021-08-20", end: "2021-08-23" }, id:"string"} ],
result = apiData.reduce((r, {data}) => {
data.allDays.forEach(date => {
r[date] ??= [];
r[date].push({start: data.start, end: data.end});
});
return r;
},{});
console.log(result);
I have a list of times slots here eg:
const times = [
{ start: '2020-07-09T08:00:00.000+02:00', end: '2020-07-09T09:30:00.000+02:00' },
{ start: '2020-07-09T08:30:00.000+02:00', end: '2020-07-09T10:00:00.000+02:00' },
]
While I'm trying to sort them by day using momentjs and lodash to get something like:
{
{startTime: '08:00', endTime: '08:30'}
{startTime: '08:30', endTime: '09:00'}
{startTime: '08:00', endTime: '08:30'}
{startTime: '08:30', endTime: '09:00'}
}
and I ended up with this solution for now:
const groupedAndFormatted = groupBy(times, date => moment(date.start_time).startOf('day').format('MMM Do YY'))
But this one didn't really give me the correct solution, any ideas?
First you need to sort them into the correct order, we will do this with sort and unix time stamps.
Then group them with the dddd
const times = [
{ start_time: '2020-07-09T08:00:00.000+02:00', endTime: '2020-07-09T09:30:00.000+02:00' },
{ start_time: '2020-07-09T08:30:00.000+02:00', endTime: '2020-07-09T10:00:00.000+02:00' },
{ start_time: '2020-07-07T09:00:00.000+02:00', endTime: '2020-07-07T10:30:00.000+02:00' }
];
const sorted_times = times.sort((a,b) => moment(a.start_time).unix() - moment(b.start_time).unix())
const grouped = _.groupBy(sorted_times, date => moment(date.start_time).format("dddd"))
const formatted = _.mapValues(grouped, dates => {
return dates.map(times => {
return {
start_time: moment(times.start_time).format("hh:mma"),
endTime: moment(times.endTime).format("hh:mma"),
}
})
})
However this will not work if you end up having multiple tuesdays on different dates.
I am trying to read through an array of objects (that are start/end times) and combine two (or more) of those times if they are back to back.
ie. the first objects end time is the same as the next objects start time. If they are, combine them. Then check the newly combined objects end time with the next object in the array
Here is a simplified array of times:
var times = [
{
start: 1,
end: 2
},{
start: 2,
end: 3
},{
start: 4,
end: 5
},{
start: 6,
end: 7
},
]
I would like that (or have a diff Array) to output like the below:
var newTimes = [
{
start: 1,
end: 3
},{
start: 4,
end: 5
},{
start: 6,
end: 7
},
]
It gets trickier if there are 3 times in a row.
var threeTime = [
{
start: 1,
end: 2
},{
start: 2,
end: 3
},{
start: 3,
end: 5
},{
start: 6,
end: 7
},
]
The above should turn into:
var newThreeTimes = [
{
start: 1,
end: 5
},{
start: 6,
end: 7
},
]
The original array of times will always be sorted from oldest (smallest start time) to newest (largest start time). The output does not need to be in any specific order. All time objects will be moment times.
Can someone help me solve this?
This is the code i have come up with
function mergeArr(arr) {
// Sort the array in descending order
arr.sort(function(a, b) {
return b.start - a.start;
});
// Traverse from the top as you will need to remove the elements
// Merge the elements based on start of one and end of the previous
for (var i = arr.length - 1; i > 0; i--) {
if (arr[i].end == arr[i - 1].start) {
arr[i].end = arr[i - 1].end;
arr.splice(i - 1, 1);
}
}
// Sort it again in reverse order.
return arr.sort(function(a, b) {
return a.start - b.start;
});
}
Comments are making the code self explanatory.
Working Fiddle
I wrote a script to render a timeline with a dataset in javascript.
But i have to mark the area's where no info is found (yellow in the image).
I absolutely have no idea how to get the ranges without info in the dataset.
(i use momentjs for date calculations)
Example dataset:
data: [
{id: 1, lane: 1, start: 05-02-2006, end: 09-09-2008},
{id: 2, lane: 2, start: 01-01-2008, end: 31-07-2010},
{id: 3, lane: 3, start: 15-12-2013, end: 12-02-2016}
]
Example image:
I would create an new array and fill in the voids, something like this
var dates = [];
for (var i=0; i<data.length; i++) {
var now = data[i],
thisStart = parseDate(now.start),
thisEnd = parseDate(now.end),
prevEnd = data[i-1] ? parseDate(data[i-1].end) : null;
if ( prevEnd && prevEnd <= thisStart ) {
dates.push({id: i + 0.5, lane: 0, start: prevEnd, end: thisStart});
}
dates.push({id : now.id, lane: now.lane, start: thisStart, end: thisEnd});
}
and you'll end up with an array like this
dates : [
{id: 1, lane: 1, start: "2006-02-04T23:00:00.000Z", end: "2008-09-08T22:00:00.000Z"},
{id: 2, lane: 2, start: "2007-12-31T23:00:00.000Z", end: "2010-07-30T22:00:00.000Z"},
{id: 2.5, lane: 0, start: "2010-07-30T22:00:00.000Z", end: "2013-12-14T23:00:00.000Z"},
{id: 3, lane: 3, start: "2013-12-14T23:00:00.000Z", end: "2016-02-11T23:00:00.000Z"}
]
Note that I marked the object that fills the voids with an id if .5 and lane 0, you could do whatever you wanted there, as long as it's recognizable to you when you create the layout.
I also created a parseDate function, all it does is parse the dates for you to valid date objects, it's included in the fiddle below
FIDDLE