Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3406079
created spec directory with underpants.spec.js file
alex-aaron May 11, 2025
31c986b
updated spec/underpants.spec.js tests for identity, typeOf, first, la…
alex-aaron May 11, 2025
072d46d
added assert, expect, should script to index.html
alex-aaron May 11, 2025
a3d15e2
updated edge cases in _.first instructions, updated var _ declaration…
alex-aaron May 11, 2025
8ce2789
updated _.first tests remove incorrrect exaple
alex-aaron May 11, 2025
24b0dc2
updated _.last instructions to remove incorrect example
alex-aaron May 11, 2025
604c908
updated spec/underpants.spec.js to include tests for contains, each, …
alex-aaron May 11, 2025
68f7151
updated _.each instructions in underpants.js to show example for hand…
alex-aaron May 11, 2025
dfa5962
updated spec/underpants.spec.js to include tests for extend, reduce, …
alex-aaron May 11, 2025
c10ad6d
corrected tests for _.first, and _.last... tests were passing even th…
alex-aaron May 11, 2025
e6016cf
updated test for _.every to remove mention of array input
alex-aaron May 11, 2025
d272eef
corrected _.some() test in spec/underpants.spec.js to use _.some meth…
alex-aaron May 12, 2025
554e6ef
removed solution code
alex-aaron May 12, 2025
d722fa3
updated test/underpants.spec.js test code for identity and typeof
alex-aaron May 12, 2025
6755105
updated spec/underpants.spec.js and test/underpants.spec.js to remove…
alex-aaron May 12, 2025
5a9ae65
removed additional string from test/underpants.spec.js _.first test
alex-aaron May 12, 2025
ed4130f
corrected camel casing of .typeOf in test code
alex-aaron May 12, 2025
9d66722
updated filter tests in test and spec directories to include optional…
alex-aaron May 12, 2025
a02d8c9
updated reject, partition, and map test code
alex-aaron May 12, 2025
1f8cf37
updated pluck, every, some tests in tests/underpants.spec.js and adde…
alex-aaron May 12, 2025
774629c
updated test/underpants.spec.js tests for reduce, extend and removed …
alex-aaron May 12, 2025
1b307ae
added post-test script back to package.json
alex-aaron May 12, 2025
e6bc2eb
added post-test script back to package.json
alex-aaron May 12, 2025
a4af5df
added .vscode/settings.json
alex-aaron May 16, 2025
b046c5a
added prettier script to package.json
alex-aaron May 16, 2025
89e80b5
updated mocha, chai, sinon, installed prettier, uninstalled istanbul
alex-aaron May 16, 2025
4120ebf
removed unnecessary string _.first unit test
alex-aaron May 26, 2025
3f385eb
corrected take in current value to current key in _.every tests in sp…
alex-aaron May 26, 2025
c05932a
updated _.unique tests in spec directory to check for invoking _.inde…
alex-aaron May 26, 2025
37f285c
added .prettierrc
alex-aaron May 28, 2025
70f02b2
updated tests
alex-aaron May 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"arrowParens": "always",
"bracketSpacing": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "all",
"jsxSingleQuote": true,
"useTabs": false
}
9 changes: 9 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",

"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
},
"files.insertFinalNewline": true
}
35 changes: 21 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
# Underpants

This activity is essentially a re-write of [underscore.js](http://underscorejs.org/).

## Why?

In the past exercises we've been writing a lot of loops over objects and arrays ourselves. Instead of doing that, we are going to write several fuctions to handle the looping for us. These functions are used every day by professional developers and we're well on our way to becoming professional developers!!

This means that if we're confused about how a function should work, we can check the underscore.js documentation linked above for help.

## Instructions
- Open up index.html in a web browser.
- Notice that all the tests are failing. :)
- Open up underpants.js in a text editor and follow the instructions.
- Make all the test pass!!
- Open underpants.html in a text editor to view the code that runs the tests.

- Open up index.html in a web browser.
- Notice that all the tests are failing. :)
- Open up underpants.js in a text editor and follow the instructions.
- Make all the test pass!!
- Open underpants.html in a text editor to view the code that runs the tests.

## Links and Resources

