CS 312 Software Development

CS 312 - Assignment One

Goals

  • Develop a basic familiarity with repl.it, 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
Old prerequisite instructions

Prerequisites

  1. Click the GitHub classroom link to accept the assignment.
  2. Go to the repl.it team page and start an "Assignment 1" project.
  3. Go to the Version Control panel and try to connect to your assignment 1 repository on GitHub. If it works, skip ahead to deleting the placeholder file.
  4. Click the button to create a new git repository.
  5. Go to the shell and type git remote add origin your-git-repo, where your-git-repo is the address of your assignment 1 repository (connect to the remote repo).
  6. Type git branch -m main (rename the primary branch)
  7. Type git pull origin main --allow-unrelated-histories (pull the code down from GitHub and ignore then fact that we have two different repos involved). It will complain about a lack of editor -- that's okay.
  8. Type git commit -m "Merge in assignment 1 code" (merge the code from GitHub into your local codebase)
  9. Delete the placeholder.js file.
  10. If you started the assignment before I caught the stray .replit file and removed it, remove the .replit file.
  11. Update the package.json file with your name and e-mail
  12. Switch to the shell and type npm install to install the various packages listed in package.json

Background

Running and Testing Your Program

In repl.it, you can run your code by hitting the 'Run' button at the top.

Running your code outside of repl.it

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. We converted the assignment examples into an initial set of tests in index.test.js.

To run the tests, switch to the 'Shell' tab in repl.it and type npm test.

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: 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 2: 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 3: Mapping

Write a function parseEmails(strings). This function takes in an Array of strings (strings), where each string is expected to be in the format 'First Last <Email>' (to keep this simple, we will assume names with a single given name followed by a family name -- in a real application we should be a bit less Euro-centric...). So, for example, my name and address would look like 'Christopher Andrews <candrews@middlebury.edu>'. For each such string, the function should return a JavaScript object with fields for first, last, and email. My string should be transformed to the object {first:'Christopher', last: 'Andrews', email:'candrews@middlebury.edu'} (note that the '<' and '>' have been stripped from the email address).

There are several wrinkles. First, your solution must use the map function. Second, while the function is designed to accept an array of strings, it should also accept a single string. Your function will need to detect this and do the right thing (the output will always be an Array, however). Finally, if a string is malformed, instead of returning an object, the function should return null (we are only worried about the structure, you don't need to validate the email address in any way). So, for example, parseEmails(["Jodi Whittaker <jwhittaker@prydon.edu>", "Peter Capaldi pcapaldi@prydon.edu"]) should return [{first:'Jodi', last:'Whittaker', email:'jwhittaker@prydon.edu'}, null].

Part 4: 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 5: 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 (this needs to be a deep copy, that creates copies of the window objects as well to avoid changing the original data). 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(n2)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 6: Reflection

In the README.md file, I would like you to write a brief reflection about your assignment.

If it meets the requirements (passes all of the automated checks), write how comfortable with your solution. Did you use good development practices, or is it a hacky, last minute solution. Do you understand your own solution? How much effort did you put into it? Do you feel like you could do a similar assignment with more ease?

If it doesn't yet meet the requirements, what are you struggling with? What does it not do correctly? What have you done to try to correct it? How hard have you tried to correct it? How much effort have you put into the entire assignment?

Put a date on the reflection and if you do any revisions, add another dated reflection below it.

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:

  1. Start by running git status in the terminal in the assignment directory to see how your modified files are reported.
  2. 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.
  3. Run git status again to see the how staged files are reported.
  4. 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.
  5. 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.


Last updated 03/05/2021