While going through Eloquent Javascript (Chapter 6) there is a reference to higher-order functions in Javascript. While there is an example provided in Chapter 3, I believe it could be a bit simpler since I still don't fully understand the concept. After searching the web I can't seem to find any succinct examples of a higher-order function.
I'd like to see a basic/simple higher-order function in Javascript that will explain the concept.
Higher functions are concepts from functional programming. In briefly, a higher function is a function which takes another function as parameter. In javascript, some higher functions are added recently.
Array.prototype.reduce
//With this function, we can do some funny things.
function sum(array){
return array.reduce(function(a, b){ return a + b; }, 0);
}
So, in the above sample, reduce is a higher order function, it takes another function, the anonymous function in the sample, as a parameter. The signature of reduce looks like this
reduce(func, init);
//func is a function takes two parameter and returns some value.
// init is the initial value which would be passed to func
//when calling reduce, some thing happen
//step 1.
[1, 2, 3, 4, 5].reduce(function(a, b){ return a + b }, 0);
//step 2.
[2, 3, 4, 5].reduce(function(a, b){ return a + b}, 0 + 1);
//step 3.
[3, 4, 5].reduce(function(a, b){ return a + b}, 0 + 1 + 2);
//...
As you can see, reduce iterate an array, and apply the func with init and first element of that array, then bind the result to init.
Another higher order funciton is filter.
Array.prototype.filter
//As the name indicates, it filter out some unwanted values from an Aarry. It also takes a function, which returns a boolean value, true for keeping this element.
[1, 2, 3, 4, 5].filter(function(ele){ return ele % 2 == 0; });
With the above two examples, I have to say higher order function is not that much easy to understand, especially reduce. But that's not complex, with higher order function, actually your code would be more clean and readable. Take the filter as example, it tells people that it throws all odd numbers away.
Here I'd like to implement a simple filter function to show you how.
function filter(array, func){
var output = [];
for(var i = 0; i < array.length; i++){
if(func(array[i])) output.push(array[i]);
}
return output;
}
Related
Relating to the following mock code (_ relates to the lodash library):
var containerFunction = function () {
var opts = {data: value};
_.map(lines, functionForEach);
}
var functionForEach = function (line) {
Do something for each line passed in from the maps function.
Need to access opts in this scope.
return value;
}
The argument line is received from the map function, but what would be the best way of passing the opts argument to the functionForEach function while keeping the rough pattern above (if possible).
I thought something like:
_.map(lines, functionForEach(opts))
or something similar might work but obviously doesn't.
Any suggestions?
You have three alternatives:
Put functionForEach inside containerFunction. If you are not going to use functionForEach elsewhere, this makes the most sense.
Write it as:
var containerFunction = function () {
var opts = {data: value};
_.map(lines, function(elt) { return functionForEach(elt, opts); });
}
var functionForEach = function (line, opts) {
Do something for each line passed in from the maps function.
Need to access opts in this scope.
return value;
}
If you must, pass opts as the third (thisArg) argument to _.map and access it using this inside functionForEachvar.
Lodash has good utilities for function composition, including partially-applying arguments. However, this isn't actually so straightforward. Let's say we want to use partialRight():
function functionForEach(item, i) { return item * i; }
_.map([1, 2, 3], _.partialRight(functionForEach, 2));
// → [0, 2, 6]
Oops. That's not correct. The reason is that we're not getting the arguments passed to functionForEach() as we expect them. We want the i argument to always have a value of 2 and the item is the mapped collection value.
Let's try something else:
_.map([1, 2, 3], _.ary(_.partialRight(functionForEach, 2), 1));
// → [2, 4, 6]
That's better. The problem is that map() passes the item as the first argument, along with 2 other arguments (the index and the collection itself). Since we don't care about these latter two arguments, we can use ary() to compose a new function that ignores them.
Here's another approach:
_.map([1, 2, 3], _.partial(_.rearg(functionForEach, 1, 0), 2));
// → [2, 4, 6]
This time, we're using rearg() to change the order of our partial() function arguments. This approach I find to be less intuitive than simple going right for partialRight().
One final example:
_.map([1, 2, 3], _.flow(_.identity, _.partialRight(functionForEach, 2)));
// → [2, 4, 6]
Here, we're using the flow() higher-order function to compose our callback. This function takes the arguments supplied to it, and chains together the functions we pass to it - the output of the last function is the input to the next. We're using the identity() function here because it simply returns the first argument passed to it, which is exactly what we want.
This last approach is similar to the ary() approach - it's doing the same thing. The flow() approach is superior if we ever wanted to build on this behavior by passing in more functions, before or after functionForEach().
Take a look at _.partial. Docs:
partial
var containerFunction = function() {
var opts = { data: value };
_.map(lines, _.partial(functionForEach, opts));
};
var functionForEach = function (opts, line) { ... };
_.partial returns a new function with the arguments prepended to the functionForEach. In the example above, _.partial returns the following function signature:
function (line) { ... }
Calling the return function with a line calls functionForEach with line and opts defaulted to whatever you passed in as the argument in _.partial.
I am practicing solving problems with recursion for a class.
I am solving the problems on this site : http://www.w3resource.com/javascript-exercises/javascript-recursion-functions-exercises.php
The question I am referring to is stated : Write a JavaScript program to get the integers in range (x, y).
Example : range(2, 9)
Expected Output : [3, 4, 5, 6, 7, 8]
Before looking at the solution I came up with this:
var range = function (start, end) {
var result = [];
var accumulator = start;
var accumulate = function () {
accumulator++;
if (accumulator === end) {
return;
} else {
result.push(accumulator);
}
accumulate();
};
accumulate();
return result;
};
The solution on the site is this:
var range = function(start_num, end_num)
{
if (end_num - start_num === 2)
{
return [start_num + 1];
}
else
{
var list = range(start_num, end_num - 1);
list.push(end_num - 1);
return list;
}
};
Is my solution technically still recursive? I had a similar answer on a quiz recently and I was told my solution is essentially iterative.
Though you use recursion, you have simply written a loop in the form of recursion.
I am going to answer this from the purely academical standpoint. If you want to avoid the intermediate state (result) and use purely functional constructs, I would write it like this
function range(start, end) {
function recRange(current, end) {
if (current > end)
return [];
return [current].concat(recRange(current + 1, end));
}
return recRange(start + 1, end - 1);
}
console.log(range(2, 9));
// [ 3, 4, 5, 6, 7, 8 ]
If you see here, we create a new function within the range function, which recursively creates a new array on every iteration (remember: this is not highly performant code, you can simply use loops and be done with this problem efficiently).
The base condition of the recursion is current < end. Once that is met, the recursion stops and an empty array is returned. In all the levels, a new array with the current value is concatenated with the result of the recursive call. So, the evaluation of the calls are roughly understood like this
[3].concat(recRange(3 + 1, end));
[3].concat([4].concat(recRange(4 + 1, end)));
...
at the end, when the recursion unwinds, the values will be like this
[3].concat([4].concat([5].concat([6].concat([7].concat([8].concat([]))))))
[3].concat([4].concat([5].concat([6].concat([7].concat([8])))))
[3].concat([4].concat([5].concat([6].concat([7, 8]))))
[3].concat([4].concat([5].concat([6, 7, 8])))
[3].concat([4].concat([5, 6, 7, 8]))
[3].concat([4, 5, 6, 7, 8])
[3, 4, 5, 6, 7, 8]
and that will be returned as the result.
To make your solution recursive, it should return some value and somehow combine the result of the recursive call to form the return value of the original call.
Let me illustrate that with an example, by modifying your solution:
var range = function (start, end) {
var accumulate = function (accumulator) {
if (accumulator === end - 1) {
return [accumulator]; // Stop condition
} else {
// Recursive block
var result = accumulate(accumulator+1); // recursive call
result.unshift(accumulator); // combine result
return result
}
};
return accumulate(start);
};
The modified accumulate function will return a one-element list for the stop condition, the simplest case it handles, where accumulator reaches the last value to return.
In the example range(2,9), the stop condition will return [8].
Then in the caller, the recursive block
var result = accumulate(accumulator+1);
result.unshift(accumulator);
return result
will take the list [8], and preprend the current value of accumulator (7), so it'll return [7,8].
...and the caller of accumulator(7), will receive [7,8] and preprend the value 6 to the list, to return [6,7,8].
At the end, the original call to accumulator(2) will generate the expected result [2,3,4,5,6,7,8].
Is my solution technically still recursive?
Yes. You're using tail recursion; however, since no arguments are being passed to accumulate() I can see why someone may say it's essentially iterative. You could easily replace your recursive call with a loop. Recursive algorithms typically leverage the stack.
Because of Javascript's closures, it is harder to understand the concept of recursion in Javascript compared to other languages like C++ or Java or C#.
To understand recursion, you must first understand recursion. :)
I'm reading a book called Eloquent JavaScript. There's an exercise in it that requires one to flatten a heterogeneous array & after trying so long and failing to get the answer, I looked up the solution online & couldn't understand the code. I'm hoping someone will be kind enough to explain, especially for argument "flat" and how it's supposed to work. The code is below
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
The reduce function defined in the book is:
function reduce(array, combine, start) {
var current = start;
for (var i = 0; i < array.length; i++)
current = combine(current, array[i]);
return current;
}
and as a method of an array,
arr.reduce(combine, start);
Let's look at each part of the reduce method. The book describes it as "folding up the array, one element at a time." The first argument for reduce is the "combiner function", that accepts two arguments, the "current" value and the "next" item in the array.
Now, the initial "current" value is given as the second argument of the reduce function, and in the solution of flattening arrays, it is the empty array, []. Note that in the beginning, the "next" item in the array is the 0th item.
Quoting the book to observe: "If your array contains at least one element, you are allowed to leave off the start argument."
It may also be confusing that in the flattening solution, current is placed as the second argument to reduce, whereas in the reduce definition above, current is used to assign the cumulative, folded value. In the flattening solution, current refers to the "next" arrays item (the individual array of integers)
Now, at each step of the reduction, the "current" value plus the next array item is fed to the (anonymous) combiner, and the return value becomes the updated "current" value. That is, we consumed an element of the array and continue with the next item.
flat is merely the name given to the accumulated result. Because we wish to return a flat array, it is an appropriate name. Because an array has the concat function, the first step of the reduce function is, (pretending that I can assign the internal variables)
flat = []; // (assignment by being the second argument to reduce)
Now, walk through the reduction as iterating over arrays, by going through the steps shown above in reduce's definition
for (var i = 0; i < arrays.length; i++)
flat = combine(flat, arrays[i]);
Calling combine gives [].concat([1, 2, 3]) // => [1, 2, 3]
Then,
flat = [1, 2, 3].concat([4, 5]) // => [1, 2, 3, 4, 5]
and again for the next iteration of the reduction. The final return value of the reduce function is then the final value of flat.
This would be the solution I came with with ES6 format:
const reduced = arrays.reduce((result,array) => result.concat(array),[]);
console.log(reduced);
I have implemented this solution and this seems to work for nested arrays as well.
function flattenArray(arr){
for(var i=0;i<arr.length;i++){
if(arr[i] instanceof Array){
Array.prototype.splice.apply(arr,[i,1].concat(arr[i]))
}
}
return arr;
}
There is an easy way to do these exercises. those functions are already built inside the javascript so you can use them easily.
But the whole joy of this exercise is to create those functions:
Create reduce function. Reduce function should add all array elements. you can use a higher-order function or just a normal one. here is an example for higher-order:
function reduce(array, calculate){
let sumOfElements = 0;
for(let element of array){
sumOfElements = calculate(sumOfElements, element)
}
return sumOfElements
}
Next step is to create a concat function. since we need to return those reduced arrays in new array we will just return them. (Warning: you must use rest parameter)
function concat(...arr){
return arr
}
And for last. you will just display it (You can use any example)
console.log(concat(reduce([1, 2, 3, 4], (a, b) => a + b), reduce([5, 6], (a, b) => a + b)))
The reduce method acts as a for loop iterating over each element in an array. The solution takes each array element and concatenates it to the next one. That should flatten the array.
var arr =[[1,2],[3,4],[5,6]]
function flatten(arr){
const flat= arr.reduce((accumulator,currentValue)=>{
return accumulator.concat(currentValue)
})
return flat
}
console.log(flatten(arr))
//Output 1,2,3,4,5,6
I'm working through Eloquent Javascript and I'm having trouble understanding something. Perhaps I've missed something along the way. This is the solution given for chapter 5 (higher-order functions), exercise 1, which takes the elements in the different arrays and puts them all in a single array:
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
My problem is: I have absolutely no clue why the arguments "flat" and "current" work in this situation. The entire chapter reads through assuming the reader understands what's going on here but I have absolutely no idea why this works. It doesn't appear that "flat" and "current" are defined anywhere. Another short example is this one where the author explains how the reduce method works (problem area in bold):
function reduce(array, combine, start) {
var current = start;
for (var i = 0; i < array.length; i++)
current = combine(current, array[i]);
return current;
}
**console.log(reduce([1, 2, 3, 4], function(a, b) {
return a + b;
}, 0));**
Where in the world did "a" and "b" come from and why does this piece of code work? Any help would be much appreciated, thank you.
flat and current don't need to be declared anywhere, they are parameters to the anonymous function that is passed to Array.reduce.
One way to illustrate this is to modify your second example to use Array.reduce directly using an anonymous function with parameters a and b. Look at this:
[1, 2, 3, 4].reduce(function(a, b) {
console.log("a: " + a + " b: " + b);
return a + b;
});
The console will now show:
a: 1 b: 2
a: 3 b: 3
a: 6 b: 4
10
What's happening is that the anonymous function(a, b) {...} is called with (1, 2), which returns 3, which is passed in again (3, 3) which returns 6, which is passed in as the first argument (6, 4), which returns the final answer 10.
Another illustration is to use a second argument to Array.reduce, say 10, to see what's going on. That 10 is used as the initialValue. So:
[1, 2, 3, 4].reduce(function(a, b) {
console.log("a: " + a + " b: " + b);
return a + b;
}, 10);
The trace is:
a: 10 b: 1
11 b: 2
13 b: 3
16 b: 4
20
You can work out how that happened.
Yes, reduce can be a little confusing in the beginning. It is a native function that takes two parameters. One is a callback function and the other one is any value.
The idea is that in the callback function you can use the values of the array one at a time to process a result. To do that it iterates over the array values and passes them to the callback function you defined one at a time and for every loop it takes the value of the last loop and passes it as well.
Let's say you want to sum all numbers in an array:
//your array
var numbers = [4,7,3];
//your initial value
var initialValue = 0;
//your function
function sum(iteratingValue, arrayValue) {
return iteratingValue + arrayValue;
}
var result = numbers.reduce(sum, initialValue);
Now, you can name your callback function parameters whatever you like, a and b, start and finish, fish and duck. It won't matter, reduce will pass the values in the same order.
Here is the definition by MDN:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
reduce executes the callback function once for each element present in the array, excluding holes in the array, receiving four arguments: the initial value (or value from the previous callback call), the value of the current element, the current index, and the array over which iteration is occurring.
First, let us touch the core of the question. Let's suppose you have a function, like this:
function func1(myfunc, arr) {
//Do something, which will result in having variables called param1 and param2
myfunc(param1, param2);
}
Let's see what happens. func1 takes myfunc and arr as parameters. myfunc will be passed when func1 is called. arr is an array.
Now, let's suppose you call it this way:
func1(function(a, b) {
//do something with a and b
}, [1, 2, 3, 4]);
You are calling func1, by passing a function and an array. The array is obvious, so let's see the function. The function expects two parameters and will do something with them. You do not have to define a or b when you call func1, since it is func1's internal job to create and initialize them. So, func1 will do its internal things and call your function passed as a parameter. Now, let's see your example:
var arrays = [[1, 2, 3], [4, 5], [6]];
console.log(arrays.reduce(function(flat, current) {
return flat.concat(current);
}, []));
Here, you call arrays.reduce (which is very similar to func1 in the general description). You pass a function and an array. Again, the array is obvious, but the question is, how flat and current are defined. The answer is that it is arrays.reduce's internal job to create and initialize them. As about the reduce prototype function, you can read more about it here.
For the life of me, I just can't figure out what I'm doing wrong here.
I'm trying to use both the reduce and concat array methods to take all of the values of a 2d array and combine them into a single value (basically condense them into a single array and then sum them up).
The problem that I keep running into is that when I try to make a for/loop to concat each array element, the argument that I'm passing into the function is not being recognized as an array, thus my call to .concat() is failing. I've placed a console.log() at the beginning of the function to see if the element is being recognized as the first array element in the 2d array, and it's coming up as "1"(?).
I tried another test outside of the function, and it logs as the actual array element. What am I doing wrong here? code below:
var arrays = [[1, 2, 3], [4, 5], [6]];
var myArray = arrays[0]; // Test
console.log(myArray); // Test
var flatArray = arrays.reduce(function(arrays)
{
console.log(arrays[0]); // Test
for (var i = 0; i < arrays.length - 1; i++)
{
arrays[0].concat(arrays[i+1]);
}
return arrays;
});
console.log(flatArray);
This is the output that I keep getting:
Array [ 1, 2, 3 ]
1
TypeError: arrays[0].concat is not a function
It's almost seems like array is being converted to a number-type when inside the function...?
You have an error in your code here:
var flatArray = arrays.reduce(function(param) {})
that param will be an element of your arrays vector.
Check this https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
You are using .reduce() incorrectly and you don't even need to use it to flatten an array. You can just do this:
var flatArray = [].concat.apply([],arrays);
Working demo: http://jsfiddle.net/jfriend00/wfjyfp42/
To understand .reduce(), the callback you pass it gets four arguments (see MDN reference). The first two arguments are important in using .reduce() correctly:
callback(previousValue, currentValue, index, array)
The previousValue is the accumulated value so far in the reduction. The currentValue is the next element of the array that is being iterated. The last two arguments do not need to be used if not needed.
Your code is only using the previousValue so it is never looking at the next item in the array as passed in by .reduce().
You could make a solution work using .reduce() like this:
var flatArray = arrays.reduce(function(previousValue, currentValue) {
return previousValue.concat(currentValue);
}, []);
Working demo: http://jsfiddle.net/jfriend00/2doohfc5/
Reduce performs an operation on two elements.
var sum = [[1, 2, 3], [4, 5], [6]].reduce(function(a, b) {
return a.concat(b);
}).reduce(function(a, b) {
return a + b;
});