In Eloquent JavaScript, 1st ed, page 77, it gives an example of building a mapping function from scratch:
function mapFunc(func, array) {
var result = []; // not touching the original!
console.log("array:", array)
forEach(array, function(element) {
result.push(func(element));
});
return result;
}
Seems pretty straightforward but when it's run like this:
console.log(mapFunc(Math.round, [0.01, 2, 9.89, Math.PI]))
It throws an error:
ReferenceError: forEach is not defined
Even when I change things up to match the es6 syntax more, same issue:
array.forEach(function(element) {
result.push(func(element))
console.log(element)
})
I've been messing with it for a while and can't figure out what the problem might be or why forEach suddenly becomes undefined. Thoughts?
You need to use array.forEach, and you need to call mapFunc() correctly. You're passing the array as the second argument to console.log() rather than mapFunc().
function mapFunc(func, array) {
var result = []; // not touching the original!
console.log("array:", array)
array.forEach(function(element) {
result.push(func(element));
});
return result;
}
console.log(mapFunc(Math.round, [0.01, 2, 9.89, Math.PI]));
The book defines a forEach() function of its own earlier in the chapter.
function forEach(array, action) {
for (var i = 0; i < array.length; i++)
action(array[i]);
}
If you add that function to your code, you should be able to use the mapFunc() function as you first wrote it. But you need to call mapFunc() correctly in either case.
Related
I am currently following a javascript course and am having issues understanding what is happening behind the scenes in javascript in one of the examples (see code below).
I understand most of the code and understand why the output logged is -> [false, true, true]. However there is one part which is driving me nuts (I pointed an arrow to it in the code at the bottom):
my confusion revolves around the parameter 1:
what journey does the parameter 1 take from the moment it gets passed with checkPastLimitSimplified(1) in var arr5 = mapForEach(arr1, checkPastLimitSimplified(1));.
I understand that when checkPastLimitSimplified(1) is invoked an execution context is created for this function in which the parameter 1 is in the variable environment.
But now what happens?
The function inside the checkPastLimitSimplified function is not executed yet but just returned. What does it look like when it is returned? at what point do the limiter variables receive the parameter 1?
I understand that .bind(this, limiter); creates a copy of the function. Is its limiter variable already a 1 before it gets returned?
function mapForEach(arr, fn) {
var newArr = [];
for (var i = 0; i < arr.length; i++) {
newArr.push(
fn(arr[i])
)
};
return newArr;
}
var arr1 = [1, 2, 3];
var checkPastLimitSimplified = function(limiter) { // < ----CONFUSED
return function(limiter, item) {
return item > limiter;
}.bind(this, limiter);
};
var arr5 = mapForEach(arr1, checkPastLimitSimplified(1));
console.log(arr5);
Lets rename variable to see relations:
var checkPastLimitSimplified = function(outer_limiter) {
return function(limiter, item) {
return item > limiter;
}.bind(this, outer_limiter);
};
bind modifies function signature to be just function(item) before return.
When client code will call checkPastLimitSimplified(1)(item), limiter will substitute from binded context.
another way to make it more understandable is putting the inner function outside:
var checkPastLimit = function(limiter, item) {
return item > limiter;
};
var checkPastLimitSimplified = function(outer_limiter) {
return checkPastLimit.bind(this, outer_limiter);
};
The result will be a copy of the first function with the first parameter (limiter) already defined.
This new function will need only the second param (item).
But there is no execution of the function code (the comparison with the limit) on this stage.
As part of an exericse, I'm re-writing underscore functions and testing them in jsfiddle. Every time I pass a callback function, I get "undefined".
My code is below:
each = function(collection, iterator) {
if(Array.isArray(collection)){
for (var i = 0; i < collection.length; i++) {
iterator(collection[i], i, collection);
}
} else {
for(var key in collection) {
iterator(collection[key], key, collection);
}
}
};
var numbers = [1,2,3,4];
var result = each(numbers, function(num) {
return num * 2;
});
console.log(result);
// undefined
Any idea what I'm doing wrong and why it's not outputting on jsfiddle?
You aren't doing anything wrong. Your each function isn't returning anything. This is fine, since each functions don't necessarily have to return anything. You might be thinking along the lines of a map or reduce function which compile the results of calling the callback on each item in the collection, and then return that compilation.
The callback you pass to an each function doesn't normally return anything. Think of an each like it's just syntactic sugar for a normal for loop; for loops don't return anything (obviously..), they just perform generic operations with the items in the collection and the variables that have been declared in the containing scope.
That being said, if you want to emulate underscore (or any good library), you will want to return the collection to enable chaining. From the underscore docs:
[each] Iterates over a list of elements, yielding each in turn to an iteratee function....Returns the list for chaining.
This is just good practice so as to avoid annoying the developers who may use your library and are used to being able to chain everything in JavaScript.
So all you'd need to do is put
return collection;
at the end of your each function and you're good. Cheers.
You are not aggregating the result of your operation that's why the result is not being result.
Here is a quick stab at how this can be fixed for arrays.
each = function(collection, iterator) {
var arr = [];
if(Array.isArray(collection)){
for (var i = 0; i < collection.length; i++) {
arr.push( iterator(collection[i], i, collection) );
}
return arr;
} else {
for(var key in collection) {
iterator(collection[key], key, collection);
}
}
};
var numbers = [1,2,3,4];
var result = each(numbers, function(num) {
return num * 2;
});
console.log(result);
jsfiddle
I am making a reduce function that uses another function, which takes multiple arguments, as a callback. My question is, if I wish to test the callback function for whether an argument exists, what syntax would I need to use.
var result = _.reduce([1,2,3], function(memo) {
return memo;
});
_.reduce = function(arr, fun, opt){
//if(arguments[1][1] == undefined) return arr[0];
The last line is my best attempt. What I am trying to say is that, if the function in the result line has no second argument, return arr[0].
If I understand your question correctly, you are trying to access the arguments inside a function call. You can use the arguments object for this.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/length
It allows you to iterate through the arguments of a function and do something with them. For example -
function yourCustomReduceFunction() {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
Hope this helps :)
I am learning Javascript for a project using online resources however i don't know how to get this function working.
var results =[[a1,a2,a3,a4,a5]];
var winner = 0;
function checkWinner (results)
{
for (var i = 0; i < results.length; i++)
if (results[0][i] > 50)
{
winner = results[0][i];
}
}
Just after the function, i use:
checkWinner(results);
In a HTML file i use alert to display the variable winner. But it obviously doesn't work. I realise it is a problem with my understanding of scope and global variables.
should be
var Results =[[a1,a2,a3,a4,a5]];
var winner = 0;
function checkWinner (results)
{
for (var i = 0; i < results[0].length; i++)
if (results[0][i] > 50)
{
winner = results[0][i];
}
}
checkWinner(Results);
To avoid name collisions name global variables from capital case.
Also in your code you call the length of the "parent" array. You need to specify the length of the "Child" array
You've got to understand the concept of scope. The variables results and winner are not the same inside and outside the function.
Also, you've got to call the function and return something from it if you want to change the value of the variables outside the function (unless you use globals). This seems to be hard for novice programmers to understand, but merely defining a function doesn't do anything.
var results =[[a1,a2,a3,a4,a5]];
function checkWinner (results)
{
for (var result in results[0])
{
if (result > 50)
{
return result;
}
}
}
var winner = checkWinner(results);
Note that:
I used a for each loop, which has a cleaner syntax.
I am also iterating over results[0] instead of results, since you've got a nested array for whatever reason.
Because your function has an argument called results, it requires you to pass the global results in spite of it being a global. Another way to do this:
var results = [[a1,a2,a3,a4,a5]];
function checkWinner()
{
for (var result in results[0])
{
if (result > 50)
{
winner = result;
return;
}
}
}
checkWinner();
However, I would recommend against using global variables this way. Here's an explanation on why global variables are bad. It's for C++, but it applies to JavaScript as well.
You're iterating over the result[0] array (the array in result[0]), but using the length of the result array.
Object.prototype.e = function() {
[].forEach.call(this, function(e) {
return e;
});
};
var w = [1,2];
w.e(); // undefined
But this works if I use alert instead
// ...
[].forEach.call(this, function(e) {
alert(e);
});
// ...
w.e(); // 1, 2
I realize this is an old question, but as it's the first thing that comes up on google when you search about this topic, I'll mention that what you're probably looking for is javascript's for.. in loop, which behaves closer to the for-each in many other languages like C#, C++, etc...
for(var x in enumerable) { /*code here*/ }
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/for...in
http://jsfiddle.net/danShumway/e4AUK/1/
A couple of things to remember :
for..in will not guarantee that your data will be returned in any particular order.
Your variable will still refer to the index, not the actual value stored at that index.
Also see below comments about using this with arrays.
edit: for..in will return (at the least) added properties to the prototype of an object. If this is undesired, you can correct for this behavior by wrapping your logic in an additional check:
for(var x in object) {
if(object.hasOwnProperty(x)) {
console.log(x + ": " + object[x]);
}
}
Your example is a bit odd, but as this question is becoming the canonical "return from forEach" question, let's use something simpler to demonstrate the problem:
Here, we have a function that checks the entries in an array to see if someProp matches value and, if so, increments the count on the entry and returns the entry:
function updateAndReturnMatch(array, value) {
array.forEach(function(entry) {
if (entry.someProp == value) {
++entry.count;
return entry;
}
});
}
But calling updateAndReturnMatch gives us undefined, even if the entry was found and updated.
The reason is that the return inside the forEach callback returns from the callback, not from updateAndReturnMatch. Remember, the callback is a function; return in a function returns from that function, not the one containing it.
To return from updateAndReturnMatch, we need to remember the entry and break the loop. Since you can't break a forEach loop, we'll use some instead:
function updateAndReturnMatch(array, value) {
var foundEntry;
array.some(function(entry) {
if (entry.someProp == value) {
foundEntry = entry;
++foundEntry.count;
return true; // <== Breaks out of the `some` loop
}
});
return foundEntry;
}
The return true returns from our some callback, and the return foundEntry returns from updateAndReturnMatch.
Sometimes that's what you want, but often the pattern above can be replaced with Array#find, which is new in ES2015 but can be shimmed for older browsers:
function updateAndReturnMatch(array, value) {
var foundEntry = array.find(function(entry) {
return entry.someProp == value;
});
if (foundEntry) {
++foundEntry.count;
}
return foundEntry;
}
The function e() isn't returning anything; the inner anonymous function is returning its e value but that return value is being ignored by the caller (the caller being function e() (and can the multiple uses of 'e' get any more confusing?))
Because
function(e) {
return e;
}
is a callback. Array.forEach most likely calls it in this fashion:
function forEach(callback) {
for(i;i<length;i++) {
item = arr[i];
callback.call(context, item, i, etc.)
}
}
so the call back is called, but the return doesn't go anywhere. If callback were called like:
return callback.call();
the it would return out of forEach on the first item in the array.
You can use for...of to loop over iterable objects, like array, string, map, set... as per Mozilla docs.
const yourArray = [1, 2, 3]
for (const el of yourArray) { // or yourMap, Set, String etc..
if (el === 2) {
return "something"; // this will break the loop
}
}