I need help with something that might be a common problem and something that others would benefit from as well, which is to modify dates in an object and combine them into a chronological list. The overall goal is to create a list which reflects all of the upcoming special dates in a year:
1 Jan - Sharpe's Anniversary
2 May - Edward's Birthday
12 Dec - Zero's Anniversary
etc...
I began by creating an object to represent this. Then slice out the year so that a comparison won't just arrange them by their order of initial occurrence, but will give an annual basis. Then perform a comparison to arrange the dates in chronological order. Then log out the result with the matching person's identity.
Up to this point I got it to work. However, I do not know how to repeat this and make it DRY. Certainly it would be sloppy to run this for anniversaries, then for birthdays without making some sort of custom function. All my attempts have not ended successfully.
Does anyone have a more elegant solution?
Much thanks to the community here :)
// Sample data
var items = [{
name: 'Edward',
anniversary: "2015-01-23",
birthday: "1988-05-02"
},
{
name: 'Sharpe',
anniversary: "2017-01-01",
birthday: "1988-05-10"
},
{
name: 'And',
anniversary: "2018-05-10",
birthday: "1988-06-12"
},
{
name: 'The',
anniversary: "1988-08-11",
birthday: "1979-03-12"
},
{
name: 'Magnetic',
anniversary: "2017-01-05",
birthday: "1956-06-21"
},
{
name: 'Zeros',
anniversary: "1990-12-12",
birthday: "1935-07-23"
}
];
// Slice out the year so that dates are annualized
for (var i = 0; i < items.length; i++) {
items[i].anniversary = items[i].anniversary.slice(5, 10);
};
// Sort dates in chronological order
items.sort(function(a, b) {
return new Date(a.anniversary) - new Date(b.anniversary);
});
// Console.log the dates with their associated name
for (var i = 0; i < items.length; i++) {
console.log(items[i].anniversary + " " + items[i].name);
}
When you remove the year from a date like "1988-08-11" you get "08-11". If you then parse this with the built-in Date parser, you'll either get an invalid date, or a date for 1 November 0008, when the original date was for 11 August. The parser will see a year and a month, and use 1 for the missing day.
But don't dispair! The ISO 8601 date format can be sorted as a string, so do as you are and sort as strings, not dates, e.g.
// Sample data
var items = [{
name: 'Edward',
anniversary: "2015-01-23",
birthday: "1988-05-02"
},
{
name: 'Sharpe',
anniversary: "2017-01-01",
birthday: "1988-05-10"
},
{
name: 'And',
anniversary: "2018-05-10",
birthday: "1988-06-12"
},
{
name: 'The',
anniversary: "1988-08-11",
birthday: "1979-03-12"
},
{
name: 'Magnetic',
anniversary: "2017-01-05",
birthday: "1956-06-21"
},
{
name: 'Zeros',
anniversary: "1990-12-12",
birthday: "1935-07-23"
}
];
// Get the events
var specialDates = items.reduce(function(acc, item) {
acc.push([item.name, item.anniversary.substr(5), 'Anniversary'], [item.name, item.birthday.substr(5), 'Birthday']);
return acc;
}, []).sort(function (a, b) { // sort on month
return a[1].localeCompare(b[1]);
}).map(function(event) { // Replace month number with short name
var months = [,'Jan','Feb','Mar','Apr','May','Jun',
'Jul','Aug','Sep','Oct','Nov','Dec'];
return [event[0], months[+event[1].split('-')[0]] + ' ' + event[1].substr(3), event[2]];
});
// Write result
specialDates.forEach(function(event) {
console.log(event[1] + ': ' + event[0] + ' ' + event[2]);
});
This could be a bit smarter and get the event name from the original object so that you can add as many event types as you want and not have them hard coded, so an object like:
var items = [{
name: 'Edward',
events: {
anniversary: "2015-01-23",
birthday: "1988-05-02"
}
},
...
Also note that new Date("2015-01-23") will treat the string as UTC (timezone offset +00:00), so for hosts west of Greenwich the date might appear to be the day before.
let goal = items
.map( ({ name, anniversary }) => ({ name, 'anniversary': anniversary.slice( 5, 10 ) }) )
.sort( ({ 'anniversary': a }, { 'anniversary': b }) => {
if( a > b ) return 1;
if( a < b ) return -1;
return 0;
}
.map( ({ name, anniversary }) => anniversary + ' ' + name );
goal.forEach( item => { console.log( item ); });
Related
I'm writing a code that has date time in a json, And I want to filter the json and show the data where date from datetime is greater than today's date.
Here is my code.
var data = [{
datetime: "2021-08-09T06:00:00.000Z",
id: "1"
}, {
datetime: "2021-06-07T02:00:00.000Z",
id: "2"
}, {
datetime: "2021-08-04T11:00:00.000Z",
id: "3"
}, {
datetime: "2021-08-04T10:00:00.000Z",
id: "4"
}, {
datetime: "2021-08-05T12:55:00.000Z",
id: "5"
}, {
datetime: "2020-08-10T13:30:00.000Z",
id: "6"
}]
data = data.filter(item=> { return new Date(item.datetime).getDate() > new Date().getDate()});
console.log(data);
it should print every thing apart from id 3, 4 and 6. Also when I do a getDate(), I think it only considers DD from MMDDYYYY, that's why 6 is getting printed. what would be the best way to achieve this?
There seems to be a typo:
data = data.filter(item=> { return new Date(item.datatime).getDate() > new Date().getDate()});
// ^
// datetime
data = data.filter(item => {
const date = new Date(item.datetime);
const today = new Date();
return date.getUTCFullYear() * 10000 + date.getUTCMonth() * 100 + date.getUTCDate() > today.getUTCFullYear() * 10000 + today.getUTCMonth() * 100 + today.getUTCDate();
});
It works except when the subject unless the subject is the same name. Then I get the first date to the second subject that is the same.
I can't change the array since it's through API. However can I make so somehow if the first date is already set on math, then it should add the second date to second subject? Now the second subject get's the first date
var subjects = [
{ name: "math" }, //The first
{ name: "sports" },
{ name: "math" }, //The second
{ name: "art" },
];
var subjectdates = [
{ name: "math", year: 2017 }, //first date
{ name: "sports", year: 2018 },
{ name: "math", year: 2019 }, //second date
{ name: "art", year: 2020 },
];
const addDates = subjects.map((classes) => ({
subject: classes,
end_subject_date: subjectdates.find((item) => classes.name == item.name),
}));
console.log(addDates);
Using Array#reduce on subjectdates, construct a Map where the key is the name and the value is a list of the elements of this name.
Then, in the loop, to get the end_subject_date, you can use Map#get to get the list of elements of this name, and Array#shift to get and remove the first element:
const
subjects = [ {name:"math"}, {name:"sports"}, {name:"math"}, {name:"art"} ],
subjectdates = [ {name:"math",year:2017}, {name:"sports",year:2018}, {name:"math",year:2019}, {name:"art",year:2020} ];
const subjectDatesMap = subjectdates.reduce((map, item) =>
map.set(
item.name,
[...(map.get(item.name) || []), item]
)
, new Map);
const addDates = subjects.map(classes => ({
subject: classes,
end_subject_date: (subjectDatesMap.get(classes.name) || []).shift()
}));
console.log(addDates);
If you have the same keys in arrays:
Sort array by keys:
subjects = subjects.sort((a,b)=>a.name>b.name?1:-1);
subjectdates = subjectdates.sort((a,b)=>a.name>b.name?1:-1);
Insert values by index:
const result = subjects.map((s, i)=>
({ subject:s.name, end_subject_date:subjectdates[i].year}) );
I have an array of objects sorted by date:
const alerts = [{
id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke', title: 'this is the first 1'
}, {
id: 2, date: '2018-10-30T23:18:31.000Z', name: 'Mark', title: 'this is the second one'
}]
I am trying to 'group' the alerts by date so trying to create 'datesections' which have a dateheader, the result should be something like:
const sections = [{
date: '2018-10-31T23:18:31.000Z',
heading: 'today',
alerts: [{ id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke',
title: 'this is the first one' }]
}, {
date: '2018-10-30T23:18:31.000Z',
heading: 'Yesterday',
alerts: [{ id: 2, date: '2018-05-30T23:18:31.000Z', name: 'Mark',
title: 'this is the second one' }]
}]
I tried something this but can't figure out how to get the alerts with the same date in the alerts prop:
const sections2=alerts.map(a =>
({
date: a.date,
heading:'today new',
alerts:alerts
})
)
const alerts = [
{ id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke', title: 'this is the first 1' },
{ id: 2, date: '2018-05-30T23:18:31.000Z', name: 'Mark', title: 'this is the second one' }
]
const grouping = _.groupBy(alerts, element => element.date.substring(0, 10))
const sections = _.map(grouping, (items, date) => ({
date: date,
alerts: items
}));
console.log(sections);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Can't help you with headings - what if it's neither "today" or "yesterday"?
I feel like you are asking a couple of things here. The key one is how to group by day with a date.
To do that you will first need to know how to group. This answer may help with that.
As far as how to group by day there are a number of ways to do that. Simplest I can think of is to cut off everything after the "T" in the date string and sort that.
From my point of view it's not really a map what you need here, map will return a new array but not what you want. You can do this with 2 for statements
let total = [];
for (let j = 0; j < alerts.length; j++) {
let item = alerts[j];
let foundDate = false;
for (let i = 0; i < total.length; i++) {
if (total[i].date === item.date) {
foundDate = true;
total.alerts.push(item);
}
}
if (!foundDate) {
console.log("!found");
total.push({
date: item.date,
heading: "Yesterday",
alerts: [item]
});
}
}
If you console.log yout total array, will contain what you want.
If you need any other explanation pls let me know.
You can use a regular expression to match the part of the date you want and then group your data. You can add there the header you want. Hope this helps.
const alerts = [
{ id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke', title: 'this is the first 1' },
{ id: 2, date: '2018-10-30T23:18:31.000Z', name: 'Mark', title: 'this is the second one' },
{ id: 3, date: '2018-10-30T23:14:32.000Z', name: 'Mark', title: 'this is the third one' }
];
const groupByDate = (data) => {
return data.reduce((acc, val) => {
const date = val.date.match(/\d{4}-\d{2}-\d{2}/g).toString();
const item = acc.find((item) => item.date.match(new RegExp(date, 'g')));
if (!item) acc.push({ date: val.date, alerts: [val], heading: 'some heading' });
else item.alerts.push(val);
return acc;
}, []);
};
console.log(groupByDate(alerts));
Maybe you need something like this? Didn't have much time for this and last array parsing might be done in more elegant way ;)
var alerts = [
{ id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke', title: 'this is the first 1' },
{ id: 3, date: '2018-10-31T23:44:31.000Z', name: 'Joke1', title: 'this is the 2nd' },
{ id: 2, date: '2018-10-30T23:18:31.000Z', name: 'Mark', title: 'this is the second one' },
{ id: 4, date: '2018-10-30T23:45:31.000Z', name: 'Mark1', title: 'this is the 3rd' },
{ id: 5, date: '2018-10-27T23:18:31.000Z', name: 'Mark2', title: 'this is the 4th' },
];
var processedAlerts = [], finalAlerts;
(function(initAlerts){
//iterate through array to make keys to group by day
for(var i = 0; i < initAlerts.length; i++){
processedAlerts[i] = initAlerts[i];
//substring here can be more sophisticated - this was faster
initAlerts[i].keyDate = initAlerts[i].date.substr(0, 10);
}
//supporting function to convert string to date
//to acheve more detailed sorting that includes time
//just use date object and use hours, minutes and/or seconds to create Date object
function dateFromString(strDate){
var date, tmpDate;
//convert string to array - I assume that date format is always the same
//yyyy-mm-dd and will become Array 0: year, 1: month, 2: day of the month
tmpDate = strDate.split("-");
//moths in js are zero pased so Jan is 0, Feb is 1 and so on
//so we want to substract 1 from human readable month value to get correct date
date = new Date(tmpDate[0], tmpDate[1]-1, tmpDate[2]);
return date;
}
//function used to compare dates and passed to sort function
function comparedates(obj1, obj2){
var date1, date2;
date1 = dateFromString(obj1.keyDate);
date2 = dateFromString(obj2.keyDate);
let comparison = 0;
if(date1>date2){
comparison = 1;
} else if(date1<date2){
comparison = -1;
}
//to achieve reverse just multiply comparison result by -1
return comparison*(-1);
}
function getHeader(date){
//here place logic to generate header
//this involves comparing dates probably from keyDate
return "temp header: " + date.toString()
}
//sort the array by keyDate from newest to oldest
processedAlerts.sort(comparedates);
//final array rebuild
//pass here sorted array
finalAlerts = (function(arrayAlerts){
var aAlerts = [], k = 0;
for(var j = 0; j < arrayAlerts.length; j++){
//check if entry for date exists
//if no than create it
if(!aAlerts[k]){
aAlerts[k] = {
//removed title because I asummed that each alert has unique title and put them in alerts instead
date: arrayAlerts[j].keyDate, //agroupped date
heading: getHeader(arrayAlerts[j].keyDate), //update this function to return personalized heading
//here you can shape the alert object how you need
//I just passed it as it was
alerts: [arrayAlerts[j]] //array with first object inside
};
} else {
//add another alert to day
aAlerts[k].alerts.push(arrayAlerts[j]) //array with first object inside
}
//increasing final array key
//if there is previous entry and keys are the same for current and previous
if(arrayAlerts[j-1] && (arrayAlerts[j].keyDate == arrayAlerts[j-1].keyDate)){
k++;
}
}
return aAlerts;
})(processedAlerts);
})(alerts);
console.log(finalAlerts);
i have list of objs:
[{
name: one,
date: 2017-09-18
}, {
name: two,
date: 2017-09-11
}, {
name: three,
date: 2017-09-13
}]
And i want to sort it by week.
Maybe like:
{
1week(or , maybe better key like start of week): [{
name: two,
date: 2017-09-11
}, {
name: three,
date: 2017-09-13
],
2week: [{
name: one,
date: 2017-09-18
}]
}
how can I determine by what week the dates belong?
how can I do better?
I played around with this, and I think this is similar to what you need:
https://jsfiddle.net/pegla/ytmayemr/
code:
let arrayOfDates = [{
name: 'one',
date: '2017-09-18'
}, {
name: 'two',
date: '2017-09-11'
}, {
name: 'three',
date: '2017-09-13'
}];
function getWeekNumber(d) {
// Copy date so don't modify original
d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()));
// Set to nearest Thursday: current date + 4 - current day number
// Make Sunday's day number 7
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay()||7));
// Get first day of year
var yearStart = new Date(Date.UTC(d.getUTCFullYear(),0,1));
// Calculate full weeks to nearest Thursday
var weekNo = Math.ceil(( ( (d - yearStart) / 86400000) + 1)/7);
// Return array of year and week number
return [d.getUTCFullYear(), weekNo];
}
let newArrayOfDatesByWeek = arrayOfDates.reduce((prevVal, currVal, index)=>{
let week = `week ${getWeekNumber(new Date(currVal.date))[1]} of ${getWeekNumber(new Date(currVal.date))[0]}`;
if(!(week in prevVal)) {
prevVal[week] = [];
}
prevVal[week].push(currVal);
return prevVal;
}, []);
console.log(newArrayOfDatesByWeek);
getWeekNumber function is taken from this answer by RobG, so thanks for that:
Get week of year in JavaScript like in PHP
I'm working through gathering information from multiple arrays but I've hit a stumbling block. I need to be able to determine how many days were spent in each city. The trouble is I can't figure out a way to count time entries that span a single day as 1 day. In the below the first two San Diego entries should result in a single day since both logs happened within the same day.
timeLogs = [
{'city':'San Diego','date':'2017-03-21T18:52:00.984Z'},
{'city':'San Diego','date':'2017-03-21T12:13:00.984Z'},
{'city':'San Diego','date':'2017-03-19T11:02:00.984Z'},
{'city':'Boulder','date':'2017-02-12T11:29:00.984Z'}
]
What I'm after is the following resulting array based on the above:
daysPerCity = [
{'San Diego':'2'},
{'Boulder':'1'}
]
Currently I'm working on a loop which coverts the dates to strings and then checks for equality, if the same I'm trying to not increment the city in the new array but I'm stuck when it hits the very first instance of a city...
You can use Array methods like reduce and map to build an object containing unique days grouped by city, and then use Object.keys(...).length to get the number of distinct days.
var timeLogs = [
{ city: 'San Diego', date: '2017-03-21T18:52:00.984Z' },
{ city: 'San Diego', date: '2017-03-21T12:13:00.984Z' },
{ city: 'San Diego', date: '2017-03-19T11:02:00.984Z' },
{ city: 'Boulder', date: '2017-02-12T11:29:00.984Z' }
]
var daysPerCity = timeLogs.reduce(function (map, e) {
(map[e.city] = map[e.city] || {})[e.date.slice(0, 10)] = true
return map
}, {})
Object.keys(daysPerCity).forEach(function (k) {
this[k] = Object.keys(this[k]).length
}, daysPerCity)
console.log(daysPerCity)
You could use this ES6 code:
const timeLogs = [
{'city':'San Diego','date':'2017-03-21T18:52:00.984Z'},
{'city':'San Diego','date':'2017-03-21T12:13:00.984Z'},
{'city':'San Diego','date':'2017-03-19T11:02:00.984Z'},
{'city':'Boulder','date':'2017-02-12T11:29:00.984Z'}
];
const result = Object.assign({}, ...Array.from(
timeLogs.reduce( (acc, {city, date}) => {
(acc.get(city) || acc.set(city, new Set).get(city))
.add(date.substr(0, 10));
return acc;
}, new Map),
([city, days]) => ({ [city]: days.size })
));
console.log(result);