I have an array with the following values (example):
[
1491408000000,
1491494400000,
1491753600000,
1493222400000,
1493308800000,
1493568000000
]
Where the index is a date time. The date time will always be at 12:00:00 on a date.
In this example, the first 3 dates are consecutive cross weekend (weekend is holiday so count as leave), then another group of 3 dates cross weekend and month.
Now, what I am trying to do is find sequential dates (cross week and month) and put them into an array as follows:
[
1491408000000,
1491494400000,
1491753600000
],
[
1493222400000,
1493308800000,
1493568000000
]
I have tried the following code to get the sequential dates but this cannot cross week and month, how to modify the code to get above result? Any help would be much appreciated!
var timeValue = new Date(dateReview).getTime();
valueCon.push(timeValue);
var k = 0;
sortedValue[k] = [];
valueCon.sort( function ( a, b ){
return +a > +b ? 1 : +a == +b ? 0: -1;
})
.forEach( function( v , i ){
var a = v,b = valueCon[i+1]||0;
sortedValue[k].push( +a );
if ( (+b - +a) > 86400000) {
sortedValue[++k] = []
}
return 1;
});
sortedValue.sort( function ( a,b ){
return a.length > b.length ? -1: 1;
});
This requires help from a function to test if two dates are in the same week. The following goes over the set of time values provided in an array and puts the first value into an array within the array. For each subsequent value, it tests if it's in the same week as the first value in each array within the outer array.
If it's in the same week as the first value in any existing array, it's pushed into that array. Otherwise, it's put in a new array and pushed into the outer array.
There may be a neater way to implement the algorithm, but I'll leave that for others.
Due to time zone differences, they are adjusted to the host time zone based on the original time values representing noon in the source time zone.
// Given 2 dates, return true if they are in the same week (Mon to Sun).
// Otherwise, return false
function sameWeek(a, b){
var e = new Date(+a);
// Week starts at 00:00:00.000 on Monday on or before date
var s = new Date(e.setDate(e.getDate() - ((e.getDay()||7) -1)));
s.setHours(0,0,0,0);
// Week ends at 23:59:59.999 the following Sunday
e.setDate(e.getDate() + 6);
e.setHours(23,59,59,999);
// Test b and return value
return b >= s && b <= e;
}
// Given time value for UTC-0400, adjust to same date and time
// in local time zone and return a date
function adjust(n) {
var d = new Date(n);
d.setMinutes(d.getMinutes() - 240 + d.getTimezoneOffset());
return d;
}
var result = [1491408000000,1491494400000,1491753600000,1493222400000,1493308800000,1493568000000
].reduce(function(acc, n) {
var d = adjust(n);
var used;
if (acc.length != 0) {
used = acc.some(function(arr) {
if (sameWeek(adjust(arr[0]), d)) {
arr.push(n);
return true;
}
});
}
if (!used || acc.length == 0) {
acc.push([n]);
}
return acc;
},[]);
// Result array
console.log(result);
// Printed as date strings adjusted to same host local time
result.forEach(arr => {
arr.forEach(n => console.log(adjust(n).toString()))
console.log('\n');
});
Manipulation of timestamps is a pain. JavaScript has a built-in Date type, as you know, and I would suggest you use it. Date#getUTCDay returns the day of the week as an integer (for reference, 4 is Friday, or the day before a weekend), while Date#setUTCDate and Date#getUTCDate together allow you to adjust the date in day increments (and have it overflow/underflow to the next/previous month). Thus, to determine whether a timestamp b follows "sequentially" (excluding weekends) after a, you can use:
function sequential (a, b) {
a = new Date(a)
return a.setUTCDate(a.getUTCDate() + (a.getUTCDay() === 4 ? 3 : 1)) === b
}
Grouping is just an exercise after that; the code above contains all of the real logic behind this solution.
Example Snippet
var dates = [
1491408000000,
1491494400000,
1491753600000,
1493222400000,
1493308800000,
1493568000000
]
function sequential (a, b) {
a = new Date(a)
return a.setUTCDate(a.getUTCDate() + (a.getUTCDay() === 4 ? 3 : 1)) === b
}
function groupSequential(dates) {
if (dates.length < 2) return [dates.slice()]
dates.sort(function(a, b) { return a - b })
var result = [], group
for (var i = 0; i < dates.length; i++) {
sequential(dates[i - 1], dates[i]) || result.push(group = [])
group.push(dates[i])
}
return result
}
console.log(groupSequential(dates))
Related
For the following Month/Day/Year datestring array...
const array1 = ["05/31/2022", "06/01/2022", "06/02/2022"]
...I am attempting to configure the array to slice and remove all datestring array items (starting with 01 as Day) if they follow after datestring array items with 31 as Day. Same goes for instances of Day 30 followed by Day 01.
To handle this, I set up a for statement to loop through all of the strings in the array. I then used a split method to remove "/" from each array item, thus breaking MM,DD,YYYY into separate variables.
for (let i = 0; i < array1.length; i++) {
var [month, day, year] = array1[i].split('/');
console.log(month, day, year)
}
My intention for the next step is to set up a conditional that checks if an array item that includes 30 or 31 as "day" is followed by an array item that includes 01 as "day", then use slice to remove subsequent dates faster 30th or 31st. For this part, I attempted to re-consolidate month, day and year into individual array items, like so:
const newArray = []
for (let i = 0; i < array1.length; i++) {
var [month, day, year] = array1[i].split('/');
newArray.push(month + day + year)
console.log(newArray)
}
with output:
['05312022', '06012022', '06022022']
However, I'm not sure how to set up a conditional that checks if an array item that includes 30 or 31 as "day" is followed by an array item that includes 01 as "day". How can I go about the functionality for such a check?
You can loop over the dates array and grab the currDay and prevDay and if the condition is satisfied, slice the dates array and return it.
const solution = (dates) => {
for (let i = 1; i < dates.length; i++) {
const currDay = dates[i].slice(3, 5);
const prevDay = dates[i - 1].slice(3, 5);
if ((currDay === "01") & (prevDay === "31" || prevDay === "30")) {
return dates.slice(0, i);
}
}
return dates;
};
console.log(solution(["05/31/2022", "06/01/2022", "06/02/2022"]));
The following us es Array#reduce to
retain all elements unless
the number of elements retained equals the index being considered and the current element has date 01 and follows a 30 or a 31
if index in consideration is greater than the number of items retained, meaning at least one element has been skipped, then the current element is skipped as well.
const array1 = ["05/31/2022", "06/01/2022", "06/02/2022"],
output = array1.reduce(
(prev,cur,i) =>
prev.length && ["30","31"].includes( prev.slice(-1)[0].slice(3,5) ) &&
(prev.length === i && cur.slice(3,5) === "01" || i > prev.length) ?
prev :
[...prev, cur],
[]
);
console.log( output );
I have bunch of electricity meter readings which have irregular dates. See below :
ReadingDate Meter
19/01/2021 5270
06/03/2021 5915
11/05/2021 6792
08/07/2021 7367
9/9/2021 8095
8/11/2021 8849
02/12/2021 9065
17/01/2022 9950
Now I'd like to transform this into monthly readings, using just this data, to end up with a table like this
Month Usage
2021-01 452
2021-02 393
2021-03 416
2021-04 399
2021-05 341
2021-06 297
2021-07 347
2021-08 358
2021-09 369
2021-10 389
2021-11 295
2021-12 586
2022-01 308
Now, I have a working solution, but I'm sure there's a more beautiful concise way of doing it.
What I do is to create an intermediate array that has one line for each date between first and last meter readings.
Each item in the array has 3 values :
the date
the average value for that date (calculated by counting the days between meter readings and dividing that by change in the meter.
the corresponding month
The last step then is to loop over this intermediate array and sum the values for each different month.
Here's the working code (its taken from Google Apps Script so please ignore the spreadsheet specific stuff:
var DailyAveragesArray = [['Date','Usage','Month']];
var monthlyObject = {};
var monthlyArray = [['Month','Usage']];
function calculateAverageDailyFigures() {
// give indices for the useful columns, 0 numbered
var ReadingDateColumn = 0;
var MeterReading = 1;
// Read into an array
var MeterReadingData = ss.getDataRange().getValues() // Get array of values
const sortedReadings = MeterReadingData.slice(1).sort((a, b) => a[0] - b[0]);
// from https://flaviocopes.com/how-to-sort-array-by-date-javascript/
// First calculate the number of days and average daily figure for each row
// Note we don't do this for the last row
for(i=0; i < sortedReadings.length - 1 ; i++){
var NumberOfDays = (sortedReadings[i+1][0] - sortedReadings[i][0])/(1000*3600*24);
sortedReadings[i].push(NumberOfDays);
var MeterDifference = sortedReadings[i+1][1] - sortedReadings[i][1];
var AverageDailyFigure = MeterDifference/NumberOfDays;
sortedReadings[i].push(AverageDailyFigure);
}
BuildDailyArray(sortedReadings);
}
function BuildDailyArray(sortedReadings){
// For each row in sorted , loop from the date to the next date-1 and create columns date and Usage
for(i=0; i<sortedReadings.length -1 ;i++){
for (var d = sortedReadings[i][0]; d < sortedReadings[i+1][0]; d.setDate(d.getDate() + 1)) {
var newDate = new Date(d);
var month = newDate.getFullYear() + '-' + ('0' + (newDate.getMonth() + 1)).slice(-2);
DailyAveragesArray.push([newDate,sortedReadings[i][3],month]);
// Check if the month is in the object and add value, otherwise create object an add value
if(month in monthlyObject){
monthlyObject[month] = monthlyObject[month] + sortedReadings[i][3];
} else {
Logger.log('Didnt find month so create it');
monthlyObject[month] = sortedReadings[i][3];
}
}
}
Logger.log(DailyAveragesArray.length);
Logger.log(monthlyObject);
var DailyUsageData = ss.getRange('D1:F'+DailyAveragesArray.length);
DailyUsageData.setValues(DailyAveragesArray);
BuildMonthlyArray();
}
function BuildMonthlyArray(){
const keys = Object.keys(monthlyObject);
Logger.log(keys);
keys.forEach((key, index) => {
monthlyArray.push([key,Math.round(monthlyObject[key])]);
});
var MonthlyUsageData = ss.getRange('H1:I'+monthlyArray.length);
MonthlyUsageData.setValues(monthlyArray);
}
So, my question is, how would I do this nicer, more beautifully, not so verbose ?
I'm not sure what the correct term is for what I want to do. I don't think it's resampling .
I'd appreciate any comments.
Thanks / Colm
Here is my shot on this.
The way i'm doing it:
Initializing all days and its value
Grouping by month
Calculating the average per month
Explanation a bit more precise
initDateFromString
The method initDateFromString takes a dates with the format DD/MM/YYYY and return the associated js date object
initAllDates
The method initAllDates will split the data into day and add the average value of the difference for each day
for example, for the first two readings, it will result to an array of dates looking like :
date
value
19/01/2021
14.02
20/01/2021
14.02
....
....
05/03/2021
14.02
06/03/2021
14.02
The value 14.02 comme from the following calcul :
(newReadingMeter - oldReadingMeter)/nbDaysBetweenDates
Which in this example is (5915 - 5270)/46 = 14.02
joinToMonth
The joinToMonth method will then group the days into month with all the days value summed !
const data = [{
ReadingDate: '19/01/2021',
Meter: 5270
},
{
ReadingDate: '06/03/2021',
Meter: 5915
},
{
ReadingDate: '11/05/2021',
Meter: 6792
},
{
ReadingDate: '08/07/2021',
Meter: 7367
},
{
ReadingDate: '9/9/2021',
Meter: 8095
},
{
ReadingDate: '8/11/2021',
Meter: 8849
},
{
ReadingDate: '02/12/2021',
Meter: 9065
},
{
ReadingDate: '17/01/2022',
Meter: 9950
}
]
function initDateFromString(dateString){
let dateParts = dateString.split("/");
return new Date(+dateParts[2], dateParts[1] - 1, +dateParts[0]);
}
function initAllDates(data){
let dates = []
let currentValue = data.shift()
const currentDate = initDateFromString(currentValue.ReadingDate)
data.forEach(metric => {
const date = initDateFromString(metric.ReadingDate)
const newDates = []
while(currentDate < date){
newDates.push({date: new Date(currentDate)})
currentDate.setDate(currentDate.getDate() + 1)
}
dates = dates.concat(newDates.map(x => {
return {Usage: (metric.Meter - currentValue.Meter) / newDates.length, date: x.date}}
))
currentDate.setDate(date.getDate())
currentValue = metric
})
return dates
}
function joinToMonth(dates){
return dates.reduce((months, day) => {
const month = day.date.getMonth()
const year = day.date.getFullYear()
const existingObject = months.find(x => x.month === month && x.year === year)
if (existingObject) {
existingObject.total += day.Usage
} else {
months.push({
month: day.date.getMonth(),
year: day.date.getFullYear(),
total: day.Usage,
})
}
return months;
}, []);
}
const dates = initAllDates(data)
const joinedData = joinToMonth(dates)
console.log(joinedData)
I have 2 different times:
var shiftStartTime = "05:48";
var shiftEndTime = "14:29";
And i have another time which is selectedDate ="06:20"(this will change according datetimepicker selection), and i want to check if selectedDate should be between (shiftStartTime and shiftEndTime ).
Can anyone help in this?
Updated Code:
i have 6 different timespan like below
var shift1StartTime = "05:48";
var shift1EndTime = "14:18";
var shift2StartTime = "14:30";
var shift2EndTime = "22:29";
va
r shift3StartTime = "22:30";
var shift3EndTime = "05:47";
using all 6 timespan i want to check the if the given time is between (shift1StartTime and shift1EndTime) return shift1
Or
if the given time is between (shift2StartTime and shift2EndTime) return shift2
Or
if the given time is between (shift3StartTime and shift3EndTime) return shift3
Simply compare the strings like
var shiftStartTime = "05:48"; var shiftEndTime = "14:29";
shiftStartTime > shiftEndTime // false
Here is some JS that does this, although better formatted time would make it a lot easier
function findTotalTime(time) {
hours = parseInt(time.substring(0,2))
mins = parseInt(time.substring(3,5))
return (hours*60) + mins
}
startTime = findTotalTime(shiftStartTime)
endTime = findTotalTime(shiftEndTime)
selectedTime = findTotalTime(selectedDate)
if (selectedTime > startTime && selectedTime < endTime) {
// time is inbetween shifts
}
const date = new Date();
const shiftStartTime = '05:48';
const shiftEndTime = '14:29';
const selectedDate = '14:20';
const start = date.setHours(+shiftStartTime.split(':')[0], +shiftStartTime.split(':')[1], 0, 0);
const end = date.setHours(+shiftEndTime.split(':')[0], +shiftEndTime.split(':')[1], 0, 0);
const selected = date.setHours(+selectedDate.split(':')[0], +selectedDate.split(':')[1], 0, 0);
if (start < selected && selected < end) {
console.log(true);
} else {
console.log(false);
}
Alright, so you got three relative times as strings in the format HH:mm. I'm assuming that your times are given as 24h strings / military time, because otherwise, you'd need an A.M. / P.M. specifier.
It is always useful to have the data you are working with in a well-suited machine-readable format, so you could parse them into a simple object holding the hour and minute as numbers.
A function doing this could look like this.
function parseTimeStr(time) {
// The pattern of your input,
// allows spaces around the `:`
// and single-digit inputs like `8:00`
let re = /([0-9][0-9]?)\s*:\s*([0-9][0-9]?)/;
let result = re.exec(time.trim());
if (result === null) {
throw "No match"
}
let hour = parseInt(result[1], 10);
let minute = parseInt(result[2], 10);
/* handle out of range values here */
return { hour, minute };
}
Alright, so you have these objects now. How do you compare them? There's a pattern for that: Have a function returning whether the first argument is greater (1), equal (0), or less (-1) than the second.
Writing this is simple now that the time is an object:
function cmpDate(date1, date2) {
if (date1.hour > date2.hour) {
return 1;
} else if (date1.hour < date2.hour) {
return -1;
} else if (date1.minute > date2.minute) {
return 1;
} else if (date1.minute < date2.minute) {
return -1;
} else {
return 0;
}
}
Alright, now we can have a helper function checking if the first argument is in the closed interval defined by the last two arguments:
function isInShift(time, shiftStart, shiftEnd) {
// time is greater or equal shiftStart
// and less or equal shiftEnd
return cmpDate(time, shiftStart) !== -1 && cmpDate(time, shiftEnd) !== 1;
}
You can then finally make your comparison by calling isInShift(parseTimeStr(selectedTime), parseTimeStr(shiftStartTime), parseTimeStr(shiftEndTime)). This will return a boolean. You can easily extend this infrastructure for multiple shifts.
Be aware that both reality and your users can be more ... screwy than you'd expect.
The above code does not do error handling for invalid time inputs, neither does it account for overnight shifts, but these are details that you can easily work out, you just have to put some effort into thinking of them.
I have an array of date ranges(selectedRanges) which shows assigned dates for a member between the main date range. I want to know the date ranges where he/she is unassigned. Please refer to the below example.
mainDateRange = ['01-01-2020', '14-06-2020'];
selectedRanges = [
['03-01-2020','04-01-2020'],
['03-01-2020','05-01-2020'], //overlapping dates
['11-01-2020','13-01-2020'],
['01-02-2020','20-02-2020'],
['15-03-2020','18-03-2020'],
['06-01-2020','06-01-2020'], //date ranges will not be ordered
['03-01-2020','04-01-2020']
]; //dates that the member has work assigned
Desired output
excludedRanges = [
['01-01-2020','02-01-2020'],
['07-01-2020','10-01-2020'],
['14-01-2020','31-01-2020'],
['21-02-2020','14-03-2020'],
['19-03-2020','14-06-2020']
]; //shows all the unassigned periods(ranges)
selectedRanges date ranges will have ranges in random order and also may have duplicate and overlapping dates.
I have searched a lot and found nothing. I am only able to get the unselected dates, not as a range. Please help.
Thank you
Interesting problem, I'll propose an approach to achieve this desired behavior by doing the following:
Transform all string dates into date objects.
Sort the selectedRanges array in ascending order using the start and end dates. This sorting step is cricual to finding the date range gaps.
Adding a "moving cursor" date that moves between the mainDateRange to find and add the missing ranges to the output array.
Before we start the date calculations, we'll need a few helper functions. I've added two functions to go back and forth between the date object and the string format you have (dd-mm-yyyy). Please note that you may not need these two helper function if you use something like Moment.js, but I won't impose an extra dependency on your project.
function stringToDate(stringDate) {
const parts = stringDate.split('-').map((p) => parseInt(p));
parts[1] -= 1;
return new Date(...parts.reverse());
}
function dateToString(date) {
return `${('0' + date.getDate()).slice(-2)}-${('0' + (date.getMonth() + 1)).slice(-2)}-${date.getFullYear()}`;
}
I've also added a sorter function that makes sure the ranges are sorted in an ascending fashion (smaller ranges first).
function dateRangeSorter(a, b) {
if (a[0] < b[0]) return -1;
else if (a[0] > b[0]) return 1;
if (a[1] < b[1]) return -1;
else if (a[1] > b[1]) return 1;
return 0;
}
Now we're good to go on the calculation, here is a code snippet that will log the output at the end.
// data
const output = [];
const oneDayInMs = 24 * 60 * 60 * 1000;
const mainDateRange = ['01-01-2020', '14-06-2020'];
const selectedRanges = [
['03-01-2020','04-01-2020'],
['03-01-2020','05-01-2020'],
['11-01-2020','13-01-2020'],
['01-02-2020','20-02-2020'],
['15-03-2020','18-03-2020'],
['06-01-2020','06-01-2020'],
['03-01-2020','04-01-2020']
];
// helpers
function stringToDate(stringDate) {
const parts = stringDate.split('-').map((p) => parseInt(p));
parts[1] -= 1;
return new Date(...parts.reverse());
}
function dateToString(date) {
return `${('0' + date.getDate()).slice(-2)}-${('0' + (date.getMonth() + 1)).slice(-2)}-${date.getFullYear()}`;
}
function dateRangeSorter(a, b) {
if (a[0] < b[0]) return -1;
else if (a[0] > b[0]) return 1;
if (a[1] < b[1]) return -1;
else if (a[1] > b[1]) return 1;
return 0;
}
// transform into date and sort
const mainDateRangeAsDates = mainDateRange.map(stringToDate);
const selectedRangesAsDates = selectedRanges.map((range) => (range.map(stringToDate)))
.sort(dateRangeSorter);
// start at the beginning of the main date range
let movingDate = mainDateRangeAsDates[0];
// loop through the selected ranges
selectedRangesAsDates.forEach(([startDate, endDate]) => {
// if there's a gap, add it to the output
if (movingDate < startDate) {
output.push([
dateToString(movingDate),
dateToString(new Date(startDate.getTime() - oneDayInMs))
]);
}
// move the cursor date to one day after the end of current rage
movingDate = new Date(endDate.getTime() + oneDayInMs);
});
// if there is a gap at the end, add it as well
if (movingDate < mainDateRangeAsDates[1]) {
output.push([
dateToString(movingDate),
dateToString(mainDateRangeAsDates[1])
]);
}
console.log(output);
Used a similar approach to this: How to make sure every number of a bigger range is within some smaller ranges?
Convert all strings to Dates. Sorts by minimum of range.
Moves minimum position forward, until it finds a gap, and pushes to res array.
Pushes range from last minimum to maximum if it exists
mainDateRange = ['01-01-2020', '14-06-2020'];
selectedRanges = [
['03-01-2020', '04-01-2020'],
['03-01-2020', '05-01-2020'], //overlapping dates
['11-01-2020', '13-01-2020'],
['01-02-2020', '20-02-2020'],
['15-03-2020', '18-03-2020'],
['06-01-2020', '06-01-2020'], //date ranges will not be ordered
['03-01-2020', '04-01-2020']
]; //dates that the member has work assigned
function gapFinder(mainDateRange, selectedRanges) {
const dateToInt = a => new Date(a.split('-').reverse().join('-'))
const intToDate = a => new Date(a).toISOString().slice(0, 10).split('-').reverse().join('-')
// convert to numbers
selectedRanges = selectedRanges.map(r => r.map(dateToInt))
// presort ranges
selectedRanges.sort(([a, ], [b, ]) => a - b)
let [min, max] = mainDateRange.map(dateToInt)
const res = []
for (const [x, y] of selectedRanges) {
if (min > max) break
if (min < x)
res.push([min, x.setDate(x.getDate() - 1)])
min = Math.max(min, y.setDate(y.getDate() + 1))
}
if (min <= max) res.push([min, max])
return res.map(r => r.map(intToDate))
}
console.log(JSON.stringify(gapFinder(mainDateRange,selectedRanges)))
selectedRanges.push(['11-06-2020', '13-06-2020'])
console.log(JSON.stringify(gapFinder(mainDateRange,selectedRanges)))
I am writing a function to limit search results from an array of objects. It seems to be working mostly... but still returns the unwanted objects.
Here is my code/explanation so far - Can you help me figure out why it still returns the unwanted objects?
I start by filtering my array, tourBands, to include only bands that have certain event types. This works fine.
The second .filter() is where I am having issues. This is filtering the bands that are in town within 3 days before the selected performanceDate. The problem is even though the console.log() works at the end of the second .filter(), it still returns the band even when it shouldnt.
The code below has comments to explain whats going on.
{
bandTypes === "touring"
? tourBands
.filter(band => {
if(showType !== 'Show Type'){
return band.showTypes.includes(showType)
}else {
return band
}
})
.filter(band => {
//User Clicks Button to Search within 3 days of the tourDate. This sets filterTourBandsByDate to true.
if(filterTourBandsByDate){
//Each Band may have more than 1 tour date.. so I now start filtering through all the tour dates. This Works.
band.bandTour.filter(tourDate => {
//Now I check to see if this tour date is within 100 miles of the savedLocation, which is their search location. This works.
let distanceToSearch = turf.distance(tourDate.geometry.coordinates, savedLocation, {units: 'miles'})
if(distanceToSearch <= 100){
//If the specific tour location is within 100 miles of the saved location, I need to make sure that the tour location being filtered is actuall upcoming and not in the past.
let year = tourDate.geometry.tourDate.slice(0,4)
let month = tourDate.geometry.tourDate.slice(5, 7)
let day = tourDate.geometry.tourDate.slice(8)
let tourDateFormatted = new Date(year, month - 1, day)
//Check to make sure the tour date is upcoming - This works.
if(new Date() < tourDateFormatted ){
//Now I am going to change the tour date to 3 days before the actual tour date. Then i'll check to see if the performanceDate (The users entered date) is past the new date.
tourDateFormatted.setDate(tourDateFormatted.getDate() - 3);
if(performanceDate > tourDateFormatted ){
console.log(tourDate)
//Returns Correct Tour Date in the correct location. If I select a date that shouldnt return the band, the console.log does NOT fire, but it still returns the band.
return band
}
}
}
})
}else{
return band
}
})
.reduce(
(allPosts, band) =>
allPosts.concat(
(band.youtube.length > 0 &&
band.bandBio !== "n/a" &&
band.bandGenre !== "n/a")
? band.posts.map((post) => ({ post, band }))
: []
),
[]
)
.sort((a, b) => new Date(b.post.date) - new Date(a.post.date))
.slice(0, page * 10)
.map(({ post, band }) => convertPost(post, band))
: null
}
In your second .filter function, you're not always returning a value. You are doing a search operation with a band.bandTour.filter(...) call, but the value you return from that is not going to bubble out and be the result of the second filter function.
You should be able to replace that third nested filter with a loop. Alternately, you can replace that third .filter(...) call with a .some(...) (since you're trying to find if one value matches).
Loop version:
.filter(band => {
if(filterTourBandsByDate){
for (const tourDate of band.bandTour) {
let distanceToSearch = turf.distance(tourDate.geometry.coordinates, savedLocation, {units: 'miles'})
if(distanceToSearch <= 100){
let year = tourDate.geometry.tourDate.slice(0,4)
let month = tourDate.geometry.tourDate.slice(5, 7)
let day = tourDate.geometry.tourDate.slice(8)
let tourDateFormatted = new Date(year, month - 1, day)
if(new Date() < tourDateFormatted ){
tourDateFormatted.setDate(tourDateFormatted.getDate() - 3);
return (performanceDate > tourDateFormatted);
}
}
}
} else {
return true
}
})
Function chaining version:
.filter(band => !filterTourBandsByDate || band.bandTour.some(tourDate => {
let distanceToSearch = turf.distance(tourDate.geometry.coordinates, savedLocation, {units: 'miles'})
if(distanceToSearch <= 100){
let year = tourDate.geometry.tourDate.slice(0,4)
let month = tourDate.geometry.tourDate.slice(5, 7)
let day = tourDate.geometry.tourDate.slice(8)
let tourDateFormatted = new Date(year, month - 1, day)
if(new Date() < tourDateFormatted ){
tourDateFormatted.setDate(tourDateFormatted.getDate() - 3);
return performanceDate > tourDateFormatted;
}
}
})