Comp 110 Who Stole the Paper

Everyone at Compter Scifflin was having a wonderful time. . . until Dwight realized the 10 lb paper was MISSING. 

Michael Scott did not call the police because he knew he could solve this crime himself. Every person was isolated into the conference room one at a time and each person had their properties recorded into a Clue object which was then put into  a CSV data file. This is where you come in as the assistant to the data investigator, Michael Scott. 

Dwight told Michael he saw was a dark piece of hair by the fifth desk. You can use this to find your first clue... and so, your search to find the thief begins.

Michael's Clues About the Data File

Each Clue written has the following properties:

  • person - the name of the person the clue is written about
  • hairColor - the name of the hair color of the person
  • weapon - the item this person would use as a weapon
  • desk - the number of the person's desk
  • note - whatever the note the person had on their body
  • hasPencil - TRUE or FALSE: does this person have a pencil on their desk 

Learning Objectives

  1. Gain comfort and familiarity with writing iterative functions
  2. Practice the patterns learned in class for building filtering or mapping functions
  3. Practice generalizing functions through the use of parameters
  4. Compose more sophisticated, specific functions by making use of simpler, more generic functions

This assignment is divided into three parts (“Walk”, “Jog”, and “Sprint”) that increase in difficulty. Here’s an overview of what you’ll be creating for each part:

1.  (Walk) Clue 1: You will be writing two filtering functions and then using those two functions to solve for Clue 1. 

2.  (Jog) Clue 2: You will be writing a mixture of five functions that will be used to solve for Clue 2. 

3.  (Sprint) Find Thief: You will choose what function you must write based on a description to solve for the thief. 

Part 0. Starting the Dev Environment

As with in lecture, begin by opening up VSCode, ensuring your project is still open, and then running the following two commands in the VSCode Integrated Terminal:

  1. npm run pull
  2. npm start

The first command "pulls" the latest files needed for COMP110 into your repo, if any. The second command starts the development environment and will open your web browser to the local server. 

Part 1. Setting up an App

In the same directory used in lecture:

  1. Right click on the "src" folder
  2. Select "New Folder"
  3. Name the new folder: ps03-clue
    1. Note: capitalization and punctuation are important!
  4. Right click on the folder you just created
  5. Select "New File"
  6. Name the new file: index-app.ts
    1. Note: capitalization and punctuation are important!
    2. Note: the i in index is lowercase!
  7. You should see a CSV data file in the data/ folder of the project named office-data.csv. This will be the data file you use to test your code.

Part 2. Starting your Code

2.0 Honor Code Header

All problem sets begin with an Honor Code pledge. Notice this header is contained within block comment tags discussed in Lecture 1. You can copy paste the pledge below and fill in your name and ONYEN.

/** 
* Author: * 
* ONYEN: * 
* UNC Honor Pledge: I certify that no unauthorized assistance has been received 
* or given in the completion of this work. I certify that I understand and 
* could now rewrite on my own, without assistance from course staff, 
* the problem set code I am submitting. 
*/

2.1 Imports

The next step after ensuring the honor code header is to import the functions we'll use from the introcs library (i.e. printimage, and so on). The first line of code to add to your app is the following:

import { print, csvToArray } from "introcs";

2.2 The Clue class and main Function

Each clue will be modeled using the Clue class below. You should review classes and properties from lecture and the topics page for more on classes and objects.

class Clue {
    person: string = "";
    hairColor: string = "";
    weapon: string = "";
    desk: number = 0;
    note: string = "";
    hasPencil: boolean = false; 
}
export let main = async () => {   
    let data: Clue[] = await csvToArray("Clue Data", Clue);   
    print(data); // TODO: Remove this line once your data is printed   
    // TODO: Your function calls go inside of this block
}; 
// TODO: Define your functions here
main();

Notice that we are establishing a variable named data that is an array of Clue objects loaded from a CSV. You can find the office-data.csv in the data folder.

Clue 1 - Walk

The initial clue 0 comes from the fact that there was dark hair found at desk number 5. 

The way we will approach this problem is to break it down into simple functions for each step of this process, then use those simpler functions to solve for the complete clue.

1.1 filterOutBlonde - Declare and export a function named filterOutBlonde. Given an array of Clue objects as an argument, return an array of Clue objects whose hairColor property is NOT blonde. Test by calling this function from the main function and printing its results:

let filtered: Clue[] = filterOutBlonde(data);
print(filtered);

1.2 nth - Declare and export a function named nth. The purpose of this function is to return the n'th Clue of an array. The nth function is given two arguments: 1) an array of Clue objects, 2) an index number. The nth function must return a Clue object.

