process.argv
?process.argv[2]
. I'm getting back a string, but.replace(/"/g, '')
the string, even though I$input
is a global that gets injected into thelevelmeup
of building modules or taking thingsOh, I solved it totally differently:
function repeat(fn, n) { if (n <= 0) return; fn() return repeat(fn, --n) } repeat($operation, 5)
I do feel like this might be slightly less "clever" – and it
teaches good recursion principles (always check your end
condition first, always make sure your input converges on the end
condition).
The introduction could use a little more explanatory text as to
what a map is and why it's useful – something like the following:
"We often have to take a set of data and modify it before we
output it. For example, say we get this back from our database:
[{first: 'Brian', last: 'Brennan'}, {first: 'Alex', last:
'Sexton'}]
And we want a list of full names. We can do this with a for loop,
but we have to manually keep track of indices and push to a new
array on our own. A better way to handle this is by using amap
. A map
is a higher order function that operates each
element of an array and returns a new array with each element
transformed
So to get our list of full names, our mapping function would be:function fullName(item) { return item.first + ' ' + item.last }
And we'd call it like so: var fullNames = entries.map(fullName)
Notice that the map function returns a new array – it doesn't
modify the array in place. This is a very important principle in
functional programming: it's always better to take input and
return new output rather than modify things in place.
Your exercise is to take an array of numbers and return a new
array with each number replaced by itself * 2..."
I think this might be cleaner and help illustrate the power of
passing function references around
function double(n) { return n * 2 } console.log($input.map(double))
I think the solution could also then explain the power of
creating small, generically useful functions and then using them
in maps to transform sets of data.
Again, I like seperating out the functions rather than using
anonymous functions:
function isShort(txt) { return txt.length < 50 } function getMessage(obj) { return obj.message } function getShortMessages(arr) { return arr.map(getMessage).filter(isShort) } console.log(getShortMessages($input))
I think it better illustrates composition.
Some illumination for why a reduce is useful would be
good. Perhaps even something that explains it in terms of mapping:
"A reduce
(also known as a "fold left") is useful when you want
to take a bunch of values and turn it into just one. A good
example of this is taking the sum of a series:
[1,2,3,4,5].reduce(function sum(accum, value){ return accum + value }, 0) // 0 + 1 + 2 + 3 + 4 + 5 = 15
The first argument to the reduce
functor is known as theaccumulator
– this keeps track of a value as the functor
processes values. The second argument to .reduce
is known as
the seed value. This is useful for priming the accumulator
so
the functor doesn't have to do conditional checking against it to
see whether or not it's the first value.
It's important to choose the correct seed value for the operation
you are performing! For example, 0
works well for addition, but
if we wanted to get the factorial rather than the sum, we would
want to use 1
instead:
[1,2,3,4,5].reduce(function factorial(accum, value){ return accum * value }, 1) // 1 * 1 * 2 * 3 * 4 * 5 = 120
(Fun fact: this has to do with 0 being the identity value for the
addition operation and 1 being the identity value for the
multiplication operation [citation needed])"
Woah, the boilerplate is confusing to me! I think to properly
explain reduce
, the operation needs to be transparent – I think
part of doing reduce
properly involves selecting the properseed
. That's an important lesson to teach, and one that gets
obscured if the operation is opaque. It's also a lesson aboutreduce
but the boilerplate function says map
.
reduce
, I think.Array.prototype.map
exactly. The function signature in thefn.call
and fn.apply
unless weYeah, doing the arguments trick without explanation isn't
great. This should either be explained in an earlier lesson (along
with call and apply), or there should be a solution that doesn't
require it. I vote for the former. Here is my solution for
reference, though I think the published one is probably better
because Array.prototype.concat
is rad and I don't use it nearly
enough.
function logger(namespace) { return function logFn() { var args = [].slice.call(arguments) args.unshift(namespace) return console.log.apply(console, args) } } $test(logger)
Ah, I was confused by the naming – Spy
with an uppercase S – and
I was expecting to build a constructor. I guess in a way it is?
I'm torn on this one. My solution was this:
function Spy(object, methodName) { if (!(this instanceof Spy)) return new Spy(object, methodName) this.count = 0 var originalFn = object[methodName] object[methodName] = function secretAgent() { this.count++ return originalFn.apply(object, arguments) }.bind(this) } $test(Spy)
This is awesome. I think with some interstitial lessons about call
and apply
, maybe one about reduce
before implementing map
withreduce
, this can be a super rad workshopper! I'm definitely willing
to help with creating/editing the intro text to add more
theory/reasons behind why someone would want to use FP techniques in
JS – I think that's important. Thank you so much for getting this
started!
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4