Skip to content

Instantly share code, notes, and snippets.

@Anna-Myzukina
Last active May 22, 2024 19:49
Show Gist options
  • Save Anna-Myzukina/222add06c2a53c7084ed7a3744b69700 to your computer and use it in GitHub Desktop.
Save Anna-Myzukina/222add06c2a53c7084ed7a3744b69700 to your computer and use it in GitHub Desktop.
functional-javascript-workshop
//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
//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
})
})
}
}
//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;
//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;
//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
//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)
}
//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
//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
//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
//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)
//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
//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;
//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;
//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
}, [])
}
//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
//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
//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
//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