Expand All @@ -20,31 +23,35 @@ Some quick notes that may come in handy:
- [underscore documentation](http://underscorejs.org/).
- Many of the functions operate on "collections." They can take both arrays or
objects as their arguments and you need to be able to handle both cases.
- You can use `Array.isArray(obj)` to find out whether an object is an array.
- You can use `obj.length` to test if something is either a string or an
array.
- You can use `Array.isArray(obj)` to find out whether an object is an array.
- You can use `obj.length` to test if something is either a string or an
array.
- Javascript has a built-in `Math` object that provides some very useful
functions. [Math Documentation](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math).
- Within a function, you can use the `arguments` variable to access all the
parameters that were passed in even if they aren't named in the function
definition. This is useful if you don't know how many arguments are going to
be passed in in advance.
- You can count the arguments by using `arguments.length` and access each
argument using `arguments[i]`.
- The `arguments` object is very similar to an array, but note that it does
not support most array functions (such as `slice` or `push`). You can read
more about this [here](http://www.sitepoint.com/arguments-a-javascript-oddity/).
- You can count the arguments by using `arguments.length` and access each
argument using `arguments[i]`.
- The `arguments` object is very similar to an array, but note that it does
not support most array functions (such as `slice` or `push`). You can read
more about this [here](http://www.sitepoint.com/arguments-a-javascript-oddity/).

### Extra Credit:

See Instructor for instructions.

- defaults
- *once*
- _once_
- memoize
- delay
- shuffle

### Double Extra Credit:

See Instructor for instructions.

- once
- invoke
- sortBy
Expand Down
236 changes: 35 additions & 201 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,204 +1,38 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Underpants Library</title>
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-1.19.0.css">
<script src="https://code.jquery.com/qunit/qunit-1.19.0.js"></script>
<meta name="description" content="Underpants: functional functions for fun">
</head>
<body>
<div id="qunit"></div>
<script src="underpants.js"></script>
<script>
/* global _ */
QUnit.test("_.identity()", function(assert) {
assert.strictEqual( _.identity(14), 14, "Should handle numbers.");
assert.deepEqual( _.identity({a: "one"}), {a: "one"}, "Should handle objects.");
assert.strictEqual(_.identity("hello there"), "hello there", "Should handle strings.");
assert.deepEqual(_.identity([1,2,3]), [1,2,3], "Should handle arrays.");
});

QUnit.test("_.typeof()", function(assert) {
assert.strictEqual(_.typeOf("a"), "string", "Should handle strings.");
assert.strictEqual(_.typeOf(10), "number", "Should handle numbers.");
assert.strictEqual(_.typeOf([1,3]), "array", "Should handle arrays.");
assert.strictEqual(_.typeOf({a: "one"}), "object", "Should handle objects.");
assert.strictEqual(_.typeOf(false), "boolean", "Should handle booleans.");
assert.strictEqual(_.typeOf(undefined), "undefined", "Should handle undefined.");
assert.strictEqual(_.typeOf(null), "null", "Should handle null.");
assert.strictEqual(_.typeOf(function(){}), "function", "Should handle functions.");
});

QUnit.test("_.first()", function(assert){
assert.deepEqual(_.first(["a","b","c"]) ,"a", "Should return the first element if no numerical argument is given.");
assert.deepEqual(_.first(["a","b","c"],2) ,["a","b"], "Should accept an argument representing the number of items to include in the output.");
assert.deepEqual(_.first(["a","b","c"], -1) ,[], "Should return empty list if numerical argument is not a positive number.");
assert.deepEqual(_.first(["a","b","c"], 5) ,["a","b","c"], "Should return the whole array if numerical argument is greater than the array's length.");
assert.deepEqual(_.first({a:"b"}, 2), [], "Should return empty array if the array param is not an array.");
});

QUnit.test("_.last()", function(assert){
assert.deepEqual(_.last(["a","b","c"]) ,"c", "Should return the last element if no numerical argument is given.");
assert.deepEqual(_.last(["a","b","c"],2) ,["b","c"], "Should accept an argument representing the number of items to include in the output.");
assert.deepEqual(_.last(["a","b","c"], -1) ,[], "Should return empty list if numerical argument is not a positive number.");
assert.deepEqual(_.last(["a","b","c"], 5) ,["a","b","c"], "Should return the whole array if numerical argument is greater than the array's length.");
assert.deepEqual(_.last({a:"b"}, 2), [], "Should return empty array if the array param is not an array.");
});

QUnit.test("_.indexOf()", function(assert){
var inputData = ["a","b","c","d"];
assert.deepEqual(_.indexOf(inputData, "b") , 1, "Should return the correct index when an element is found.");
assert.deepEqual(_.indexOf(inputData.concat("b"), "b") , 1, "Should return the index of the first occurance of a found element.");
assert.deepEqual(_.indexOf(inputData, "e") , -1, "Should return -1 if the element is not found.");
assert.deepEqual(inputData, ["a","b","c","d"], "Should not have side effects.");
});

QUnit.test("_.contains()", function(assert){
var inputData = [1,"3",4,5,"a","4","b"];
assert.strictEqual(_.contains(inputData, "a") , true, "Should return true if a list contains an element.");
assert.strictEqual(_.contains(inputData, "c") , false, "Should return false if a list doesn't contain an element.");
assert.strictEqual(_.contains(inputData, 3) , false, "Should not convert types when checking.");
assert.deepEqual(inputData, [1,"3",4,5,"a","4","b"], "Should not have side effects.");
});

QUnit.test("_.each()", function(assert){
var inputArray = [1,2,3,4,5];
inputArray.ignoreMe = "this shouldn't show up";
var inputObject = {a:"1",b:"2",c:"3",d:"4"};

_.each(inputArray, function(e, i, a){
inputArray[i] = e*a.length;
});
assert.deepEqual(inputArray ,[5,10,15,20,25] , "Should handle arrays.");

_.each(inputObject, function(v, k, o){
inputObject[v] = k + Object.keys(o).length;
delete inputObject[k];
});
assert.deepEqual(inputObject,{1:"a4", 2:"b4", 3:"c4", 4:"d4"} , "Should handle Objects.");
});

QUnit.test("_.unique()", function(assert){
var inputData = ["a",1,1,"a","c",false,"b",5,"c",null, false, null];
assert.deepEqual(_.unique(inputData),["a",1,"c",false,"b",5,null], "Should return an array with no duplicates.");
assert.deepEqual(inputData, ["a",1,1,"a","c",false,"b",5,"c",null, false, null], "Should not have side effects.");
});

QUnit.test("_.filter()", function(assert){
var inputData = ["a",1,"b",2,"c",4];
assert.deepEqual(_.filter(inputData, function(e,i,a){
return typeof e === "string" && i < a.length/2;
}), ["a","b"], "Should filter elements in an array.");
assert.deepEqual(inputData, ["a",1,"b",2,"c",4], "Should not have side effects.");
});

QUnit.test("_.reject()", function(assert){
var inputData = ["a",1,"b",2,"c",4];
assert.deepEqual(_.reject(inputData, function(e,i,a){
return typeof e === "string" || i < a.length/2;
}), [2,4], "Should reject elements in an array.");
assert.deepEqual(inputData, ["a",1,"b",2,"c",4], "Should not have side effects.");
});

QUnit.test("_.partition()", function(assert){
var inputData = ["a",1,"b",2,"c",4];
assert.deepEqual(_.partition(inputData, function(e,i,a){
return typeof e === "string";
}), [["a","b","c"],[1,2,4]], "Should reject elements in an array.");
assert.deepEqual(inputData, ["a",1,"b",2,"c",4], "Should not have side effects.");
});

QUnit.test("_.map", function(assert){
var inputArray = ["a","b","c","d"];
var inputObject = {"a":1, "b":2, "c":3, "d":4};
assert.deepEqual(_.map(inputArray, function(e,i,a){
return e + i * a.length;
}), ["a0", "b4", "c8", "d12"], "Should map through arrays.");
assert.deepEqual(_.map(inputObject, function(v,k,o){
return k + v * Object.keys(o).length;
}), ["a4", "b8", "c12", "d16"], "Should map through Objects.");
assert.deepEqual([inputArray, inputObject],[["a","b","c","d"],{"a":1, "b":2, "c":3, "d":4}],"Should not have side effects.");
});

QUnit.test("_.pluck()", function(assert){
var inputData = [
{ name: "Ralph", age: 22},
{ name: "Jimmy", age: 13},
{ name: "Carla", age: 20}
];
assert.deepEqual(_.pluck(inputData, "name"), ["Ralph","Jimmy","Carla"], "Should pluck properties out of a list of objects.");
assert.deepEqual(inputData, [
{ name: "Ralph", age: 22},
{ name: "Jimmy", age: 13},
{ name: "Carla", age: 20}
], "Should not have side effects.");
});

QUnit.test("_.every()", function(assert){
var inputData = [2,4,6,7,8];
var inputDataTruthy = [1, [], true, "a"];
var inputDataFalsy = ["",0,false,null];
var inputObject = {a:"one",b:"two",c:"three"};
assert.deepEqual(_.every(inputData, function(v){
return v % 2 === 0 || v === 7;
}) , true, "Should return true when all iterations are true");
assert.deepEqual(_.every(inputData, function(v){
return v % 2 === 0;
}) , false, "Should return false when not all iterations are true");
assert.deepEqual(_.every(inputObject, function(v,k,o){
return ["aone3","btwo3","cthree3"].indexOf(k+v+Object.keys(o).length) !== -1;
}), true, "Should handle objects");
assert.deepEqual(_.every(inputDataTruthy), true, "Should return true for truthy results when no function is passed in.");
assert.deepEqual(_.every(inputDataFalsy), false, "Should return false for falsy results when no function is passed in.");
assert.deepEqual(inputData, [2,4,6,7,8], "Should not have side effects.");
});

QUnit.test("_.some()", function(assert){
var inputData = [2,4,6,7,8];
var inputDataTruthy = [1, [], true, "a"];
var inputDataFalsy = ["",0,false,null];
var inputObject = {a:"one",b:"two",c:"three"};
assert.deepEqual(_.some(inputData, function(v){
return v === 7;
}) , true, "Should return true when at least one iteration is true");
assert.deepEqual(_.some(inputData, function(v){
return v > 10;
}) , false, "Should return false when no iterations are true");
assert.deepEqual(_.some(inputObject, function(v,k,o){
return ["aone3","btwo3"].indexOf(k+v+Object.keys(o).length) !== -1;
}), true, "Should handle objects");
assert.deepEqual(_.some(inputDataTruthy), true, "Should return true for truthy results when no function is passed in.");
assert.deepEqual(_.some(inputDataFalsy), false, "Should return false for falsy results when no function is passed in.");
assert.deepEqual(inputData, [2,4,6,7,8], "Should not have side effects.");
});

QUnit.test("_.reduce()", function(assert){
var inputArray = [10,20,30,40];

assert.strictEqual(_.reduce(inputArray, function(memo, element, i){
return memo + element + i;
}, 10), 116, "Should work with an array and a seed");
assert.strictEqual(_.reduce(inputArray, function(memo, element, i){
return memo * element * (i+1);
}), 5760000, "Should work without a seed");
assert.strictEqual(_.reduce(inputArray, function(memo, element, i){
return memo * element * (i+1);
}, 0), 0, "Should work when seed is falsy");
assert.deepEqual(inputArray, [10,20,30,40], "Should not have side effects");
});

QUnit.test("_.extend()", function(assert){
var inputData = {a:"one", b:"two"};
_.extend(inputData, {c: "three", d: "four"});
assert.deepEqual(inputData, {a: "one",b:"two",c:"three",d:"four"}, "Should extend an object.");
inputData = {a:"one", b:"two"};
_.extend(inputData, {a: "three", d: "four"});
assert.deepEqual(inputData, {a: "three",b:"two",d:"four"} , "Should overwrite existing properties");
inputData = {a:"one", b:"two"};
_.extend(inputData);
assert.deepEqual(_.extend(inputData, {a:"three",c:"four"}, {d:"five",c:"six"}), {a:"three",b:"two",c:"six",d:"five"}, "Should handle any number of arguments.");
});
</script>
</body>
<head>
<meta charset="utf-8" />
<title>Underpants Library</title>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/OperationSpark/mocha-style@main/mocha.css"
/>
<script
src="https://cdn.jsdelivr.net/gh/OperationSpark/mocha-style@main/injectStyles.js"
runMocha
></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mocha/11.1.0/mocha.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chai/4.5.0/chai.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sinon.js/19.0.2/sinon.min.js"></script>
<meta
name="description"
content="Underpants: functional functions for fun"
/>
</head>
<body>
<div id="mocha"></div>
<script>
mocha.setup('bdd');
</script>

<script>
var assert = chai.assert;
var expect = chai.expect;
var should = chai.should();
</script>

<script src="underpants.js"></script>
<script src="spec/underpants.spec.js"></script>
</body>
</html>
Loading