CS 312 - Assignment One
Goals
- Develop a basic familiarity with
git
and GitHub classroom - Get started with basic JavaScript and Node.js
- Practice some of the functional aspects of JavaScript (higher-order functions and closures)
- Practice test-driven development (TDD)
- Use a linter to write more consistent, more maintainable, higher quality, code
Prerequisites
- Install
git
and Node.js as described on the Getting Started page - Click the GitHub classroom link and then clone repository GitHub classroom creates to your local computer (see below).
- Update the
package.json
file with your name and e-mail - Install the package dependencies with
npm install
Click the GitHub classroom link to create a repository for yourself with the name "pa1-\<Your GitHub username>.git", e.g. "assignment-01-ChristopherPAndrews.git". Clone that repository to your local computer:
git clone https://github.com/csci312-f20/assignment-01-<Your GitHub username>.git pa1
The clone command above will create a copy of the assignment skeleton in the local directory pa1
.
The above assumes the HTTPS interface to GitHub. GitHub offers both SSH and HTTPS-based interfaces to your repository. Using the former requires that you set up a public-key with GitHub. GitHub recommends the HTTPS for simplicity, but you may prefer to use SSH for added security. It is also possible to switch between the two using these instructions.
The assignment skeleton is a NPM package. Once you have cloned the repository edit the package.json
file to add your name. Then install the package dependencies with npm install
.
Background
Running and Testing Your Program
You can run your code interactively in Node.js by running .load *filename*
at the interpreter prompt (i.e., you have already typed node
on the command line). Do this to load your functions for testing. You can also run your file non-interactively, e.g. node *filename*
.
You can and are encouraged to practice test-driven development, TDD, (as seen in class). The assignment skeleton is set up for unit testing with Jest. You can run the test suite with npm test
. We converted the assignment examples into an initial set of tests in index.test.js
. Note that these tests are currently failing and so are set to be skipped. As you start developing "unskip" each test by changing describe.skip
to describe
. Code that passes all of the provided tests is not guaranteed to be correct (thus we encourage you to add additional tests). However, code that fails one or more these tests does not meet the specification.
Assignment
Part 1: Truthiness
One of the places where people get tripped up in JavaScript is with "falsy" and "truthy" values. Do some online research to learn more. Write a function howFalse(arr)
, which takes an array as an argument and returns an object containing a count of each kind of falsy value found in the array. The object should only have properties for falsy values that were actually present. For example, howFalse([5, 0, 42, 'dalek', 3-3, 7, Math.sqrt(-1)])
should return {0: 2, NaN: 1}
. Your solution should involve the use of a higher-order function.
Part 2: Reduce
Write a function myMax(arr)
to find the largest value in an array using reduce
. It should accept an array as an argument and return the largest value in the array (you can assume that the array is non-empty and that the values in the array are comparable). For example, myMax([1, 2, 3])
should return 3
. Your code should be of the form const myMax = arr => arr.reduce(TODO);
, where TODO
should be replaced with the actual functionality.
Part 3: Filtering
Write a function threshold(objs, field, cutoff)
. This function takes in an array of objects (objs
), the name of a property found in the objects (field
), and a cutoff value (cutoff
). The function should return an array of those objects in objs
whose values for field
are less than or equal to cutoff
. For example, threshold([{x: 4, y: 5}, {x: 2, y: 9}, {x: 1, y: 1}], 'y', 5)
should return [{x: 4, y: 5}, {x: 1, y: 1}]
. Your solution must use the array's filter
method.
Part 4: Optional arguments
In JavaScript, functions can take an optional number of arguments. For example Math.min can take an arbitrary number of values for comparison. Write a function myMin()
that duplicates this functionality (without just calling Math.min
). As an example, myMin(1, 2, 3)
should return 1
. Your function should match the behavior of Math.min
exactly, including the corner cases like no arguments.
Part 5: Interval alarm with closures
You are building an application to facilitate interval workouts. Write a function intervalAlarm
that takes an array of integers specifying interval times in seconds and returns a function that you can invoke to start the timer. The returned function should not take any arguments. When you invoke this function it should print a message when each interval expires, like shown below (including the length of the specified interval and the total time elapsed). Invoking intervalAlarm
should not start the timer. You will need to use a closure.
> const alarm = intervalAlarm([1, 0.5, .8])
undefined
> alarm()
undefined
> Interval of 1s completed (1.006s elapsed)!
Interval of 0.5s completed (1.502s elapsed)!
Interval of 0.8s completed (2.304s elapsed)!
> alarm()
undefined
> Interval of 1s completed (1s elapsed)!
Interval of 0.5s completed (1.501s elapsed)!
Interval of 0.8s completed (2.302s elapsed)!
Part 6: Calendar histogram
Now that you have experience with data structures and iteration, we will combine those tools to implement a calendar "histogram". You are trying to find the number of individuals who are available in specific windows during the week (think Doodle). A window is specified by an integer day of the week (0 is Sunday, 6 is Saturday), an inclusive start time and an exclusive end time (time is expressed in minutes since midnight). For example Tuesday 8:00AM-9:15AM would be specified as the following object:
{
day: 2,
start: 480,
end: 555
}
Write a function availablityCount(windows, availabilities)
that has two arrays of time window objects as arguments. Your function should return a copy of windows
array (in any order) with each window also containing a count
field of the number of objects in availabilities
that overlap that window. Windows in which no one was available should have a count of zero. You should only increment the count if the availability fully overlaps the time window. For example the following call
availabilityCounts(
[
{ day: 2, start: 480, end: 495 },
{ day: 2, start: 840, end: 855 },
],
[{ day: 2, start: 480, end: 555 }]
);
should return
[
{ day: 2, start: 480, end: 495, count: 1 },
{ day: 2, start: 840, end: 855, count: 0 },
];
because the availability (Tuesday 8:00AM-9:15AM) fully overlaps the first window, but does not overlap the second (Tuesday 2:00PM - 2:45PM).
There are many possible approaches to this problem. Any correct implementation will be accepted (e.g. $$O(n^2)$$) time complexity is acceptable), but for context, the solution is less than 20 nicely formatted, heavily commented, lines (think about how you could use the functional tools you worked with above).
Part 7: Finishing Up
Once your program is working make sure you don't have any style issues by running ESLint via npm run lint
. ESLint can fix many of the errors automatically by running npm run lint -- --fix
(although since ESLint can sometimes introduce errors during this process, we suggest committing your code before running "fix" so you can rollback any changes). To get full credit for the style portion of the assignment your code must have zero ESLint errors.
Notice that there is an additional file in your directory, .gitignore
, which specifies files that should not be tracked by Git. It is good practice to create this file first in your repository to prevent undesired files from getting committed. Here we have provided a relevant .gitignore
file in the skeleton. In general we want to exclude platform specific files, like the OSX .DS_Store files, any files that are automatically generated as well as files containing secrets such as API keys.
If you haven't done so already commit your changes to index.js:
- Start by running
git status
in the terminal in the assignment directory to see how your modified files are reported. - Then add the modified files to stage them for the commit, i.e.
git add index.js
. The staging area now contains the files whose changes will be committed. - Run
git status
again to see the how staged files are reported. - Commit your changes with
git commit -m "Your pithy commit message"
(replace "Your pithy commit message" with a pithy but informative commit message, quotes are required). You can also skip the-m
option. If you do so,git
will open a text editor for you to write your commit message. - Run
git log
to see your commit reported.
Finally submit your assignment by pushing your changes to the GitHub classroom via git push --all origin
and then submitting your repository to Gradescope as described here. You can submit (push to GitHub and submit to Gradescope) multiple times. The last submission before the deadline will be the one scored.