function each(collection, callback) {
if (Array.isArray(collection)) {
for (var i = 0; i < collection.length; i++) {
callback(collection[i]);
}
}
else {
for (var prop in collection) {
callback(collection[prop], prop, collection);
}
}
}
var array = [1, 2, 3, 4, 5];
function reduce(collection, callback, initial) {
var current = initial;
each(collection, function(current, e) {
current = callback(current, e);
})
return current;
}
console.log(reduce(array, function(a, b) { return a + b }, 0)); -->>> 0
I'm trying to rewrite the underscore each/reduce functions, and use the each function in reduce. I know that I have a mistake in there-- (current should not be in the each callback function) it should be just be
each(collection, function(e) {
current = callback(current, e);
})
and that returns 15 as it should, but why does it return 0 when you do add that current in there as a parameter? Shouldn't it just return NaN? As the last part of the loop will try to add 5 and undefined which is NaN.
The thing is that as soon as you add current to the parameter list of your callback function, you do have two variables - with the same name, but in different scopes. The one in the callback does shadow the one in the reduce function.
So your callback reducer is called with the element that each passed to your callback and undefined, but when assigning the result (NaN) to current it will just assign to the local variable of your each callback.
The outer current variable will stay completely unaffected, and when it is returned from reduce it still holds the initial value it was initialised with - 0.
Related
I am new to JavaScript and have several questions about functional programming.
Here is a statement:
outer(inner(5));
Is it possible to construct function outer in a way that allows it
to capture function inner and its argument 5?
Is it possible to construct function inner in a way that allows it to
pass itself and its argument 5 to function outer?
If the answer to both questions above is no, is it possible to
construct functions outer and inner in a way that allows the former
to capture function inner and its argument 5 or the
latter to pass itself and its argument 5 to function
outer?
I tried:
using the arguments object but to no avail.
function outer (parameter) {
return arguments;
}
function inner (n) {
return n + 1;
}
console.log(outer(inner(5))); // returns Arguments { 0: 6 ... }
using currying but I do not see how it can help me since I am not given the following statement:
outer()(5);
A possible workaround consists in returning an array from inner() composed of on one side the processing function and on the other side the argument.
outer will be able to access both by reading the array.
function outer(arr)
{
var fun = arr[ 0 ];
var arg = arr[ 1 ];
var result = fun(arg);
console.log('inner function is:', fun);
console.log('its argument is:', arg);
console.log('its result is:', result);
return result;
}
function inner(num)
{
return [
function (_num)
{
return _num + 1;
},
num
]
}
console.log(outer(inner(5)));
You could achieve this by letting your inner return a function (foo) which closes over n. You can then let foo return n+1. Then, within your outer function, you can invoke foo to get its return value:
const outer = f => f();
const inner = n => _ => n+1;
console.log(outer(inner(5)));
Alternatively, another possibility would involve changing your return value. You could return an array from inner which contains the original passed through arguments (...arguments) and the returned value (to_return) and then use destructuring assignment to get the passed in argument(s) (n & m) and the returned result:
function outer([result, n, m]) {
console.log("returned from inner: ", result);
console.log("arguments passed into inner: " + [n, m]);
return n;
}
function inner(n, m) {
let to_return = n + 1;
return [to_return, ...arguments];
}
console.log(outer(inner(5, 2))); // returns 5
Note: I added an m argument to demonstrate how you can extend this to multiple arguments
function outer(myFunction, argument) {
if (typeof myFunction !== "function") {
return false;
}
return myFunction(argument);
}
function inner(n) {
return n + 1;
}
console.log(outer(inner, 5));
Just a simple approach. Don’t execute the function inner but pass it as an argument (myFunction). And let the outer function execute it with the given argument.
I try to use jquery(jquery-2.1.1) and constructor function to build html. But when I tried this method in for loop, the loop can't stop. Can anyone tell me why it happens?
Here' s the code. Thanks a lot.
function tag() {
this.addClass = function(...theArgs) {
for (index in theArgs) {
this.$html.addClass(theArgs[index]);
}
}
this.setAttr = function(attr, value) {
this.$html.attr(attr, value);
}
this.append = function(...theArgs) {
for (index in theArgs) {
this.$html.append(theArgs[index]);
}
}
this.find = function(value) {
return this.$html.find(value);
}
this.empty = function() {
this.$html.empty();
}
this.remove = function(value) {
this.find(value).remove();
}
this.clone = function() {
return jQuery.extend(true, {}, this);
}
this.show = function() {
return this.$html[0];
}
}
function label(text) {
tag.call(this);
this.$html = $("<label></label>");
this.append(text);
}
for(var index = 0; index < 2; index++) {
var fieldLabel = new label(1);
console.log(index);
}
The problem here is you use the index (without var) as the running variable for all loops in your tag function. That index variable is still effective in the outer scope of the for-loop at the end (which should stop with the condition >=2).
At the beginning of the loop, index is 0. The next loop it should be 1. But when going into the inner append method, it's reset back to 0 due to the loop for-in (the argument passed in is just 1, so the spread notation makes an array of 1 length, and for-in stops with index set to 0) . So at the end of the second loop it is still 0. That means it will never become greater the value 1 (which is increased only at the beginning of the for-loop). The condition < 2 will always be satisfied and the for-loop just runs forever.
You can either use another name for the running variable in for-in. Or just declare another local-scoped index by using var, like this:
this.append = function(...theArgs) {
for (var index in theArgs) {
this.$html.append(theArgs[index]);
}
}
Or better using for-of as someone suggested.
I just started playing around with functional programming and am trying to pass a function as an argument of another function. However the function that I am trying to pass also has arguments like so:
function splitStringBy(string, type, func) {
// Split string by type.
var splitArray = string.split(type);
console.log(splitArray);
// Do something with the array.
func !== undefined ? func(splitArray) : null;
}
function loopArray(array, func) {
// Loop through array.
for (var i = 0; i < array.length; i++) {
func(array[i]);
}
}
I need to pass splitArray to my loopArray()
Here's how I'm trying to call it:
splitStringBy($scope.textSpace, "<br>", loopArray(splitArray, function() {
console.log('It worked!');
}));
Console comes up with Error: splitArray is not defined.
Rather than passing loopArray as a function, you're actually calling it, then passing its return value to splitStringBy. Since splitArray isn't defined when you first reference it, it's throwing that error.
What you want to do is something like this:
function splitStringBy(string, type, func) {
// Split string by type.
var splitArray = string.split(type);
console.log(splitArray);
// Do something with the array.
func !== undefined ? func(splitArray) : null;
}
function loopArray(func) {
// Return function for looping.
return function(array) {
// Loop through array.
for (var i = 0; i < array.length; i++) {
func(array[i]);
}
}
}
splitStringBy($scope.textSpace, "<br>", loopArray(function() {
console.log('It worked!');
}));
This is called currying, where a function passes a function as a return value. loopArray will create a function, then return it. We then pass the newly made function to splitStringBy, which then invokes it.
The code is from Eloquent Functional Programming. I have trouble understanding the test(element). If the test(element) is referencing equals(x), then is element = 0 since there is only one parameter?
function count(test, array) {
return reduce(function(total, element) {
return total + (test(element) ? 1 : 0);
}, 0, array);
}
function equals(x) {
return function(element) {return x === element;}; // element gets value from x?
}
function countZeroes(array) {
return count(equals(0), array);
}
Previous code
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
function reduce(counter, base, array) {
var total = 0;
forEach(array, function (element) {
total += counter(element);
});
return total;
}
element does not get its value from x.
Rather, element refers to a parameter which will have it's value supplied when the function is invoked - by count in this case. The variable x, a parameter in the outer scope, is bound in scope of the function/closure that is returned when equals is invoked. That is, equals(0) evaluates to a function/closure which is then used as a predicate to count.
First, let's use equals directly, bearing in mind that equals evaluates to a function as does equals(0):
equals(0)(1) // -> false, x=0, element=1
equals(0)(0) // -> true, x=0, element=0
// ^^^^^^^^^ - invokes equals(..), evaluates to the closure binding `x`
// ^^^ - invokes the previously returned closure, supplying `element`
But because that's a bit hard to abstractly see, let's give the closure a name:
var equalToZero = equals(0) // -> function, x=0
// ^^^^^^^^^ - invokes equals(..), evaluates to the closure binding `x`
equalToZero(1) // -> false, x=0, element=1
equalToZero(0) // -> true, x=0, element=0
// ^^^^^^^^^^^^^^ - invokes the previously returned closure, supplying `element`
// And just as before, it is the `count` function that
// supplies the `element` argument when it invokes the function.
// (The function is locally known by the name `equalToZero`.)
count(equalToZero, array);
Which we could imagine was written like this:
function equalToZero (element) {
return 0 === element;
}
The difference being, of course, that in the above function the value (0) is hard-coded, while in the closure-creating equals it is whatever the bound x variable evaluates to.
(For more wonderful goodness on closures, see How do JavaScript closures work?)
Drawing a set of circle with RaphaelJS library.
For each circle I want to create an onclick function that passes a variable, but in this function my variable is undefined.
What is the problem?
This is my code:
//children is an array such as [1,2,4[5,6,7]]
for (var i = 0; i < children.length; i++) {
var array = children;
alert("ARRAY[0]===" + array[0])
var st = space2Draw.set();
st.push(space2Draw.circle(xChildren, yChildren, 20).click(function (array) {
//array[i] is undefined
alert("ARRAY[i]===" + array[i]);
//retrive(array[i]);
}),
LineRoot(xRadice, yRadice, xChildren, yChildren, space2Draw));
space2Draw.text(xChildren, yChildren, children[i]).attr({
fill: "white"
});
st.attr({
fill: "red"
});
xChildren += 50;
}
You shouldn't have a "array" parameter to your click callback, it is overriding the "array" var from the parent scope. You can just remove the argument and it should be ok.
I think you will have another problem with i always being children.length in your click callback (because the function scope will actually be closed at the end of the loop. More info here). You should create an auxiliary function to create the callback for you.
You could try something like this:
//children is an array such as [1,2,4[5,6,7]]
for (var i = 0; i < children.length; i++) {
var array = children;
alert("ARRAY[0]===" + array[0])
var st = space2Draw.set();
st.push(space2Draw.circle(xChildren, yChildren, 20).click(getCallback(array , i)),
LineRoot(xRadice, yRadice, xChildren, yChildren, space2Draw));
space2Draw.text(xChildren, yChildren, children[i]).attr({
fill: "white"
});
st.attr({
fill: "red"
});
xChildren += 50;
}
function getCallback(array , i){
return function () {
alert("ARRAY[i]===" + array[i]);
}
}
You should do this:
...
st.push(space2Draw.circle(xChildren, yChildren, 20).click((function (array, i) {
return function () {
//array[i] is undefined
alert("ARRAY[i]===" + array[i]);
//retrive(array[i]);
}
}(array, i)));
...
The reason why this works is that in javascript, scope is defined by functions, not by blocks.
Your callback gets access (by closure) to the array and i (among other variables), but when the callback gets executed, i is equal to children.length, because the loop has finished iterating.
By using IIFE (immediately invocation function expression) we create a new scope, and the callback gets access to the current value of array and i.
Also, the click function calls the callback by passing an event object as first parameter, so our IIFE should return this
return function (event) {
// access array and i by closure
// event is the event representing the click
// event, provided by Raphael
// do something
}