I wrote a Chrome extension that lists upcoming concerts for the next seven days, this month, and next month. I had trouble with thenextSeven() method, especially transitioning from dates like Dec. 31st to Jan. 1st, hence the really fuglyif-statements. I was wondering if anyone had any advice to make the code more condensed. I would really appreciate any feedback!
$(document).ready(function() { var months = ['Jan. ', 'Feb. ', 'March ', 'April ', 'May ', 'June ', 'July ', 'Aug. ', 'Sept. ', 'Oct. ', 'Nov. ', 'Dec. ']; var systemTime = new Date(); $.ajax({ url: 'http://nycmetalscene.com/', type: 'GET', success: function(data) { var parsedData = $.parseHTML(data); var parseResults = $(parsedData).find('p').text(); var events = parseResults.split(/Sun\. |Mon\. |Tues\. |Wed\. |Thurs\. |Fri\. |Sat\. /g); events.splice(0, 1); var monthIndex = systemTime.getMonth(); var day = systemTime.getDate(); var testDay = systemTime.getDate(); var week = []; var thisDay; getDays(); $('#nextseven').on('click', function() { $('#concerts').empty(); nextSeven(); }); $('#thismonth').on('click', function() { $('#concerts').empty(); showMonth(); }); $('#nextmonth').on('click', function() { $('#concerts').empty(); showNextMonth(); }); function getDays() { for (var i = 0; i < 7; i++) { if (day < 31) { week.push(day); day++; } else { week.push(day); day = 1; } } for (var i = 0; i < week.length; i++) { if (week[i] === 1 || week[i] === 31) { week[i] = ' ' + week[i] + 'st,'; } else if (week[i] === 2 || week[i] === 22) { week[i] = ' ' + week[i] + 'nd,'; } else if (week[i] === 3 || week[i] === 23) { week[i] = ' ' + week[i] + 'rd,'; } else { week[i] = ' ' + week[i] + 'th,'; } } } function nextSeven() { for (i = 0; i < week.length; i++) { thisDay = week[i]; $.each(events, function(i, item) { var eventsstr = JSON.stringify(item); if (eventsstr.match(thisDay)) { if (monthIndex === 11 && testDay >= 26) { if (eventsstr.match(months[monthIndex]) || eventsstr.match(months[0])) { var concert = JSON.parse(eventsstr); $('#concerts').append('<p>' + concert + '</p>'); } } else if (monthIndex < 11 && testDay >= 26) { if (eventsstr.match(months[monthIndex]) || eventsstr.match(months[monthIndex + 1])) { var concert = JSON.parse(eventsstr); $('#concerts').append('<p>' + concert + '</p>'); } } else { if (eventsstr.match(months[monthIndex])) { var concert = JSON.parse(eventsstr); $('#concerts').append('<p>' + concert + '</p>'); } } } }); } } function showMonth() { $.each(events, function(i, item) { var eventsstr = JSON.stringify(item); if (eventsstr.match(months[monthIndex])) { var concert = JSON.parse(eventsstr); $('#concerts').append('<p>' + concert + '</p>'); } }); } function showNextMonth() { $.each(events, function(i, item) { var eventsstr = JSON.stringify(item); if (eventsstr.match(months[monthIndex + 1] || months[0])) { var concert = JSON.parse(eventsstr); $('#concerts').append('<p>' + concert + '</p>'); } }); } } });});<!DOCTYPE html><html><head> <meta http-equiv="content-type" content="text/html; charset=utf-8 ;"> <script src=jquery-3.1.1.min.js></script> <script type="text/javascript" src="Scrape.js"></script></head><body> <div> <h1>NYC Concerts</h1> <div> <ul> <center> <a href="#">Next 7 Days</a> <a href="#">This Month</a> <a href="#">Next Month</a> </center> </ul> <h2><div></div></h2> </div> <link rel="stylesheet" type="text/css" href="popup.css"></link></body></html>- 1\$\begingroup\$I have rolled back the last edit. Please seewhat you may and may not do after receiving answers.\$\endgroup\$Vogel612– Vogel6122016-12-17 23:50:57 +00:00CommentedDec 17, 2016 at 23:50
1 Answer1
What doesn't work properly
There are two severe issues in the way you evaluate when to go from a given month to the next one.
When defining the set of days in a week
var systemTime = new Date();var day = systemTime.getDate();// ...for (var i = 0; i < 7; i++) { if (day < 31) { week.push(day); day++; } else { week.push(day); day = 1; }}In the above code you work as ifall months had 31 days!
Instead you should take account of those having only 30 days, and of February with its 28 or 29 days depending on bisextile years.
Obviously doing so becomes a much more complex task, so I'd suggest using a totally different way, based on theDate object internal capabilities:
var systemTime = new Date();// ...for (var i = 0; i < 7; i++) { var day = systemTime.getDate(); week.push(day); systemTime.setDate(day + 1);}This way, not only we ensure to get correct days in any case but the code is even more reduced than before.
When looking for an involved month in the content of an event
The same kind of error appears in thenextSeven() function, when you testmonthIndex < 11 && begDay >= 26: in fact, testingbegDay >= 26 is appropriate only for months having 31 days... you know the next.
But there is also another issue here:
- for each event you select it if it's matching
thisDay - then you look for the given
monthIndexormonthIndex + 1,independently from the current day - so you may accept wrong days, having the required number but belonging to another month!
Again I'll suggest to work based on full dates rather than separate days and months, but it'll be more clear to explain it later (see "The week case" below).
How to simplify and reduce code
Your code can be improved in several ways.
Formatting day
Once defined the set of days, here is how you finally format them:
for (var i = 0; i < week.length; i++) { if (week[i] === 1 || week[i] === 31) { week[i] = ' ' + week[i] + 'st,'; } else if (week[i] === 2 || week[i] === 22) { week[i] = ' ' + week[i] + 'nd,'; } else if (week[i] === 3 || week[i] === 23) { week[i] = ' ' + week[i] + 'rd,'; } else { week[i] = ' ' + week[i] + 'th,'; }}First I'd suggest usingmap() instead of afor() loop.
A second improvement is to factorize what's common to all cases, starting by defining the propersuffix, then building the result at once.And finally definesuffix in a more compact way, for example using a predefined object registering suffixes.
This way, the whole code sequence above can be replaced by this:
const suffixes = {'1': 'st', '2': 'nd', '3': 'rd'};var systemTime = new Date();// ...week = week.map(function (day) { return ' ' + day + (suffixes[day % 10] || 'th') + ',';});Defining days at once
In fact, we now see that the two partial tasks examined above can be joined, so the wholegetDays() function becomes much more simple:
const suffixes = {'1': 'st', '2': 'nd', '3': 'rd'};var systemTime = new Date();// ...function getDays() { for (var i = 0; i < 7; i++) { day = systemTime.getDate(); week.push(' ' + day + (suffixes[day % 10] || 'th') + ','); systemTime.setDate(day + 1); }}Reorganization
It must be observed that thisgetDays() function only serves to populateweeks, which itself is usedonly in thenextSeven() context.
From that:
- The
getDays();call should'nt appear in the general context ofsuccessbut in its ownnextSeven()context. - In this context, we see that thewhole job is wrapped in a
for()loop iteratingweek, to work onthisDay, which is merelyweek[i]. - So it'll be more efficient to abandon the
getDays()function and integrate its job tonextSeven().
Current organization schema:
getDays();// ...function getDays() { for (var i = 0; i < 7; i++) { var day = systemTime.getDate(); week.push(' ' + day + (suffixes[day % 10] || 'th') + ','); systemTime.setDate(day + 1); }}// ...function nextSeven() { for (i = 0; i < week.length; i++) { thisDay = week[i]; // ... }}Suggested organization schema:
function nextSeven() { for (var i = 0; i < 7; i++) { var day = systemTime.getDate(); thisDay = ' ' + day + (suffixes[day % 10] || 'th') + ','; // ... systemTime.setDate(day + 1); }}(note: this way, theweek variable doesn't exist any more)
Compacting some sequences
There are cases where using several statements to build a desired result can be simplified to a unique statement, so avoiding temporary variables not used elsewhere, though becoming more readable at the same time.
At the opposite, yet for readability, it may be better to extract constants from the sequence.
Here is an example:
var parsedData = $.parseHTML(data);var parseResults = $(parsedData).find('p').text();var events = parseResults.split(/Sun\. |Mon\. |Tues\. |Wed\. |Thurs\. |Fri\. |Sat\. /g);events.splice(0, 1);It can be replaced by:
const regexDays = /Sun\. |Mon\. |Tues\. |Wed\. |Thurs\. |Fri\. |Sat\. /g;// ...var events = $($.parseHTML(data)).find('p').text().split(regexDays).splice(0, 1);Factorization
The most obviously visible case where factorization can take place is the one of theshowMonth() andshowNextMonth() functions.
What's done in their body differ only by one usingmonthIndex and the other usingmonthIndex + 1, so they can be replaced by a unique function acceptingmonthIndex as an argument:
function showMonth(monthIndex) { $.each(events, function(i, item) { var eventsstr = JSON.stringify(item); if (eventsstr.match(months[monthIndex] || months[0])) { var concert = JSON.parse(eventsstr); $('#concerts').append('<p>' + concert + '</p>'); } });}And where they were called:
showMonth();becomesshowMonth(monthIndex);showNextMonth();becomesshowMonth(monthIndex + 1);
The week case
Now it's time to bo back to the issue mentioned above, while at the same time simplifying thenextSeven() process.
The revisedshowMonth() function currently takes care of the given month: we can ask it to also take care of the given day, so it will select only those events which satisfy both, like this:
function showMonth(monthIndex, day) { $.each(events, function(i, item) { var eventsstr = JSON.stringify(item); if ( eventsstr.match(months[monthIndex] || months[0]) && eventsstr.match(day) ) { var concert = JSON.parse(eventsstr); $('#concerts').append('<p>' + concert + '</p>'); } });}Now we can very simply call it fromnextSeven():
function nextSeven() { for (var i = 0; i < 7; i++) { var day = systemTime.getDate(); var thisDay = ' ' + day + (suffixes[day % 10] || 'th') + ','; var thisMonth = systemTime.getMonth(); showMonth(thisMonth); systemTime.setDate(day + 1); }}But so we observe thatshowMonth() doesn't work properly for the two other cases (month, nextMonth), so we must finally add what's needed for that (i.e. don't care of day if undefined):
function showMonth(monthIndex, day) { $.each(events, function(i, item) { var eventsstr = JSON.stringify(item); if ( eventsstr.match(months[monthIndex] || months[0]) && (!day || eventsstr.match(day)) ) { var concert = JSON.parse(eventsstr); $('#concerts').append('<p>' + concert + '</p>'); } });}NOTE: since I don't know the exact structure and format of the events content, I don't go further on this, but it's likely possible to be more directly precise and efficient, depending on how month and day are presented.
You mustlog in to answer this question.
Explore related questions
See similar questions with these tags.
