Last active
May 22, 2024 19:49
-
-
Save Anna-Myzukina/222add06c2a53c7084ed7a3744b69700 to your computer and use it in GitHub Desktop.
functional-javascript-workshop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 15 | |
/*Fix this code! The callback should be called with all the users loaded. | |
The order of the users should match the order of supplied user ids. Because this | |
function is asynchronous, we do not care about its return value. | |
# Arguments | |
* userIds: an Array of numeric user ids. | |
* load: a Function used to load user objects. Expects a numeric id and a callback. | |
* The callback will be called with the result of loading the user with the specified id | |
* (either a user object or null). | |
* done: a Function that expects an Array of user objects (as retrieved from `load`). | |
*/ | |
function loadUsers(userIds, load, done) { | |
let users = [] | |
for (let i = 0; i < userIds.length; i++){ | |
load(userIds[i], (user) => { | |
if (user) { | |
users.push(user) | |
} | |
if (users.length === userIds.length) done(users) | |
}) | |
} | |
} | |
module.exports = loadUsers |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 5 | |
/*Task | |
Return a function that takes a list of valid users, and returns a function that returns true if all of the | |
supplied users exist in the original list of users. | |
You only need to check that the ids match | |
Arguments | |
* goodUsers: a list of valid users | |
Use array#some and Array#every to check every user | |
passed to your returned function exists in the array passed to the exported function | |
*/ | |
module.exports = function checkUsersValid(goodUsers) { | |
return function allUsersValid(submittedUsers) { | |
return submittedUsers.every(function(submittedUser) { | |
return goodUsers.some(function(goodUser) { | |
return goodUser.id === submittedUser.id | |
}) | |
}) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 4 | |
/*# Task | |
Use Array#filter to write a function called getShortMessages. | |
getShortMessages takes an array of objects with '.message' properties and returns an array of messages that are | |
less than < 50 characters long. | |
The function should return an array containing the messages themselves, without their containing object. | |
# Arguments | |
* messages: an Array of 10 to 100 random objects that look something like this: | |
{ | |
message: 'Esse id amet quis eu esse aute officia ipsum.' // random | |
} | |
*/ | |
function getShortMessages(messages) { | |
// SOLUTION GOES HERE | |
return messages.filter(function(messageWrapper){ | |
return messageWrapper.message.length < 50; | |
}).map(function(messageWrapper){ | |
return messageWrapper.message; | |
}); | |
} | |
module.exports = getShortMessages; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 3 | |
/*Task | |
Convert the following code from a for-loop to Array#map: | |
function doubleAll(numbers) { | |
var result = [] | |
for (var i = 0; i < numbers.length; i++) { | |
result.push(numbers[i] * 2) | |
} | |
return result | |
} | |
module.exports = doubleAll | |
# Arguments | |
numbers: An Array of 0 to 20 Integers between 0 and 9 | |
*/ | |
function doubleAll(numbers) { | |
// SOLUTION GOES HERE | |
return numbers.map(function(n){ | |
return n * 2; | |
}); | |
} | |
module.exports = doubleAll; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 6 | |
/*Task | |
Given an Array of strings, use Array#reduce to create an object that contains the number | |
of times each string occured in the array. Return the object directly (no need to console.log) | |
Arguments | |
* inputWords: An array of random Strings. | |
*/ | |
function countWords(inputWords) { | |
// SOLUTION GOES HERE | |
return inputWords.reduce(function(countMap, word){ | |
countMap[word] = ++countMap[word] || 1 | |
return countMap | |
}, {}) | |
} | |
module.exports = countWords | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 10 | |
/*Task | |
Use Function#bind to implement a logging function that allows you to namespace messages. | |
Your implementation should take a namespace string, and return a function that prints | |
messages to the console with the namespace prepended. | |
Make sure all arguments passed to the returned logging function are printed. | |
Print the output to the console directly | |
# Arguments | |
* namespace: a String to prepend to each message passed to the returned function. | |
*/ | |
module.exports = function(namespace) { | |
// SOLUTION GOES HERE | |
return console.log.bind(console, namespace) | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 13 | |
/*Task | |
Modify the recursive repeat function provided in the boilerplate, such that it does not block the | |
event loop (i.e. Timers and IO handlers canfire). This necessarily requires repeat to be asynchronous. | |
A timeout is queued to fire after 100 milliseconds, which will print the results of the test and exit the process. | |
repeat should release control of the event loop to allow the timeout to interrupt before all of the operations | |
complete. | |
Try to perform as many operations as you can before the timeout fires! | |
*/ | |
function repeat(operation, num) { | |
if (num <= 0) return | |
operation() | |
// release control every 10 or so | |
// iterations. | |
// 10 is arbitrary. | |
if (num % 10 === 0) { | |
setTimeout(function() { | |
repeat(operation, --num) | |
}) | |
} else { | |
repeat(operation, --num) | |
} | |
} | |
module.exports = repeat |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 8 | |
/**Task: | |
Write a function duckCount that returns the number of | |
arguments passed to it which have a property 'quack' defined directly on them. | |
Do not match values inherited from prototypes. | |
Example: | |
var notDuck = Object.create({quack: true}) | |
var duck = {quack: true} | |
duckCount(duck, notDuck) // 1 | |
# Arguments | |
* You will be passed 0-20 arguments. Each argument could be of any type with any properties. | |
* Some of these items will have a 'quack' property. | |
*/ | |
function duckCount() { | |
// SOLUTION GOES HERE | |
return Array.prototype.slice.call(arguments).filter(function(obj) { | |
return Object.prototype.hasOwnProperty.call(obj, 'quack') | |
}).length | |
} | |
module.exports = duckCount |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 17 | |
/*Task | |
In this challenge, we're going to implement a 'curry' function for an arbitrary number of arguments. | |
curryN will take two parameters: | |
* fn: The function we want to curry. | |
* n: Optional number of arguments to curry. If not supplied, `curryN` | |
* should use the fn's arity as the value for `n`. | |
*/ | |
function curryN(fn, n) { | |
// SOLUTION GOES HERE | |
n = n || fn.length | |
function curryMe (f) { | |
if (n <= 1) return fn (f) | |
return curryN(fn.bind(fn, f), n-1) | |
} | |
return curryMe | |
} | |
module.exports = curryN |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 18 | |
/* | |
# Task | |
Write a function that allows you to use Array.prototype.slice without using | |
slice.call or slice.apply to invoke it. | |
Normally you have to use slice with call or apply: | |
var slice = Array.prototype.slice | |
function() { | |
var args = slice.call(arguments) // this works | |
} | |
We want this to work: | |
var slice = yourFunction | |
function() { | |
var args = slice(arguments) // this works | |
} | |
Hints | |
* This is absolutely a one liner. | |
* Every JavaScript Function inherits methods such as call, apply and bind from the object `Function.prototype`. | |
* Function#call executes the value of `this` when it is invoked. Inside `someFunction.call()`, the value of `this` will be `someFunction`. | |
* Function.call itself is a function thus it inherits from `Function.prototype` | |
function myFunction() { | |
console.log('called my function') | |
} | |
Function.prototype.call.call(myFunction) // => "called my function" | |
*/ | |
module.exports = Function.call.bind(Array.prototype.slice) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 12 | |
/*Task | |
Override a specified method of an object with new functionality while still maintaining all of the old behaviour. | |
Create a spy that keeps track of how many times a function is called | |
*/ | |
function Spy(target, method) { | |
let spy = { count : 0 } | |
let oldFn = target[method] | |
target[method] = function (){ | |
spy.count++ | |
return oldFn.apply(this, arguments) | |
} | |
return spy | |
} | |
module.exports = Spy |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 1 | |
/** | |
# Task | |
Write a function that takes an input string and returns it uppercased. | |
## Arguments | |
input: a String of random words (lorem ipsum). | |
*/ | |
"use strict"; | |
function upperCaser(input) { | |
return input.toUpperCase(); | |
} | |
module.exports = upperCaser; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 2 | |
/*# Task | |
Implement a function that takes a function as its first argument, a number num as its second argument, | |
then executes the passed in function num times. | |
Use the boilerplate code given to you below to get started. Most/all future exercises will provide boilerplate. | |
# Arguments | |
operation: A Function, takes no arguments, returns no useful value. | |
num: the number of times to call `operation` | |
*/ | |
function repeat(func, num) { | |
// SOLUTION GOES HERE | |
if (num <=0 ) return; | |
func(); | |
return repeat(func, --num); | |
} | |
// Do not remove the line below | |
module.exports = repeat; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 11 | |
/*Task | |
Use Array#reduce to implement a simple version of Array#map. | |
# Expected Output | |
A function map that applies a function to each item in an array and collects the results in a new Array. | |
var nums = [1,2,3,4,5] | |
// `map` is your exported function | |
var output = map(nums, function double(item) { | |
return item * 2 | |
}) | |
console.log(output) // => [2,4,6,8,10] | |
# Arguments | |
* input: an arbitrary Array of any type. | |
* operation: an arbitrary Function which can be applied to items in `input`. | |
*/ | |
module.exports = function arrayMap(arr, fn, thisArg) { | |
return arr.reduce(function(acc, item, index, arr) { | |
acc.push(fn.call(thisArg, item, index, arr)) | |
return acc | |
}, []) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 9 | |
/*Task | |
Use partial application to create a function that fixes the first argument to console.log. i.e. Implement | |
a logging function that prepends | |
anamespace string to its output. | |
Your implementation should take a namespace String and return a function that prints messages to the console | |
with the namespace prepended. | |
You should use Function#apply to implement the partial application. | |
Make sure all arguments passed to the returned logging function are printed. | |
Print the output to the console directly | |
# Arguments | |
* namespace: a String to prepend to each message passed to the returned function | |
*/ | |
var slice = Array.prototype.slice | |
function logger(namespace) { | |
// SOLUTION GOES HERE | |
return function() { | |
let arr = Array.from(arguments) | |
console.log.apply(console, [namespace].concat(arr)) | |
} | |
} | |
module.exports = logger |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 16 | |
/*Task | |
Implement a recursive function that returns all of the unique dependencies, and sub-dependencies | |
of a module, sorted alphabetically. Dependencies should be printed as dependency@version e.g. []()'. | |
Multiple versions of the same module are allowed, but duplicates modules of the same version should be removed. | |
*/ | |
function getDependencies(tree) { | |
if (tree && tree.dependencies) { | |
let dependencies = tree.dependencies | |
let ans = [] | |
Object.keys(dependencies).forEach((key) => { | |
var moduleName = `${key}@${dependencies[key].version}` | |
ans = ans.concat([moduleName], getDependencies(dependencies[key])) | |
}) | |
return ans.filter((elmt, idx, arr) => arr.indexOf(elmt) === idx).sort() | |
} | |
return [] | |
} | |
module.exports = getDependencies |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 7 | |
/*Task | |
your reduce function will be passed an array of words, and a function, and an initial value which | |
will return an object containing the counts for each word found in the array. You don't need to implement | |
this functionality, it will be supplied to your reduce implementation. | |
For simplicity, your implementation of reduce need not replicate the behaviour of a reduce missing an | |
initial value. You may assume the initial value will always be supplied. | |
# Arguments | |
* arr: An Array to reduce over | |
* fn: Function to use as the reduction step. Like regular Array#reduce, this function must be passed | |
* previousValue, currentValue, index andthe array we're iterating over. | |
* init: Initial value of the reduction. Unlike Array#reduce, this value is required (and you may assume | |
* it will always be supplied). | |
*/ | |
function reduce(arr, fn, initial) { | |
// SOLUTION GOES HERE | |
if (arr.length == 0) return initial | |
return reduce(arr.slice(1, arr.length), fn, fn(initial,arr[0])) | |
} | |
module.exports = reduce |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Exercise 14 | |
/*Task | |
Modify the boilerplate below such that it uses a trampoline to continuously call itself synchronously. | |
You can assume that the operation passed to repeat does not take arguments (or they are already bound | |
to the function) and the return value is not important. | |
*/ | |
function repeat(operation, num) { | |
// Modify this so it doesn't cause a stack overflow! | |
return function() { | |
if (num <= 0) return | |
operation() | |
return repeat(operation, --num) | |
} | |
} | |
function trampoline(fn) { | |
// You probably want to implement a trampoline! | |
while(fn && typeof fn === 'function') { | |
fn = fn() | |
} | |
} | |
module.exports = function(operation, num) { | |
// You probably want to call your trampoline here! | |
trampoline(function() { | |
return repeat(operation, num) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment