From e3edc29afa601b0bcb64234f1216b60a50584df9 Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 00:35:16 -0500 Subject: [PATCH 01/19] Started skeleton for assignment --- 2013-02-03-assignment.md | 44 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 2013-02-03-assignment.md diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md new file mode 100644 index 0000000..f3e20a3 --- /dev/null +++ b/2013-02-03-assignment.md @@ -0,0 +1,44 @@ +# JavaScript Assignment + +All skeleton files are provided in the `assignments` folder. Please fork this repo to get started. For what forking means or how to fork a repo, please visit [this github help page][help] for a detailed explanation. + +Again, please feel free to post questions on [github Issues page][issues] for the repo. + +--- + +## Q1: Multiples of 3 and 5 + +### Hint + +--- + +## Q2: Implement Map + +### Hint + +--- + +## Q3: Implement Filter + +### Hint + +--- + +## Q4: Implement Reduce + +### Hint + +--- + +## Q5: Prototypal Inheritance + +### Hint + +--- + +## [Bonus] Async Waterfall + + + +[issues]: https://github.com/Duke-PL-Course/JavaScript/issues?state=open +[help]: https://help.github.com/articles/fork-a-repo From 666e2f455b8f0d637270a026906cc698fb1dfe2a Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 00:44:02 -0500 Subject: [PATCH 02/19] Added q1: multiples question --- 2013-02-03-assignment.md | 20 ++++++++++++++++++++ assignments/q1-multiples.js | 3 +++ 2 files changed, 23 insertions(+) create mode 100644 assignments/q1-multiples.js diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index f3e20a3..47a4bea 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -8,8 +8,28 @@ Again, please feel free to post questions on [github Issues page][issues] for th ## Q1: Multiples of 3 and 5 +Find the sum of all positive multiples of 3 or 5 below a specified number (`limit`). + +```javascript +function multiples(limit) { + // ... +} +``` + +Example Tests: + +```javascript +multiples(1) // 0 +multiples(10) // 23 +multiples(16) // 60 +``` + ### Hint +Numbers that are both multiples of 3 and 5 should only be counted once. For example, **15** should only be counted as once. + +Note that `limit` is **non-inclusive**, so if the limit was `15`, the list of numbers we would be interested in are `[3, 5, 6, 9]`. + --- ## Q2: Implement Map diff --git a/assignments/q1-multiples.js b/assignments/q1-multiples.js new file mode 100644 index 0000000..0efa132 --- /dev/null +++ b/assignments/q1-multiples.js @@ -0,0 +1,3 @@ +function multiples(limit) { + // ... +} From 2b951bda611a2c648c77fa455236d6dbbe7400f5 Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 00:57:04 -0500 Subject: [PATCH 03/19] Fixed some styling with q1, and added q2: map --- 2013-02-03-assignment.md | 31 +++++++++++++++++++++++++++---- assignments/q1-multiples.js | 2 +- assignments/q2-map.js | 3 +++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 assignments/q2-map.js diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index 47a4bea..56856e9 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -11,7 +11,7 @@ Again, please feel free to post questions on [github Issues page][issues] for th Find the sum of all positive multiples of 3 or 5 below a specified number (`limit`). ```javascript -function multiples(limit) { +function multiples (limit) { // ... } ``` @@ -19,9 +19,9 @@ function multiples(limit) { Example Tests: ```javascript -multiples(1) // 0 -multiples(10) // 23 -multiples(16) // 60 +multiples(1); // 0 +multiples(10); // 23 +multiples(16); // 60 ``` ### Hint @@ -34,8 +34,29 @@ Note that `limit` is **non-inclusive**, so if the limit was `15`, the list of nu ## Q2: Implement Map +The `map` function is one that we saw in Ruby's [`Enumerable`][enumerable] module, and we've seen it in [underscore.js][underscore] as well. We will continue to see it again and again, especially when we explore some of the more functional languages. Because it is such an essential function, we want you to understand how it works by implementing it. In addition, you'll see that the next two questions are in the same vein and ask you to implement two more common functions that are seen in functional languages. + +`map` will take an array `xxs` of length greater than or equal to 0, and apply a function `fn`, to all elements of the array. `fn` will take a single parameter, and it is the job of `map` to pass each element of the array to `fn`. What `map` will return is an array of the same length as the input; however, the elements of the array will be transformed by `fn`. Please note that you should not change the original values of the input array `xxs`; it should be treated as **immutable**. So, you should create a new array to be returned. + +```javascript +function map (xxs, fn) { + // ... +} +``` + +Example Tests: + +```javascript +map([1, 2, 3, 4, 5], function (x) { return x * 2; }); // [2, 4, 6, 8, 10] +map(['foo', 'bar', 'baz'], function (x) { return x + ' is awesome'; }); // ['foo is awesome', 'bar is awesome', 'baz is awesome'] +``` + ### Hint +`map`'s job is basically to iterate over all elements of an array, applying a function to each, and returning the collective result as a new array. Don't try to over think this one. + +Some implementations of `map` in functional languages prefer a recursive implementation involving pattern matching; however, in JavaScript, it will most likely be easier to use an iterative implementation. The choice is ultimately up to you, however. + --- ## Q3: Implement Filter @@ -60,5 +81,7 @@ Note that `limit` is **non-inclusive**, so if the limit was `15`, the list of nu +[enumerable]: http://ruby-doc.org/core-1.9.3/Enumerable.html +[underscore]: http://underscorejs.org [issues]: https://github.com/Duke-PL-Course/JavaScript/issues?state=open [help]: https://help.github.com/articles/fork-a-repo diff --git a/assignments/q1-multiples.js b/assignments/q1-multiples.js index 0efa132..ebe5e18 100644 --- a/assignments/q1-multiples.js +++ b/assignments/q1-multiples.js @@ -1,3 +1,3 @@ -function multiples(limit) { +function multiples (limit) { // ... } diff --git a/assignments/q2-map.js b/assignments/q2-map.js new file mode 100644 index 0000000..89cf22e --- /dev/null +++ b/assignments/q2-map.js @@ -0,0 +1,3 @@ +function map (xxs, fn) { + // ... +} From 9218d9d4988e94f2dd9684a2f86dfce652609de6 Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 01:01:05 -0500 Subject: [PATCH 04/19] Added an example to map and added =>'s for output --- 2013-02-03-assignment.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index 56856e9..175d604 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -19,9 +19,9 @@ function multiples (limit) { Example Tests: ```javascript -multiples(1); // 0 -multiples(10); // 23 -multiples(16); // 60 +multiples(1); // => 0 +multiples(10); // => 23 +multiples(16); // => 60 ``` ### Hint @@ -47,8 +47,8 @@ function map (xxs, fn) { Example Tests: ```javascript -map([1, 2, 3, 4, 5], function (x) { return x * 2; }); // [2, 4, 6, 8, 10] -map(['foo', 'bar', 'baz'], function (x) { return x + ' is awesome'; }); // ['foo is awesome', 'bar is awesome', 'baz is awesome'] +map([1, 2, 3, 4, 5], function (x) { return x * 2; }); // => [2, 4, 6, 8, 10] +map(['foo', 'bar', 'baz'], function (x) { return x + ' is awesome'; }); // => ['foo is awesome', 'bar is awesome', 'baz is awesome'] ``` ### Hint From 2bc3f42fcff4eee665cae0ea6c06029e482cd2b6 Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 01:14:43 -0500 Subject: [PATCH 05/19] Added q3: filter and hint about typing for map --- 2013-02-03-assignment.md | 25 +++++++++++++++++++++++++ assignments/q3-filter.js | 3 +++ 2 files changed, 28 insertions(+) create mode 100644 assignments/q3-filter.js diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index 175d604..7831322 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -49,6 +49,7 @@ Example Tests: ```javascript map([1, 2, 3, 4, 5], function (x) { return x * 2; }); // => [2, 4, 6, 8, 10] map(['foo', 'bar', 'baz'], function (x) { return x + ' is awesome'; }); // => ['foo is awesome', 'bar is awesome', 'baz is awesome'] +map([1, 2, 3], function (x) { return 'and a ' + x; }); // => ['and a 1', 'and a 2', 'and a 3'] ``` ### Hint @@ -57,12 +58,36 @@ map(['foo', 'bar', 'baz'], function (x) { return x + ' is awesome'; }); // => [' Some implementations of `map` in functional languages prefer a recursive implementation involving pattern matching; however, in JavaScript, it will most likely be easier to use an iterative implementation. The choice is ultimately up to you, however. +The type of the returned array does not need to match that of the input array. + --- ## Q3: Implement Filter +`filter`, like `map`, is a very common function in functional languages. Filter will look through each value in an array `xxs`, similarly to `map`, but instead of returning an array of transformed values, an array of values that meet the given criteria will be returned. The criteria is defined by a function `condition` that is passed as a paremeter, and the return value of `condition` will be a boolean, or something that can be evaluated in an `if` statement. + +The contents of the input array `xxs` should remain **immutable**, and the order of the output array should be [stable][] with respect to the input array. In other words, the order should be relatively the same, even if some items are omitted: things should not be shuffled around. + +```javascript +function filter (xxs, condition) { + // ... +} +``` + +Example Tests: + +```javascript +filter([1, 2, 3, 4, 5], function (x) { return x % 2 === 0; }); // => [2, 4] +filter([1, 2, 3, 4, 5], function (x) { return x % 2 === 1; }); // => [1, 3, 5] +filter(['I', 'am', 'fat', 'catz'], function (x) { return x.length >= 3; }); // => ['fat', 'catz'] +``` + ### Hint +Much of the same structure used in `map` can be used in `filter`. + +The returned value of `condition(x)`, where `x` is a single element in the input array, will be an expression that can be evaluated in an `if` statement. + --- ## Q4: Implement Reduce diff --git a/assignments/q3-filter.js b/assignments/q3-filter.js new file mode 100644 index 0000000..efbdf94 --- /dev/null +++ b/assignments/q3-filter.js @@ -0,0 +1,3 @@ +function filter (xxs, condition) { + // ... +} From 22811f6b5e2ca0d59cac59aae8c08795ef0a022a Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 01:18:38 -0500 Subject: [PATCH 06/19] Changed xxs -> xs --- 2013-02-03-assignment.md | 10 +++++----- assignments/q2-map.js | 2 +- assignments/q3-filter.js | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index 7831322..63ad8c4 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -36,10 +36,10 @@ Note that `limit` is **non-inclusive**, so if the limit was `15`, the list of nu The `map` function is one that we saw in Ruby's [`Enumerable`][enumerable] module, and we've seen it in [underscore.js][underscore] as well. We will continue to see it again and again, especially when we explore some of the more functional languages. Because it is such an essential function, we want you to understand how it works by implementing it. In addition, you'll see that the next two questions are in the same vein and ask you to implement two more common functions that are seen in functional languages. -`map` will take an array `xxs` of length greater than or equal to 0, and apply a function `fn`, to all elements of the array. `fn` will take a single parameter, and it is the job of `map` to pass each element of the array to `fn`. What `map` will return is an array of the same length as the input; however, the elements of the array will be transformed by `fn`. Please note that you should not change the original values of the input array `xxs`; it should be treated as **immutable**. So, you should create a new array to be returned. +`map` will take an array `xs` of length greater than or equal to 0, and apply a function `fn`, to all elements of the array. `fn` will take a single parameter, and it is the job of `map` to pass each element of the array to `fn`. What `map` will return is an array of the same length as the input; however, the elements of the array will be transformed by `fn`. Please note that you should not change the original values of the input array `xs`; it should be treated as **immutable**. So, you should create a new array to be returned. ```javascript -function map (xxs, fn) { +function map (xs, fn) { // ... } ``` @@ -64,12 +64,12 @@ The type of the returned array does not need to match that of the input array. ## Q3: Implement Filter -`filter`, like `map`, is a very common function in functional languages. Filter will look through each value in an array `xxs`, similarly to `map`, but instead of returning an array of transformed values, an array of values that meet the given criteria will be returned. The criteria is defined by a function `condition` that is passed as a paremeter, and the return value of `condition` will be a boolean, or something that can be evaluated in an `if` statement. +`filter`, like `map`, is a very common function in functional languages. Filter will look through each value in an array `xs`, similarly to `map`, but instead of returning an array of transformed values, an array of values that meet the given criteria will be returned. The criteria is defined by a function `condition` that is passed as a paremeter, and the return value of `condition` will be a boolean, or something that can be evaluated in an `if` statement. -The contents of the input array `xxs` should remain **immutable**, and the order of the output array should be [stable][] with respect to the input array. In other words, the order should be relatively the same, even if some items are omitted: things should not be shuffled around. +The contents of the input array `xs` should remain **immutable**, and the order of the output array should be [stable][] with respect to the input array. In other words, the order should be relatively the same, even if some items are omitted: things should not be shuffled around. ```javascript -function filter (xxs, condition) { +function filter (xs, condition) { // ... } ``` diff --git a/assignments/q2-map.js b/assignments/q2-map.js index 89cf22e..a01bc71 100644 --- a/assignments/q2-map.js +++ b/assignments/q2-map.js @@ -1,3 +1,3 @@ -function map (xxs, fn) { +function map (xs, fn) { // ... } diff --git a/assignments/q3-filter.js b/assignments/q3-filter.js index efbdf94..757f6f1 100644 --- a/assignments/q3-filter.js +++ b/assignments/q3-filter.js @@ -1,3 +1,3 @@ -function filter (xxs, condition) { +function filter (xs, condition) { // ... } From 542e90bb776873c33fe0680e93143ce98bae9817 Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 01:45:34 -0500 Subject: [PATCH 07/19] Added reduce --- 2013-02-03-assignment.md | 23 +++++++++++++++++++++++ assignments/q4-reduce.js | 4 ++++ 2 files changed, 27 insertions(+) create mode 100644 assignments/q4-reduce.js diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index 63ad8c4..704292b 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -92,8 +92,31 @@ The returned value of `condition(x)`, where `x` is a single element in the input ## Q4: Implement Reduce +`reduce` is a slightly tricker function than the previous two, because it requires keeping track of something in addition to having an optional **seed value**, also known as the initial value. The basic duty of `reduce` is to boil down a list of elements into a single element. Even though this sounds mundane, it can be used very creatively to implement much more complex functionality. + +`reduce` takes a list `xs`, an iterating function `fn`, and an optional `seed`. The list `xs` will be an arbitrary list of elements. The iterating function `fn`, will be a function that takes in two parameters, the first of which being the accumulating `seed` value. The `seed` can be initially provided in the function call to `reduce`; however, if it is not specified, the first value of the list should be used as the seed. Each successive step in `reduce` will essentially replace what was originally the `seed`, with what is returned from the function `fn`. Hopefully, the examples below will clarify. + +```javascript +function reduce (xs, fn, seed) { + // NOTE: that seed is optional; you should give the appropriate default in the function body + // ... +} +``` + +Example Tests: + +```javascript +reduce([1, 2, 3, 4, 5, 6], function (memo, x) { return memo + x; }); // => 21 +reduce([1, 2, 3, 4, 5, 6], function (memo, x) { return memo + x; }, 21); // => 42 +reduce([1, 2, 3], function (memo, x) { return memo.concat([x * 2]); }, []); // => [2, 4, 6] +``` + ### Hint +Choosing an appropirate default value for the `seed` may be tricky. The flow should be given seed -> first element -> undefined. So if an empty array is passed in, and no seed is provided, the result should be undefined. + +A memoizer should be kept that is initialized to the value of the `seed`, and then passed along to the function `fn` along with each successive element. The value of this memoizer should be being set at each step. + --- ## Q5: Prototypal Inheritance diff --git a/assignments/q4-reduce.js b/assignments/q4-reduce.js new file mode 100644 index 0000000..becc358 --- /dev/null +++ b/assignments/q4-reduce.js @@ -0,0 +1,4 @@ +function reduce (xs, fn, seed) { + // NOTE: that seed is optional; you should give the appropriate default in the function body + // ... +} From 48d8500c8689976258925ca166b47611e0126074 Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 11:11:34 -0500 Subject: [PATCH 08/19] Added description for bonus question --- 2013-02-03-assignment.md | 80 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index 704292b..e3f855d 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -127,9 +127,85 @@ A memoizer should be kept that is initialized to the value of the `seed`, and th ## [Bonus] Async Waterfall +Nested callback functions can get out of hand extremely quickly. There have been many attempts to solve this through different forms of control flow. One popular solution is the [async.js][] library. This library, in addition to providing some utility tools for collections and memoization, provides many useful control flow constructs. One of which is [`waterfall`][waterfall]. This function will run a series of callback functions, called tasks, consecutively (not in parallel), and it also allows you to pass arguments to the next tasks. Note that the structure of each task looks like the following: + +```javascript +// there can by any number of arguments, +// but the last argument must be a reference to the next task +function (arg1, arg2, ..., callback) { + // perform task + callback(null, 'foo', 'bar'); // call the next task + // if the first argument is not falsy, an error occured, + // and you should directly call the final callback with the error +} +``` + +In addition, the `waterfall` function is given a final `callback` function that should be called after the last task finishes. This callback will have a signature of: + +```javascript +function (err, arg1, arg2, ...) { +} +``` + +Because asynchronous tasks often result in some sort of error (poor connections, etc.), the `waterfall` function provides a means of breaking out early. The first argument when calling the callback should be falsy (usually null) if no error has occured. If that value ends up being truthy, however, then the `waterfall` implementation should directly call the final callback with the error value (the first parameter). So, the `waterfall` function should have the following signature: + +```javascript +/** + * @param tasks is an array of functions + * @param callback is the final function + */ +function waterfall(tasks, callback) { + // ... +} +``` + +This will hopefully be much clearer with some examples: + +```javascript +// example with non-erroring tasks +waterfall([ + // the first task will always have the signature function (callback) + function (callback) { + // do stuff + callback(null, 'one', 'two'); + }, + function (arg1, arg2, callback) { + callback(null, arg1 + '1', arg2 + '2'); + } +], function (err, result1, result2) { + // usually it is not idiomatic to return multiple result parameters; + // however, we want to show that it is possible + console.log(err, result1, result2); // > null "one1" "two2" +}); + +// example with an erroring tasks +waterfall([ + function (callback) { + if (true) { + // uh-oh, I error out + callback("oh nooooez"); + } else { + callback(null, 'would otherwise be foo'); + } + }, + // the second task should never be called + function (foo, callback) { + console.log("I'm never called"); + callback(null, foo + 'bar'); + } +], function (err, result) { + // usually it is not idiomatic to return multiple result parameters; + // however, we want to show that it is possible + console.log(err, result); // > "oh nooooez" undefined +}); +``` + +### Hint +[async.js]: https://github.com/caolan/async [enumerable]: http://ruby-doc.org/core-1.9.3/Enumerable.html -[underscore]: http://underscorejs.org -[issues]: https://github.com/Duke-PL-Course/JavaScript/issues?state=open [help]: https://help.github.com/articles/fork-a-repo +[issues]: https://github.com/Duke-PL-Course/JavaScript/issues?state=open +[underscore]: http://underscorejs.org +[waterfall]: https://github.com/caolan/async#waterfall From 48ce82ce82076f035ce08f8ce679df0ee0ec2b7f Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 11:12:15 -0500 Subject: [PATCH 09/19] removed extraneous comment from waterfall error example --- 2013-02-03-assignment.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index e3f855d..7f37fa8 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -194,8 +194,6 @@ waterfall([ callback(null, foo + 'bar'); } ], function (err, result) { - // usually it is not idiomatic to return multiple result parameters; - // however, we want to show that it is possible console.log(err, result); // > "oh nooooez" undefined }); ``` From cc4147f1f40208f4a12627b0edc9f0a3e288d4c1 Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Mon, 4 Feb 2013 11:13:50 -0500 Subject: [PATCH 10/19] Added skeleton for waterfall --- 2013-02-03-assignment.md | 4 ++-- assignments/bonus-waterfall.js | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 assignments/bonus-waterfall.js diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index 7f37fa8..5950d22 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -143,7 +143,7 @@ function (arg1, arg2, ..., callback) { In addition, the `waterfall` function is given a final `callback` function that should be called after the last task finishes. This callback will have a signature of: ```javascript -function (err, arg1, arg2, ...) { +function (err, result1, result2, ...) { } ``` @@ -154,7 +154,7 @@ Because asynchronous tasks often result in some sort of error (poor connections, * @param tasks is an array of functions * @param callback is the final function */ -function waterfall(tasks, callback) { +function waterfall (tasks, callback) { // ... } ``` diff --git a/assignments/bonus-waterfall.js b/assignments/bonus-waterfall.js new file mode 100644 index 0000000..ad1b2e0 --- /dev/null +++ b/assignments/bonus-waterfall.js @@ -0,0 +1,7 @@ +/** + * @param tasks is an array of functions + * @param callback is the final function + */ +function waterfall (tasks, callback) { + // ... +} From 780fc7c8e460dc84b5df780b98d5d264f308853e Mon Sep 17 00:00:00 2001 From: Yang Su Date: Thu, 7 Feb 2013 17:13:51 -0500 Subject: [PATCH 11/19] Added additional hint for reduce --- 2013-02-03-assignment.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index 5950d22..cdc24e9 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -113,10 +113,21 @@ reduce([1, 2, 3], function (memo, x) { return memo.concat([x * 2]); }, []); // = ### Hint -Choosing an appropirate default value for the `seed` may be tricky. The flow should be given seed -> first element -> undefined. So if an empty array is passed in, and no seed is provided, the result should be undefined. +Choosing an appropirate default value for the `seed` may be tricky. The flow should be given seed -> first element -> `undefined`. So if an empty array is passed in, and no seed is provided, the result should be `undefined`. A memoizer should be kept that is initialized to the value of the `seed`, and then passed along to the function `fn` along with each successive element. The value of this memoizer should be being set at each step. +To check for whether a seed value is passed in can be done with the following: +```javascript +function reduce (xs, fn, seed) { + if (typeof seed === 'undefined') { + // seed value was not passed in + } else { + // seed value was passed in + } +} + + --- ## Q5: Prototypal Inheritance From ab93a9d5dba34fa0a0c4ddbfc784dffbd7110f7f Mon Sep 17 00:00:00 2001 From: Yang Su Date: Thu, 7 Feb 2013 17:27:45 -0500 Subject: [PATCH 12/19] Removed Question 5 --- 2013-02-03-assignment.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index cdc24e9..cabe09f 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -127,13 +127,6 @@ function reduce (xs, fn, seed) { } } - ---- - -## Q5: Prototypal Inheritance - -### Hint - --- ## [Bonus] Async Waterfall @@ -212,6 +205,7 @@ waterfall([ ### Hint + [async.js]: https://github.com/caolan/async [enumerable]: http://ruby-doc.org/core-1.9.3/Enumerable.html [help]: https://help.github.com/articles/fork-a-repo From d430ed08ba7bddb5dc782ef19d05b6b1e5cfc3eb Mon Sep 17 00:00:00 2001 From: Yang Su Date: Thu, 7 Feb 2013 17:28:56 -0500 Subject: [PATCH 13/19] Update 2013-02-03-assignment.md Fixed Markdown --- 2013-02-03-assignment.md | 1 + 1 file changed, 1 insertion(+) diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index cabe09f..ddaffe3 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -126,6 +126,7 @@ function reduce (xs, fn, seed) { // seed value was passed in } } +``` --- From aee69438a075668de7f7573f9055fadfa0400aeb Mon Sep 17 00:00:00 2001 From: Yang Su Date: Thu, 7 Feb 2013 17:45:58 -0500 Subject: [PATCH 14/19] Added parallel implementation --- assignments/bonus-parallel.js | 70 +++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 assignments/bonus-parallel.js diff --git a/assignments/bonus-parallel.js b/assignments/bonus-parallel.js new file mode 100644 index 0000000..1e6e531 --- /dev/null +++ b/assignments/bonus-parallel.js @@ -0,0 +1,70 @@ +/** + * @param hash is an hash of names to functions + * @param callback is the final function + */ +function parallel(hash, callback) { + var i = 0; + var count = 0; + var retval = {}; + var cb = function(err, key, data) { + if (err) { + callback(err); + // replace callback with noop so the final + // callback will only be executed once + callback = function() {}; + } else { + i += 1; + retval[key] = data; + if (i === count) { + callback(null, retval); + } + } + }; + + // Count the number of functions + var key; + for (key in hash) { + if (hash.hasOwnProperty(key)) { + count += 1; + } + } + + // Dispatch functions + for (key in hash) { + if (hash.hasOwnProperty(key)) { + hash[key](function(err, data) { + cb(err, key, data); + }); + } + } +} + +// example with non-erroring tasks +parallel({ + first: function(callback) { + callback(null, { + piece: 'of data' + }); + }, + second: function(callback) { + callback(null, { + piece: 'of data' + }); + } +}, function(err, data) { + console.log(err, data); + // > null { first: { piece: 'of data' }, second: { piece: 'of data' } } +}); + +parallel({ + first: function(callback) { + callback(null, { + piece: 'of data' + }); + }, + second: function(callback) { + callback('well shit'); + } +}, function(err, data) { + console.log(err, data); // > well shit undefined +}); From b515524f201aaa26301f56e0c9da2893ff19e6ba Mon Sep 17 00:00:00 2001 From: Yang Su Date: Thu, 7 Feb 2013 18:14:34 -0500 Subject: [PATCH 15/19] Added point values to each problem --- 2013-02-03-assignment.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/2013-02-03-assignment.md b/2013-02-03-assignment.md index ddaffe3..bd020eb 100644 --- a/2013-02-03-assignment.md +++ b/2013-02-03-assignment.md @@ -6,7 +6,7 @@ Again, please feel free to post questions on [github Issues page][issues] for th --- -## Q1: Multiples of 3 and 5 +## Q1: Multiples of 3 and 5 (25%) Find the sum of all positive multiples of 3 or 5 below a specified number (`limit`). @@ -32,7 +32,7 @@ Note that `limit` is **non-inclusive**, so if the limit was `15`, the list of nu --- -## Q2: Implement Map +## Q2: Implement Map (25%) The `map` function is one that we saw in Ruby's [`Enumerable`][enumerable] module, and we've seen it in [underscore.js][underscore] as well. We will continue to see it again and again, especially when we explore some of the more functional languages. Because it is such an essential function, we want you to understand how it works by implementing it. In addition, you'll see that the next two questions are in the same vein and ask you to implement two more common functions that are seen in functional languages. @@ -62,7 +62,7 @@ The type of the returned array does not need to match that of the input array. --- -## Q3: Implement Filter +## Q3: Implement Filter (25%) `filter`, like `map`, is a very common function in functional languages. Filter will look through each value in an array `xs`, similarly to `map`, but instead of returning an array of transformed values, an array of values that meet the given criteria will be returned. The criteria is defined by a function `condition` that is passed as a paremeter, and the return value of `condition` will be a boolean, or something that can be evaluated in an `if` statement. @@ -90,7 +90,7 @@ The returned value of `condition(x)`, where `x` is a single element in the input --- -## Q4: Implement Reduce +## Q4: Implement Reduce (25%) `reduce` is a slightly tricker function than the previous two, because it requires keeping track of something in addition to having an optional **seed value**, also known as the initial value. The basic duty of `reduce` is to boil down a list of elements into a single element. Even though this sounds mundane, it can be used very creatively to implement much more complex functionality. @@ -130,7 +130,7 @@ function reduce (xs, fn, seed) { --- -## [Bonus] Async Waterfall +## [Bonus] Async Waterfall (50%) Nested callback functions can get out of hand extremely quickly. There have been many attempts to solve this through different forms of control flow. One popular solution is the [async.js][] library. This library, in addition to providing some utility tools for collections and memoization, provides many useful control flow constructs. One of which is [`waterfall`][waterfall]. This function will run a series of callback functions, called tasks, consecutively (not in parallel), and it also allows you to pass arguments to the next tasks. Note that the structure of each task looks like the following: @@ -203,8 +203,6 @@ waterfall([ }); ``` -### Hint - [async.js]: https://github.com/caolan/async From e9b041b00298571094ec0250ee42d2e54cc98180 Mon Sep 17 00:00:00 2001 From: Yang Su Date: Thu, 7 Feb 2013 17:45:43 -0500 Subject: [PATCH 16/19] JS Solutions --- assignments/bonus-waterfall.js | 55 ++++++++++++++++++++++++++++++++-- assignments/q1-multiples.js | 14 +++++++-- assignments/q2-map.js | 11 +++++-- assignments/q3-filter.js | 14 +++++++-- assignments/q4-reduce.js | 16 ++++++++-- 5 files changed, 100 insertions(+), 10 deletions(-) diff --git a/assignments/bonus-waterfall.js b/assignments/bonus-waterfall.js index ad1b2e0..357329e 100644 --- a/assignments/bonus-waterfall.js +++ b/assignments/bonus-waterfall.js @@ -2,6 +2,57 @@ * @param tasks is an array of functions * @param callback is the final function */ -function waterfall (tasks, callback) { - // ... +function waterfall(tasks, callback) { + var i = 0; + var cb = function(err) { + var newArgs; + if (err) { + callback(err); + } else { + i += 1; + newArgs = Array.prototype.slice.call(arguments); + if (i === tasks.length) { + callback.apply(null, newArgs); + } else { + newArgs.push(cb); + tasks[i].apply(null, newArgs.slice(1)); + } + } + }; + + tasks[i](cb); } + +// example with non-erroring tasks +waterfall([ + // the first task will always have the signature function (callback) + function (callback) { + // do stuff + callback(null, 'one', 'two'); + }, + function (arg1, arg2, callback) { + callback(null, arg1 + '1', arg2 + '2'); + } +], function (err, result1, result2) { + // usually it is not idiomatic to return multiple result parameters; + // however, we want to show that it is possible + console.log(err, result1, result2); // > null "one1" "two2" +}); + +waterfall([ + function (callback) { + if (true) { + // uh-oh, I error out + callback("oh nooooez"); + } else { + callback(null, 'would otherwise be foo'); + } + }, + // the second task should never be called + function (foo, callback) { + console.log("I'm never called"); + callback(null, foo + 'bar'); + } +], function (err, result) { + console.log(err, result); // > "oh nooooez" undefined +}); \ No newline at end of file diff --git a/assignments/q1-multiples.js b/assignments/q1-multiples.js index ebe5e18..9f183ca 100644 --- a/assignments/q1-multiples.js +++ b/assignments/q1-multiples.js @@ -1,3 +1,13 @@ -function multiples (limit) { - // ... +function multiples(limit) { + var sum = 0; + for (var i = 3; i < limit; i += 1) { + if (i % 3 === 0 || i % 5 === 0) { + sum += i; + } + } + return sum; } + +console.log(multiples(1)); +console.log(multiples(10)); +console.log(multiples(16)); diff --git a/assignments/q2-map.js b/assignments/q2-map.js index a01bc71..2c99498 100644 --- a/assignments/q2-map.js +++ b/assignments/q2-map.js @@ -1,3 +1,10 @@ -function map (xs, fn) { - // ... +function map(xs, fn) { + var retval = []; + for (var i = 0, len = xs.length; i < len; i += 1) { + retval.push(fn(xs[i])); + } + return retval; } +console.log(map([1, 2, 3, 4, 5], function (x) { return x * 2; })); // => [2, 4, 6, 8, 10] +console.log(map(['foo', 'bar', 'baz'], function (x) { return x + ' is awesome'; })); // => ['foo is awesome', 'bar is awesome', 'baz is awesome'] +console.log(map([1, 2, 3], function (x) { return 'and a ' + x; })); // => ['and a 1', 'and a 2', 'and a 3'] diff --git a/assignments/q3-filter.js b/assignments/q3-filter.js index 757f6f1..94244a4 100644 --- a/assignments/q3-filter.js +++ b/assignments/q3-filter.js @@ -1,3 +1,13 @@ -function filter (xs, condition) { - // ... +function filter(xs, condition) { + var retval = [], item; + for (var i = 0, len = xs.length; i < len; i += 1) { + item = xs[i]; + if (condition(item)) { + retval.push(item); + } + } + return retval; } +console.log(filter([1, 2, 3, 4, 5], function (x) { return x % 2 === 0; })); // => [2, 4] +console.log(filter([1, 2, 3, 4, 5], function (x) { return x % 2 === 1; })); // => [1, 3, 5] +console.log(filter(['I', 'am', 'fat', 'catz'], function (x) { return x.length >= 3; })); // => ['fat', 'catz'] diff --git a/assignments/q4-reduce.js b/assignments/q4-reduce.js index becc358..e8ca563 100644 --- a/assignments/q4-reduce.js +++ b/assignments/q4-reduce.js @@ -1,4 +1,16 @@ -function reduce (xs, fn, seed) { +function reduce(xs, fn, seed) { // NOTE: that seed is optional; you should give the appropriate default in the function body - // ... + var i = 0, len = xs.length; + if (typeof seed === 'undefined') { + seed = xs[0]; + i = 1; + } + + for (; i < len; i += 1) { + seed = fn(seed, xs[i]); + } + return seed; } +console.log(reduce([1, 2, 3, 4, 5, 6], function (memo, x) { return memo + x; })); // => 21 +console.log(reduce([1, 2, 3, 4, 5, 6], function (memo, x) { return memo + x; }, 21)); // => 42 +console.log(reduce([1, 2, 3], function (memo, x) { return memo.concat([x * 2]); }, [])); // => [2, 4, 6] From c46218605d1e841864acd6ca604b23cbf6b87a4a Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Tue, 26 Feb 2013 16:05:10 -0500 Subject: [PATCH 17/19] Added Review Problem --- 2013-02-26-javascript-review-problem.md | 14 ++++++++++++++ review/review.html | 22 ++++++++++++++++++++++ review/review.js | 3 +++ 3 files changed, 39 insertions(+) create mode 100644 2013-02-26-javascript-review-problem.md create mode 100644 review/review.html create mode 100644 review/review.js diff --git a/2013-02-26-javascript-review-problem.md b/2013-02-26-javascript-review-problem.md new file mode 100644 index 0000000..d9069aa --- /dev/null +++ b/2013-02-26-javascript-review-problem.md @@ -0,0 +1,14 @@ +# JavaScript Practice Review Problem + +## Identifying Child Elements + +The purpose of this problem is to learn a little bit about how the document object model (DOM) works along with event handlers, and to review closures. In this exercise, you'll be adding click handlers to items in a list. The behavior of the handler should be to pop up an alert that contains the position of the child. In other words, when clicking the first list item, it should pop up an alert with the contents `0` because it is the 0-th child. + +In the `./review/` directory, you will find two files: `review.html` and `review.js`. The first file is going to be the test environment for running your code. Modify the code in `review.js` to add these click handlers. + +## Hints + +* Use [`document.getElementById()`](http://www.w3schools.com/jsref/dom_obj_core_document.asp) to retrieve a DOM element +* Refer to the [DOM Node][] API for some helpful methods. + +[DOM Node]: http://www.w3schools.com/jsref/dom_obj_node.asp diff --git a/review/review.html b/review/review.html new file mode 100644 index 0000000..385b122 --- /dev/null +++ b/review/review.html @@ -0,0 +1,22 @@ + + + + JavaScript Review Problem: Identifying Child Elements + +

+
    + + + + diff --git a/review/review.js b/review/review.js new file mode 100644 index 0000000..d763076 --- /dev/null +++ b/review/review.js @@ -0,0 +1,3 @@ +(function () { + // ... +}()); From db0b396b8e2c601301028b6e70913ff6c97a3b3e Mon Sep 17 00:00:00 2001 From: Kevin Gao Date: Tue, 26 Feb 2013 16:17:28 -0500 Subject: [PATCH 18/19] Changed Style --- review/review.html | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/review/review.html b/review/review.html index 385b122..c0fd6b3 100644 --- a/review/review.html +++ b/review/review.html @@ -1,10 +1,36 @@ - JavaScript Review Problem: Identifying Child Elements + + JavaScript Review Problem: Identifying Child Elements + + -

    -
      +
      +

      +
        +