I have my each
function which I created to emulate Underscore.js's _each()
for my Javascript study.
var each = function(list, iteratee) {
if (Array.isArray(list)) { // list is array
for (var i = 0; i < list.length; i++) {
iteratee(list[i], i, list);
}
} else if (list.constructor === Object) { // list is object
for (var key in list) {
iteratee(list[key], key, list);
}
}
};
Then I wanted to create a function find
which is also available from Underscore.js. This function looks through each value in the list
, returning the first one that passes a truth test (predicate
), or
undefined
if no value passes the test. The function returns as soon as it finds an acceptable element and doesn't traverse the entire list.
Here is my version of find
that I came up with.
var find = function(list, predicate) {
each(list, function(elem){
if (predicate(elem)) {
return elem;
}
});
};
I thought it would return a value immediately after it has found a true value for an element that passes a test from an external predicate function. But instead, it's giving me an undefined
.
Below code works as I expected. But why would they provide different output?
var find = function(list, predicate) {
if (Array.isArray(list)) { // list is array
for (var i = 0; i < list.length; i++) {
if (predicate(list[i])) {
return list[i];
}
}
} else if (list.constructor === Object) { // list is object
for (var key in list) {
if (predicate(list[key])) {
return list[key];
}
}
}
};
What I don't understand is that why doesn't each
works as I expected when I included it in my find
function. Wouldn't they simply different in terms of their style of expression? In other word, one is functional style, and another is not?
答案 0 :(得分:2)
This is caused due to the lack of return statement. Each function iterates over find but doesn't return anything. return statement in predicate returns the output to the each function where it is not expected
Example of working function:
var find = function(list, predicate) {
var res = undefined/null/whatever;
each(list, function(elem) {
if (predicate(elem)) {
res = elem;
}
});
return res;
};
However this function is not efficient as it won't stop when result is found
答案 1 :(得分:1)
This has to do with how return
works. Let's look at your code:
var find = function(list, predicate) {
// you pass list and an anonymous callback to `each`
each(list, function (elem) {
// if this condition is true
if (predicate(elem)) {
// return elem
return elem;
}
});
}
The problem is that return elem
applies to the anonymous callback, not the find
function.
If you want to be able to "break" the each
loop, you can check the current state on each iteration of the for-loop within each
.
// only going to write for arrays
var each = function (list, iteratee) {
for (var i = 0; i < list.length; i++) {
if (iteratee(list[i], i, list)) continue;
else break;
}
});
// then in find:
var find = function (list, predicate) {
var ret = null
each(list, function(elem) {
if (predicate(elem)) {
ret = elem;
return false; // return false, since we no longer wish to continue
}
});
return ret;
};
A second solution is to return from within the each
loop:
var each = function (list, iteratee) {
for (var i = 0; i < list.length; i++) {
if (iteratee(list[i], i, list)) {
continue;
} else {
return list[i];
}
}
// didn't find anything, so `return null`
return null;
});
var find = function (list, predicate) {
return each(list, function(elem) {
// if `predicate`, return false, so the loop breaks
return !predicate(elem);
});
};
The only problem I have with this solution is that it distorts the meaning of each
. each
intuitively means "go through everything," which the second solution doesn't necessarily do.