date-fns | format date - javascript

Problem
I have below function that formats date string.
import { format, parseISO } from "date-fns";
export function convertDate(myDate, displayFormat) {
return format(new Date(parseISO(myDate)), displayFormat);
}
I have articles that has content such as
title: 'My title'
date: '2022-01-04'
I call the convertDate function using below:
if (articles) {
for (let i = 0; i < articles.length; i++) {
const year = convertDate(articles[i].date, "y");
years.push(year);
}
uniqueYear = [...new Set(years)];
}
My timezone is CEST.
Error
I am getting error:
Expected result:
I can call the function by using {convertDate(article.date, "PPP")} which also works.
Please help!

Running the following minimal example at runkit.com returns "2022", it doesn't throw the error described in the OP:
var dateFns = require("date-fns")
function convertDate(myDate, displayFormat) {
return dateFns.format(new Date(dateFns.parseISO(myDate)), displayFormat);
}
let articles = [{
title: 'My title',
date: '2022-01-04'
}];
convertDate(articles[0].date, "y"); // "2022"
So the error is elsewhere.
Also, the use of new Date is redundant:
dateFns.format(dateFns.parseISO(myDate), displayFormat)
is sufficient and more robust.
As suggested elsewhere, getting the year from the timestamp can be done using string manipulation, no need for casting to a Date. To get the years:
let articles = [{title: 'title 0', date: '2022-01-04'},
{title: 'title 1', date: '2020-01-04'}];
let years = articles.map(o => o.date.substring(0,4));
console.log(years);
If you need it as a Date for other things (e.g. formatted month name), cast it to a Date once and reuse it.

Related

How to append a data item to a deep nested array while keeping the original response data structure immutable?

