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..dcef359e 100644 --- a/src/arrays.js +++ b/src/arrays.js @@ -9,37 +9,89 @@ 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. + let i = 0; + if (startingValue === undefined) { + startingValue = elements[0]; + i = 1; + } + let acc = startingValue; + for (; 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 */ - +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]; + 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..e043ec01 100644 --- a/src/callbacks.js +++ b/src/callbacks.js @@ -25,34 +25,46 @@ const foods = ['pineapple', 'mango', 'ribeye', 'curry', 'tacos', 'ribeye', 'mango']; +function firstItem(foods, f) { + return f(foods[0]); +} + firstItem(foods, (firstItem) => { console.log(`The first item is ${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); +} 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(foods, f) { + return f(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, f) { + return f(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, f) { + return f(x * y); +} multiplyNums(5, 10, (product) => { console.log(`The product is ${product}.`); }); @@ -60,7 +72,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(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'); }); @@ -68,14 +82,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, f) { + f(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 */