1.2 findClue1 - Declare and export a function named findClue1. The purpose of this function is to find the first clue. This function should be given an array of Clue objects as a parameter and return a string. The string this function returns should be the contents of the note property of the 5th non-blonde from the input data. (Note: remember, the 5th clue will have index 4 because we start indexing from 0!) Your findClue1 function should make use of both your filterOutBlonde function and the nth function by calling each. An example usage of this function from your main function:

let clue1: string = findClue1(data);
print("Clue 1: " + clue1);

Hint: Once you've found the 5th non-blonde Clue in findClue1, you will need to return its note property. How can you access its note property?

You have solved the Walk segment of this problem set when your findClue1 function returns a note containing the word "Gumby". Once you have this working, submit for grading to check to be sure you have all edge cases completed before continuing to find the next clue.

Clue 2 - Jog

Written into the first clue is a key number that will use to find the next clue. The number in Clue 1 represents a number that is above the average desk number for all Clue objects, the Clue you are looking for's desk # property is equal to the number found in Clue 1. 

Similar to the Walk exercise, in Jog you will write smaller functions that perform single tasks and then compose them together into more advanced functions. In order to solve for this clue, we will need to establish some arithmetic reducers for finding an average from an array of numbers. Then we'll build some more advanced filters.

2.1 sum - Declare and export a function named sum. The purpose of this function is to find the summation of an array of numbers. Given an array of numbers, this function should return the result of adding all numbers in the array together. An empty array should return 0. Example usage:

let x: number[] = [1,2,3];
print(sum(x)); // Prints 6

2.2 average - Declare and export a function named average. Given an array of numbers, the purpose of this function is to return the average value of the array. (Hint: You already know the total number of items in the array with the length property.)Example usage:

let x: number[] = [1,2,3];
print(average(x)); // Prints 2

2.3 filterAboveDeskNumber - Declare and export a function named filterAboveDeskNumber. Given an array of Clue objects as a first argument, and a desk number as a second argument, return an array of all Clue objects whose desk number property is greater than the given desk number. Example usage:

let filtered: Clue[] = filterAboveDeskNumber(data, 20);
print(filtered); // Only displays clues whose desk property is greater than 20

2.4 filterByHairColor - Declare and export a function named filterByHairColor. Given an array of Clue objects as a first argument, and a hairColor string as a second argument, return an array of all Clue objects whose hair color property is equal to the given color parameter. Notice this is exactly like the filterOutBlondes function you wrote in walk. This function, however, will be more generally useful because it uses a parameter to specify the hair color you want to filter by.

let filtered: Clue[] = filterByHairColor(data, "blue");
print(filtered); // Only displays clues whose hair color property is blue

2.5 mapToDeskNumber - Declare and export a function named mapToDeskNumber. Given an array of Clue objects as an argument, this function should return an array of numbers, and more specifically the desk numbers, of each Clue in the original array. 

let deskNumbers: number[] = mapToDeskNumber(data);
print(deskNumbers); // Only displays the desk numbers of each Clue

2.6 findClue2 - Now that you have all of the functions above, you're ready to solve for clue 2! Define and export a function named findClue2. Given an array of Clues, it should return the note property of the Clue in the array at the index from the number in Clue 1. The clue should have a brown hair color property and a desk number property above the average of all of the Clues in the input array. You should be able to complete this function by making use of the functions above. You can assume there will always be a Clue in the input array that meets these criteria. Hint: break this function down into a sequence of steps by initializing and using multiple intermediate variables step-by-step. To test your progress, print your intermediate variable values and return an empty string until your final result is computed. Example usage:

let clue2: string = findClue2(data);
print("Clue 2:" + clue2);

You have solved the Jog segment of this problem set when your findClue2 function returns a note containing the word "prank". Print it out to reveal the clue. Once you have this working, submit for grading to check to be sure you have all edge cases completed before continuing to the final step of finding THE THIEF.

Catching the Thief - Sprint

3.1 - findThief - Declare and export a function named findThief. Given an array of Clues, this function should return the name of a person who has a pencil on their desk and uses "chili" as a weapon. For this final segment of the problem set, you will need to write your own helper function(s) like we did for the first two clues to filter down the list of suspects to 1. We will not grade you on these helper functions, so you are free to experiment, we will only grade based on the correctness of the findThief function. Example usage:

let thief: string = findThief(data);
print("Thief: " + thief);

Hint #: to filter by weapon, define a filter function that evaluates the weapon property using the string method includes as shown lecture 4.

Once you've found the culprit, submit for final grading. Case closed? Great work!