I'm creating data per day and I'm dealing with following response data ...
{
tipster: {
name: "Gallita FC",
description: "TEST",
picks: [{
date: "Friday, February 18th 2022",
data: [{
title: "yesterday",
description: "TEST",
date: "Friday, February 18th 2022",
category: "NHL",
pickImageUrl: "https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png",
}],
}, {
date: "Saturday, February 19th 2022",
data: [{
title: "today",
description: "TEST",
date: "Saturday, February 19th 2022",
category: "NHL",
pickImageUrl: "https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png",
}],
}],
imageUrl: "https://res.cloudinary.com/sports-master/image/upload/v1644649610/27ADF778-454B-4DB7-88B7-DC98202E2736_utb7xw.png",
bannerUrl: "https://scontent.fmex34-1.fna.fbcdn.net/v/t1.6435-9/167022015_1317341031983063_7337313589197318410_n.jpg?_nc_cat=111&ccb=1-5&_nc_sid=a26aad&_nc_ohc=5ctqP2nFf7IAX94PNSO&_nc_ht=scontent.fmex34-1.fna&oh=00_AT_TzRHhhV73ji7wzW2X1u27TOU8TNlObwtp0ILc0DzC1Q&oe=62207F2C",
id: "62075e5a13a43ace611fe5bd",
},
}
Within the tipster.picks array I need to append an additional data item to the last matching data item. A match could be where data.title equals "today".
The code I came up with so far does not lead to the correct result ...
const newPick = {
title,
description,
date,
category,
pickImageUrl,
};
const tipsterUpdate = {
...req.body,
picks: [...tipster.picks, tipster.picks.slice(-(1)[0], newPick)],
};
I'm using spread operator because I need to maintain the old data and only add a new object on the data array.
I really appreciate a little help here.
Thank you.
Destructure out the picks array from everything else in the tipster object, then build a new tipster object containing an updated picks array.
const data={tipster:{name:"Gallita FC",description:"TEST",picks:[{date:"Friday, February 18th 2022",data:[{title:"yesterday",description:"TEST",date:"Friday, February 18th 2022",category:"NHL",pickImageUrl:"https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png"}]},{date:"Saturday, February 19th 2022",data:[{title:"today",description:"TEST",date:"Saturday, February 19th 2022",category:"NHL",pickImageUrl:"https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png"}]}],imageUrl:"https://res.cloudinary.com/sports-master/image/upload/v1644649610/27ADF778-454B-4DB7-88B7-DC98202E2736_utb7xw.png",bannerUrl:"https://scontent.fmex34-1.fna.fbcdn.net/v/t1.6435-9/167022015_1317341031983063_7337313589197318410_n.jpg?_nc_cat=111&ccb=1-5&_nc_sid=a26aad&_nc_ohc=5ctqP2nFf7IAX94PNSO&_nc_ht=scontent.fmex34-1.fna&oh=00_AT_TzRHhhV73ji7wzW2X1u27TOU8TNlObwtp0ILc0DzC1Q&oe=62207F2C",id:"62075e5a13a43ace611fe5bd"}};
const newPick = {
title: 'Bob',
description: 'Bob does it again',
date: new Date(),
category: 'Bob',
pickImageUrl: 'bobImage',
};
// Accept data, the new pick, and a search
// (in this case "today")
function addNewPick(data, newPick, search) {
// Grab the picks, and then everything else
// from the tipster object
const { tipster: { picks, ...rest } } = data;
// `find` the index of the array containing the search text
const index = picks.findIndex(pick => {
return pick.data.some(obj => {
return obj.title === search;
});
});
// Add the new pick to the "today" array
picks[index].data.push(newPick);
// Return a new tipster object with
// the updated picks
return {
tipster: { ...rest, picks }
};
}
const out = addNewPick(data, newPick, 'today');
console.log(out);
quoting the OP
I'm using spread operator because I need to mantaint the old data and only add a new object on the data array.
Since spread syntax creates a shallow copy only, thus any nested level of the copy is still a reference and therefore in danger of being mutated, I suggest a one time deep clone via structuredClone (there are polyfills for environments which do not yet support this Web-Api method).
And as for a generic approach, which inserts a new data item after (either) the last data with a matching condition (or even after every condition matching data item), one needs a function which gets provided
the tipster.picks reference of the deeply cloned response data object,
the to be inserted new data item,
a callback function which implements the condition of a matching data item.
Within a first step one would collect a list of all data items where the condition does match. The second step is the insert task which can be adapted to maybe changing requirements ...
function insertDataItemAfterLastMatchingCondition(picks, item, condition) {
// collect a list of all data items where `condition` matches.
const matchList = picks
.reduce((matches, pickItem) => {
const { data } = pickItem;
const index = data.findIndex(condition);
if (index >= 0) {
matches.push({ array: data, index });
}
return matches;
}, []);
// insert new item excusivley after the last matching data item.
const { array, index } = matchList.at(-1) ?? {};
if (Array.isArray(array)) {
array.splice((index + 1), 0, item);
}
// // insert new item (copy) after every matching data item.
//
// matchList.forEach(({ array, index }) =>
// array.splice((index + 1), 0, {...item})
// );
}
const responseData = {tipster:{name:"Gallita FC",description:"TEST",picks:[{date:"Friday, February 18th 2022",data:[{title:"yesterday",description:"TEST",date:"Friday, February 18th 2022",category:"NHL",pickImageUrl:"https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png"}]},{date:"Saturday, February 19th 2022",data:[{title:"today",description:"TEST",date:"Saturday, February 19th 2022",category:"NHL",pickImageUrl:"https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png"}]}],imageUrl:"https://res.cloudinary.com/sports-master/image/upload/v1644649610/27ADF778-454B-4DB7-88B7-DC98202E2736_utb7xw.png",bannerUrl:"https://scontent.fmex34-1.fna.fbcdn.net/v/t1.6435-9/167022015_1317341031983063_7337313589197318410_n.jpg?_nc_cat=111&ccb=1-5&_nc_sid=a26aad&_nc_ohc=5ctqP2nFf7IAX94PNSO&_nc_ht=scontent.fmex34-1.fna&oh=00_AT_TzRHhhV73ji7wzW2X1u27TOU8TNlObwtp0ILc0DzC1Q&oe=62207F2C",id:"62075e5a13a43ace611fe5bd"}};
const responseClone = (typeof structuredClone === 'function')
&& structuredClone(responseData)
|| JSON.parse(JSON.stringify(responseData)); // fallback
const newData = {
title: 'tomoorow',
description: 'TEST',
date: 'Sunday, February 20th 2022',
category: 'NHL',
pickImageUrl: 'https://res.cloudinary.com/creaciones-inteligentes-roy/image/upload/v1644455039/Captura_de_Pantalla_2022-02-09_a_la_s_18.59.43_voy1pj.png',
};
insertDataItemAfterLastMatchingCondition(
responseClone.tipster.picks,
newData,
data => data.title === 'today',
);
console.log({ responseData, responseClone });
.as-console-wrapper { min-height: 100%!important; top: 0; }

