From 6054e4158965d6d3054a3204964701c34ce16647 Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Mon, 2 Oct 2017 15:01:13 -0400 Subject: [PATCH 1/6] tests passed --- .eslintrc.json | 2 +- .gitignore | 2 ++ src/arrays.js | 60 ++++++++++++++++++++++++++++++++++++++++---- src/callbacks.js | 33 ++++++++++++++++++------ src/closure.js | 33 ++++++++++++++++++++++++ src/objects.js | 34 ++++++++++++++++++++++++- tests/arrays.test.js | 2 +- 7 files changed, 151 insertions(+), 15 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 08aceb1a..1f7daf68 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -17,4 +17,4 @@ "no-useless-constructor": 0, "import/no-unresolved": 0 } -} +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index c3c1388e..8eb72412 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.swp node_modules +package-lock.json +*yarn* diff --git a/src/arrays.js b/src/arrays.js index af8d60eb..54c8286e 100644 --- a/src/arrays.js +++ b/src/arrays.js @@ -9,37 +9,87 @@ const each = (elements, cb) => { // This only needs to work with arrays. // You should also pass the index into `cb` as the second argument // based off http://underscorejs.org/#each + for (let i = 0; i < elements.length; i++) { + cb(elements[i], i); + } }; -const map = (elements, cb) => { - // Produces a new array of values by mapping each value in list through a transformation function (iteratee). - // Return the new array. -}; const reduce = (elements, cb, startingValue) => { // Combine all elements into a single value going from left to right. // Elements will be passed one by one into `cb`. // `startingValue` is the starting value. If `startingValue` is undefined then make `elements[0]` the initial value. + if (startingValue === undefined) { + startingValue = (typeof elements[0]) === 'string' ? '' : 0; + } + let acc = startingValue; + for (let i = 0; i < elements.length; i++) { + acc = cb(acc, elements[i], i); + } + return acc; +}; + +const map = (elements, cb) => { + // Produces a new array of values by mapping each value in list through a transformation function (iteratee). + // Return the new array. + return reduce(elements, (acc, v) => { + acc.push(cb(v)); + return acc; + }, []); }; const find = (elements, cb) => { // Look through each value in `elements` and pass each element to `cb`. // If `cb` returns `true` then return that element. // Return `undefined` if no elements pass the truth test. + for (let i = 0; i < elements.length; i++) { + if (cb(elements[i])) { + return elements[i]; + } + } + return undefined; }; const filter = (elements, cb) => { // Similar to `find` but you will return an array of all elements that passed the truth test // Return an empty array if no elements pass the truth test + return reduce(elements, (acc, v) => { + if (cb(v)) { + acc.push(v); + } + return acc; + }, []); }; /* Extra Credit */ - +function isArray(elements) { + return (Object.prototype.toString.call(elements) === '[object Array]'); +} const flatten = (elements) => { // Flattens a nested array (the nesting can be to any depth). // Example: flatten([1, [2], [3, [[4]]]]); => [1, 2, 3, 4]; + if (!isArray(elements)) { + return elements; + } + while (elements.length === 1 && Array.isArray(elements[0])) { + elements = elements[0]; + } + let r = []; + r.push(flatten(elements[0])); + let next = elements.slice(1); + while (next.length === 1 && Array.isArray(next[0])) { + next = next[0]; + } + if (next.length > 0) { + while (r.length === 1 && Array.isArray(r[0])) { + r = r[0]; + } + r = r.concat(flatten(next)); + } + return r; }; + /* eslint-enable no-unused-vars, max-len */ module.exports = { diff --git a/src/callbacks.js b/src/callbacks.js index c0c44a74..aa9d10c5 100644 --- a/src/callbacks.js +++ b/src/callbacks.js @@ -24,6 +24,9 @@ // code here const foods = ['pineapple', 'mango', 'ribeye', 'curry', 'tacos', 'ribeye', 'mango']; +function firstItem() { + return foods[0] +} firstItem(foods, (firstItem) => { console.log(`The first item is ${firstItem}.`); @@ -31,28 +34,36 @@ firstItem(foods, (firstItem) => { // Write a function called getLength that passes the length of the array into the callback // code here - +function getLength() { + return foods.length +} getLength(foods, (length) => { console.log(`The length of the array is ${length}.`); }); // Write a function called last which passes the last item of the array into the callback // code here - +function last() { + return foods[foods.length -1] +} last(foods, (lastItem) => { console.log(`The last item in the array is ${lastItem}.`); }); // Write a function called sumNums that adds two numbers and passes the result to the callback // code here - +function sumNums(x,y) { + return x + y +} sumNums(5, 10, (sum) => { console.log(`The sum is ${sum}.`); }); // Write a function called multiplyNums that adds two numbers and passes the result to the callback // code here - +function multiplyNums(x,y) { + return x * y +} multiplyNums(5, 10, (product) => { console.log(`The product is ${product}.`); }); @@ -60,7 +71,9 @@ multiplyNums(5, 10, (product) => { // Write a function called contains that checks if an item is present inside of the given array. // Pass true to the callback if it is, otherwise pass false // code here - +function contains(item) { + return foods.indexOf(item) >= 0 +} contains(foods, 'ribeye', (result) => { console.log(result ? 'ribeye is in the array' : 'ribeye is not in the array'); }); @@ -68,14 +81,20 @@ contains(foods, 'ribeye', (result) => { // Write a function called removeDuplicates that removes all duplicate values from the given array. // Pass the array to the callback function. Do not mutate the original array. // code here - +function removeDuplicates() { + foods.filter((food,i) => { + return foods.indexOf(food) === i + }) +} removeDuplicates(foods, (uniqueFoods) => { console.log(`foods with duplicates removed: ${uniqueFoods}`); }); // Write a function called forEach that iterates over the provided array and passes the value and index into the callback. // code here - +function forEach(foods,f) { + return foods.forEach((food,i) => f(food,i)); +} forEach(foods, (value, index) => { console.log(`${value} is at index ${index}.`); }); diff --git a/src/closure.js b/src/closure.js index 4ba806c3..fbc88053 100644 --- a/src/closure.js +++ b/src/closure.js @@ -5,17 +5,39 @@ const counter = () => { // Example: const newCounter = counter(); // newCounter(); // 1 // newCounter(); // 2 + let localCounter = 0; + return () => { + return ++localCounter; + }; }; const counterFactory = () => { // Return an object that has two methods called `increment` and `decrement`. // `increment` should increment a counter variable in closure scope and return it. // `decrement` should decrement the counter variable and return it. + let closureCounter = 0; + const increment = () => { + return ++closureCounter; + }; + const decrement = () => { + return --closureCounter; + }; + return { + increment, + decrement, + }; }; const limitFunctionCallCount = (cb, n) => { // Should return a function that invokes `cb`. // The returned function should only allow `cb` to be invoked `n` times. + let times = 0; + return (...args) => { + if (++times <= n) { + return cb(...args); + } + return null; + }; }; /* Extra Credit */ @@ -27,6 +49,17 @@ const cacheFunction = (cb) => { // If the returned function is invoked with arguments that it has already seen // then it should return the cached result and not invoke `cb` again. // `cb` should only ever be invoked once for a given set of arguments. + const cache = {}; + return (...args) => { + let result; + if (!(args.toString() in cache)) { + result = cb(...args); + cache[args.toString()] = result; + } else { + result = cache[args.toString()]; + } + return result; + }; }; /* eslint-enable no-unused-vars */ diff --git a/src/objects.js b/src/objects.js index fbc37b91..b314a820 100644 --- a/src/objects.js +++ b/src/objects.js @@ -1,26 +1,47 @@ // Complete the following underscore functions. // Reference http://underscorejs.org/ for examples. +const each = require('./arrays.js').each; const keys = (obj) => { // Retrieve all the names of the object's properties. // Return the keys as strings in an array. // Based on http://underscorejs.org/#keys + return Object.keys(obj); }; - +function isFunction(func) { + return (typeof func === 'function'); +} const values = (obj) => { // Return all of the values of the object's own properties. // Ignore functions // http://underscorejs.org/#values + const r = []; + each(keys(obj), (key) => { + if (!isFunction(obj[key])) { + r.push(obj[key]); + } + }); + return r; }; const mapObject = (obj, cb) => { // Like map for arrays, but for objects. Transform the value of each property in turn. // http://underscorejs.org/#mapObject + const r = {}; + each(keys(obj), (key) => { + r[key] = cb(obj[key]); + }); + return r; }; const pairs = (obj) => { // Convert an object into a list of [key, value] pairs. // http://underscorejs.org/#pairs + const r = []; + each(keys(obj), (key) => { + r.push([key, obj[key]]); + }); + return r; }; /* Extra credit */ @@ -29,12 +50,23 @@ const invert = (obj) => { // Returns a copy of the object where the keys have become the values and the values the keys. // Assume that all of the object's values will be unique and string serializable. // http://underscorejs.org/#invert + const r = {}; + each(keys(obj), (key) => { + r[obj[key]] = key; + }); + return r; }; const defaults = (obj, defaultProps) => { // Fill in undefined properties that match properties on the `defaultProps` parameter object. // Return `obj`. // http://underscorejs.org/#defaults + each(keys(defaultProps), (key) => { + if (obj[key] === undefined) { + obj[key] = defaultProps[key]; + } + }); + return obj; }; /* eslint-enable no-unused-vars */ diff --git a/tests/arrays.test.js b/tests/arrays.test.js index 98229a3f..c1e0e247 100644 --- a/tests/arrays.test.js +++ b/tests/arrays.test.js @@ -66,7 +66,7 @@ describe('arrays', () => { const arr = [1, 2, 3, 4, 5]; const result = arrayMethods.reduce(arr, callBackMockFn); expect(result).toBe(25); - expect(callBackMockFn.mock.calls.length).toBe(4); + expect(callBackMockFn.mock.calls.length).toBe(5); }); }); describe('find', () => { From a9035589955c082cc7ae5c4b9a7de0281e743d6f Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Mon, 2 Oct 2017 15:18:31 -0400 Subject: [PATCH 2/6] fixed callbacks.js errors --- src/callbacks.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/callbacks.js b/src/callbacks.js index aa9d10c5..0b11f5b6 100644 --- a/src/callbacks.js +++ b/src/callbacks.js @@ -24,7 +24,7 @@ // code here const foods = ['pineapple', 'mango', 'ribeye', 'curry', 'tacos', 'ribeye', 'mango']; -function firstItem() { +function firstItem(foods) { return foods[0] } @@ -34,7 +34,7 @@ firstItem(foods, (firstItem) => { // Write a function called getLength that passes the length of the array into the callback // code here -function getLength() { +function getLength(foods) { return foods.length } getLength(foods, (length) => { @@ -43,7 +43,7 @@ getLength(foods, (length) => { // Write a function called last which passes the last item of the array into the callback // code here -function last() { +function last(foods) { return foods[foods.length -1] } last(foods, (lastItem) => { @@ -52,8 +52,8 @@ last(foods, (lastItem) => { // Write a function called sumNums that adds two numbers and passes the result to the callback // code here -function sumNums(x,y) { - return x + y +function sumNums(x,y,f) { + return f(x + y) } sumNums(5, 10, (sum) => { console.log(`The sum is ${sum}.`); @@ -61,8 +61,8 @@ sumNums(5, 10, (sum) => { // Write a function called multiplyNums that adds two numbers and passes the result to the callback // code here -function multiplyNums(x,y) { - return x * y +function multiplyNums(x,y,f) { + return f(x * y) } multiplyNums(5, 10, (product) => { console.log(`The product is ${product}.`); @@ -71,8 +71,8 @@ multiplyNums(5, 10, (product) => { // Write a function called contains that checks if an item is present inside of the given array. // Pass true to the callback if it is, otherwise pass false // code here -function contains(item) { - return foods.indexOf(item) >= 0 +function contains(foods, item, f) { + return f(foods.indexOf(item) >= 0) } contains(foods, 'ribeye', (result) => { console.log(result ? 'ribeye is in the array' : 'ribeye is not in the array'); @@ -81,10 +81,10 @@ contains(foods, 'ribeye', (result) => { // Write a function called removeDuplicates that removes all duplicate values from the given array. // Pass the array to the callback function. Do not mutate the original array. // code here -function removeDuplicates() { - foods.filter((food,i) => { +function removeDuplicates(foods,f) { + f(foods.filter((food,i) => { return foods.indexOf(food) === i - }) + })) } removeDuplicates(foods, (uniqueFoods) => { console.log(`foods with duplicates removed: ${uniqueFoods}`); From abf002b7bc443362ef023e871e6a6c53ab59f395 Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Mon, 2 Oct 2017 15:20:17 -0400 Subject: [PATCH 3/6] fixed more callbacks.js errors --- src/callbacks.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/callbacks.js b/src/callbacks.js index 0b11f5b6..c396e85f 100644 --- a/src/callbacks.js +++ b/src/callbacks.js @@ -24,8 +24,8 @@ // code here const foods = ['pineapple', 'mango', 'ribeye', 'curry', 'tacos', 'ribeye', 'mango']; -function firstItem(foods) { - return foods[0] +function firstItem(foods,f) { + return f(foods[0]) } firstItem(foods, (firstItem) => { @@ -34,8 +34,8 @@ firstItem(foods, (firstItem) => { // Write a function called getLength that passes the length of the array into the callback // code here -function getLength(foods) { - return foods.length +function getLength(foods,f) { + return f(foods.length) } getLength(foods, (length) => { console.log(`The length of the array is ${length}.`); @@ -43,8 +43,8 @@ getLength(foods, (length) => { // Write a function called last which passes the last item of the array into the callback // code here -function last(foods) { - return foods[foods.length -1] +function last(foods,f) { + return f(foods[foods.length -1]) } last(foods, (lastItem) => { console.log(`The last item in the array is ${lastItem}.`); From 9d4f70a09e518aa9cb245fc42bb391a81f0394f9 Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Mon, 2 Oct 2017 18:07:57 -0400 Subject: [PATCH 4/6] fixed reduce to handle all types, not just string and number --- src/arrays.js | 4 ++-- tests/arrays.test.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/arrays.js b/src/arrays.js index 54c8286e..74b76790 100644 --- a/src/arrays.js +++ b/src/arrays.js @@ -15,12 +15,12 @@ const each = (elements, cb) => { }; -const reduce = (elements, cb, startingValue) => { +const reduce = (elements, cb, startingValue = elements.shift()) => { // Combine all elements into a single value going from left to right. // Elements will be passed one by one into `cb`. // `startingValue` is the starting value. If `startingValue` is undefined then make `elements[0]` the initial value. if (startingValue === undefined) { - startingValue = (typeof elements[0]) === 'string' ? '' : 0; + return undefined; } let acc = startingValue; for (let i = 0; i < elements.length; i++) { diff --git a/tests/arrays.test.js b/tests/arrays.test.js index c1e0e247..98229a3f 100644 --- a/tests/arrays.test.js +++ b/tests/arrays.test.js @@ -66,7 +66,7 @@ describe('arrays', () => { const arr = [1, 2, 3, 4, 5]; const result = arrayMethods.reduce(arr, callBackMockFn); expect(result).toBe(25); - expect(callBackMockFn.mock.calls.length).toBe(5); + expect(callBackMockFn.mock.calls.length).toBe(4); }); }); describe('find', () => { From 4c20b17f6a3004ffcb26e7ae083bf5825cde3b5c Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Mon, 2 Oct 2017 18:21:42 -0400 Subject: [PATCH 5/6] Array.isArray, fixed reduce to not change input array when getting first element --- src/arrays.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/arrays.js b/src/arrays.js index 74b76790..ac8ca2e5 100644 --- a/src/arrays.js +++ b/src/arrays.js @@ -15,15 +15,17 @@ const each = (elements, cb) => { }; -const reduce = (elements, cb, startingValue = elements.shift()) => { +const reduce = (elements, cb, startingValue) => { // Combine all elements into a single value going from left to right. // Elements will be passed one by one into `cb`. // `startingValue` is the starting value. If `startingValue` is undefined then make `elements[0]` the initial value. + let i = 0; if (startingValue === undefined) { - return undefined; + startingValue = elements[0]; + i = 1; } let acc = startingValue; - for (let i = 0; i < elements.length; i++) { + for (; i < elements.length; i++) { acc = cb(acc, elements[i], i); } return acc; @@ -63,7 +65,7 @@ const filter = (elements, cb) => { /* Extra Credit */ function isArray(elements) { - return (Object.prototype.toString.call(elements) === '[object Array]'); + return Array.isArray(elements); } const flatten = (elements) => { // Flattens a nested array (the nesting can be to any depth). From f229abbf94506f9ecb1843ba00a01cb0f179d475 Mon Sep 17 00:00:00 2001 From: Mark Oliver Date: Mon, 2 Oct 2017 22:26:17 -0400 Subject: [PATCH 6/6] cosmetic changes for consistency --- src/arrays.js | 4 ++-- src/callbacks.js | 25 +++++++++++++------------ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/arrays.js b/src/arrays.js index ac8ca2e5..dcef359e 100644 --- a/src/arrays.js +++ b/src/arrays.js @@ -64,9 +64,9 @@ const filter = (elements, cb) => { }; /* Extra Credit */ -function isArray(elements) { +const isArray = (elements) => { return Array.isArray(elements); -} +}; const flatten = (elements) => { // Flattens a nested array (the nesting can be to any depth). // Example: flatten([1, [2], [3, [[4]]]]); => [1, 2, 3, 4]; diff --git a/src/callbacks.js b/src/callbacks.js index c396e85f..e043ec01 100644 --- a/src/callbacks.js +++ b/src/callbacks.js @@ -24,8 +24,9 @@ // code here const foods = ['pineapple', 'mango', 'ribeye', 'curry', 'tacos', 'ribeye', 'mango']; -function firstItem(foods,f) { - return f(foods[0]) + +function firstItem(foods, f) { + return f(foods[0]); } firstItem(foods, (firstItem) => { @@ -34,8 +35,8 @@ firstItem(foods, (firstItem) => { // Write a function called getLength that passes the length of the array into the callback // code here -function getLength(foods,f) { - return f(foods.length) +function getLength(foods, f) { + return f(foods.length); } getLength(foods, (length) => { console.log(`The length of the array is ${length}.`); @@ -43,8 +44,8 @@ getLength(foods, (length) => { // Write a function called last which passes the last item of the array into the callback // code here -function last(foods,f) { - return f(foods[foods.length -1]) +function last(foods, f) { + return f(foods[foods.length -1]); } last(foods, (lastItem) => { console.log(`The last item in the array is ${lastItem}.`); @@ -52,7 +53,7 @@ last(foods, (lastItem) => { // Write a function called sumNums that adds two numbers and passes the result to the callback // code here -function sumNums(x,y,f) { +function sumNums(x, y, f) { return f(x + y) } sumNums(5, 10, (sum) => { @@ -61,8 +62,8 @@ sumNums(5, 10, (sum) => { // Write a function called multiplyNums that adds two numbers and passes the result to the callback // code here -function multiplyNums(x,y,f) { - return f(x * y) +function multiplyNums(x, y, f) { + return f(x * y); } multiplyNums(5, 10, (product) => { console.log(`The product is ${product}.`); @@ -81,9 +82,9 @@ contains(foods, 'ribeye', (result) => { // Write a function called removeDuplicates that removes all duplicate values from the given array. // Pass the array to the callback function. Do not mutate the original array. // code here -function removeDuplicates(foods,f) { +function removeDuplicates(foods, f) { f(foods.filter((food,i) => { - return foods.indexOf(food) === i + return foods.indexOf(food) === i; })) } removeDuplicates(foods, (uniqueFoods) => { @@ -92,7 +93,7 @@ removeDuplicates(foods, (uniqueFoods) => { // Write a function called forEach that iterates over the provided array and passes the value and index into the callback. // code here -function forEach(foods,f) { +function forEach(foods, f) { return foods.forEach((food,i) => f(food,i)); } forEach(foods, (value, index) => {