Can't read update and save to object csv data typescript/js

It is a typescript
Can anybody help with the followin:
I read data from CSV file
Transform this data on flight (remove some extra columns)
Then I want updated csv in stream get back to variable in the code.
Console.log(updatedCsv) // in stream - displays what I need
BUT!
When I try to push it into array nothing happens and then variable (in which I pushed data from stream) is considered undefined:
import * as fs from "fs";
import * as csv from "csv";
udateCsv(){
fs.createReadStream('allure-report/data/suites.csv')
.pipe(csv.parse({ delimiter: ',', columns: true }))
.pipe(csv.transform((input) => {
console.log(input) // <----- it shows in console data I needed
/* like this:
{
Status: 'passed',
'Start Time': 'Wed Nov 11 17:37:33 EET 2020',
'Stop Time': 'Wed Nov 11 17:37:33 EET 2020',
'Duration in ms': '1',
'Parent Suite': '',
Suite: 'The Internet Guinea Pig Website: As a user, I can log into the secure area',
'Sub Suite': '',
'Test Class': 'The Internet Guinea Pig Website: As a user, I can log into the secure area',
'Test Method': 'Hook',
Name: 'Hook',
Description: ''
}
*/
skipHeaders.forEach((header) => delete input[header]);
this.rowsArray = input // NOTHING HAPPENS, rowsArray: string[] = new Array(); input - I don't know what is the type or if I use push. I can't get this data out of pipe
return input;
}))
.pipe(csv.stringify({ header: true }))
.pipe(fs.createWriteStream( this.path))
AND ALSO
as a workaround I wanted to read the newly generated csv but it is also unseccesfful, looks like I need to use promises. I tried some example from internet but was fail. PLEASE HELP
For those who wondering - I was able to resolve my goal using the following approach:
BUT!! I still wonder how to handle this problem via Promises, async/await approaches.
class CsvFormatter{
pathToNotUpdatedCsv: string
readline: any
readStream: any
headers: any
fieldSchema: string[] = new Array()
rowsArray: string[] = new Array()
constructor(pathToCsv: string, encoding: string) {
this.pathToNotUpdatedCsv = pathToCsv
this.readStream = fs.createReadStream(this.pathToNotUpdatedCsv, encoding = 'utf8');
}
async updateCsv(){
//read all csv lines of not updated file
this.readline = readline.createInterface({
input: this.readStream,
crlfDelay: Infinity
});
//save them to array
for await (const line of this.readline) {
this.rowsArray.push(line)
}
//remove columns in csv and return updated csv array
this.rowsArray = this.getUpdatedRows()
//separating headers and other rows in csv
this.headers = this.rowsArray.shift()
}
getUpdatedRows(){
let headersBeforeUpdate = this.removeExtraQuotes(this.rowsArray[0])
let rowsAfterUpdate = []
let indexesOfColumnToDelete = []
let partOfUpdatedArray = []
//get indexes which will be used for deletion of headers and content rows
skipHeaders.forEach((header) => {
indexesOfColumnToDelete.push(headersBeforeUpdate.indexOf(header))
})
//delete rows by index
this.rowsArray.forEach(row => {
partOfUpdatedArray = this.removeExtraQuotes(row)
indexesOfColumnToDelete.forEach(index=>{
partOfUpdatedArray.splice(index)
})
rowsAfterUpdate.push(partOfUpdatedArray)
})
return rowsAfterUpdate
}

Read from AsyncStorage, Parse Array, Convert String to Date object

I'm trying to read array from Async storage with "reminders" key.
Problem is JSON.parse cannot convert 'time' key of element in Array to Date object.
I need to read from storage, parse and assign to reminders state using setReminders()
// EXAMPLE DATA IN ASYNC STORAGE
[{day: 'Monday', time: '2020-04-03T15:17:07.554Z', status: false},
{day: 'Friday', time: '2020-04-03T15:17:07.951Z', status: true},]
// LOAD REMINDERS
useEffect(readReminders, []);
function readReminders() {
AsyncStorage.getItem('reminders').then(value =>setReminders(value));
}
You can parse Date from string using Date.parse(string) or new Date(string) like:
function readReminders() {
AsyncStorage.getItem('reminders').then(values => {
const reminders = values.map(item => {
return {
...item,
time: new Date(item.time)
}
});
});
}
I have add the same issue with the date . try using moment instead of new Date()...
'npm install moment
import moment from "moment";
const time= '2020-04-03T15:17:07.554Z';
const todate= moment(time);
Hope this will help.

Date validation using joi - invalid date isn't throwing an error 2019-11-31

I am trying to use JOI to check that a date is a valid date and also not in the future. I would expect 31 November 2001 to fail because there is no 31 November.. however it it passes!
Strangely 32 November 2001 fails! Any idea what the problem is? My test code is below
const joi = require('joi')
const moment = require('moment')
const schema = joi.object({
location: joi.string().strict().trim().min(1).required().error(() => {
return {
message: 'A location must be entered',
}
}),
incidentDate: joi.date().max(moment().format('YYYY-MM-DD')).required().error(() => {
return {
message: 'A date must be entered that is not in the future',
}
}),
})
const requestForm = {"dateOfIncident":{"day":"31","month":"11","year":"2001"},"location":"sdcds"}
const strDate = `${requestForm.dateOfIncident.year}-${requestForm.dateOfIncident.month}-${requestForm.dateOfIncident.day}`
requestForm.incidentDate = strDate
const joiErrors = joi.validate(requestForm, schema, { stripUnknown: true })
console.log(joiErrors)
adding another .format did the trick
incidentDate: joi.date().format('YYYY-MM-DD').max(moment().format('YYYY-MM-DD')).required().error(() => {
return {
message: 'A date must be entered in the correct format that is not in the future',
}
})
For anyone coming later, I manage to validate the 31ths as follows:
const myDate = Joi.alternatives().conditional('myDate', {
is: Joi.date().iso(),
then: Joi.string().regex(/^((?!(([0-9]{4}-02-30|[0-9]{4}-02-31|[0-9]{4}-04-31|[0-9]{4}-06-31|[0-9]{4}-09-31|[0-9]{4}-11-31)(T[0-9]{2}:[0-9]{2}:[0-9]{2}Z)?)).)*$/)
.message('Date does not exist.'),
otherwise: Joi.string().regex(/^(?!.)/).message('Date is not in a valid format')
})
The otherwise regex is just for allowing a custom message, otherwise it would only say that none of the alternatives were fulfilled.

JS error: stringtime.match is not a function? while trying to transform duration from "2h 3min" string to numbers

I wrote this code and thought that it was successful. However, when I ran it in some tests, I got a result "stringtime.match is not a function".
I'm trying to write the code in which I create a turnHoursToMinutes() function that receives an array as parameter, and - replaces the duration info of each of the movies for its equivalent in minutes. I will also need to return a new array with all the info about movies, meaning, I shouldn't modify the original array.
let movies = [
{
title: 'The Shawshank Redemption',
year: '1994',
director: 'Frank Darabont',
duration: '2h 22min',
genre: ['Crime', 'Drama'],
rate: '9.3'
},
{
title: 'The Godfather',
year: '1972',
director: 'Francis Ford Coppola',
duration: '2h 55min',
genre: ['Crime', 'Drama'],
rate: '9.2'
},
{
title: 'The Godfather: Part II',
year: '1974',
director: 'Francis Ford Coppola',
duration: '3h 22min',
genre: ['Crime', 'Drama'],
rate: '9.0'
}
]
function turnHoursToMinutes(minutesArr) {
let durationArr = minutesArr.map(currentItem => {
let stringtime = currentItem.duration;
let strnumbers = stringtime.match(/\d+/g);
let numbers = strnumbers.map(Number);
let minutes = 0;
if (stringtime.includes("h") && stringtime.includes("min")) {
minutes = (numbers[0] * 60) + numbers[1];
} else if (stringtime.includes("h")) {
minutes = numbers[0] * 60;
} else if (stringtime.includes("min")) {
minutes = numbers[1];
}
currentItem.duration = minutes;
return {...currentItem};
});
return durationArr;
}
console.log(turnHoursToMinutes(movies));
I looked online for solutions and tried to apply some suggestions to my code but I couldn't get it to work :( Also, I thought I return a new array but I still got an error saying that I didn't return a new array...
I made sure that stringtime is a string like one solution said but I still can't get it working. Please help!
Check this, as it says:
The match() method retrieves the result of matching a string against a regular expression.
So if you pass something that it is not a string or simply you dont put an object with the property duration , it will throw you an error.
You must check if let stringtime = currentItem.duration; is null or not.

Categories

Resources