I won't spend a lot of time on every, since we basically built every in the Rewrite Underscore's reduce function post. Every returns true if every item in the array passes a truth test is truthy, and false if otherwise. Every handles more situations than what we built in the previous post does though. Say, for example, if no function is provided, or what if we get a mix of truthy and falsey results? If you don't know what truthy or falsey means, google it. There is also one other small point that has to be brought up. The key word undefined is falsey, and should return false to every.
This word undefined causes a problem with the way I rewrote the Underscore reduce function. Let me show you this quickly, and an alternative rewriting of reduce that you can use if you want to test these by copying and pasting into a repl editor.
var _ = {};
_.identity = function(val) {
return val;
}
_.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);
}
}
}
_.reduce = function(collection, iterator, accumulator) {
_.each(collection, function(item){
if(accumulator === undefined){
accumulator = item;
}
else{
accumulator = iterator(accumulator, item);
}
})
return accumulator;
}
_.every = function(collection, iterator) {
iterator = iterator || _.identity;
return !! _.reduce(collection, function(a, b){
return a && iterator(b);
}, true)
}
Every uses reduce and each. Let's call every:
var words = [true, undefined, 1];
_.every(words);
That's it in this example. I'm just going to feed it the variable words. Notice in the every rewrite that every expects a function as an argument. However, it's first line of code says that iterator = iterator(the function given) or the function _.identity. Since I've provided no function when I called every, _.identity will be used, and remember, identity simply returns what ever value it is provided.
So, every calls reduce, feeds it the collection words, and hard wires in a function that takes two parameters(the accumulator and the item), and then returns the accumulator && the result of the function fed item, and we initially set the accumulator to true.
So, it walks through like this : accumulator equals true so we look on the right side of the && operator. This function is identity so it just returns the value passed to it, which in this case is the boolean value true(which is the first item in the array words). So this function returns true. Now quickly look up at the reduce rewrite, look inside each, look inside the else statement, and look at the line accumulator = iterator(accumulator, item). When I said, one sentence earlier, 'So this function returns true', this is where it gets returned to. This line : accumulator = iterator(accumulator, item);
It looks something like this accumulator = true.
Next, the next iteration of each takes place. Accumulator does not === undefined, so it moves on to the else statement. accumulator = iterator(accumulator, item).
The iterator that we wrote says return a && iterator(b) - remember a is the accumulator and b is the item - since a equals true we look on the right side, iterator(b) returns the value : undefined. Normally this would not be a problem. The accumulator becomes equal to undefined, and since undefined is falsey, in the next iteration only undefined would be returned, and would continue to be returned. Don't believe me. Try this in a repl editor:
var test = function(x, y){
return x && y;
}
var example = test(undefined, true);
example;
undefined will be returned because undefined is falsey and therefor the right side of the && operator will not be looked upon.
Try some other combinations as well:
"", "happy" = ""
"happy", "" = ""
A string is truthy, an empty string is falsey.
1, 0 = 0
0, 1 = 0
1 is truthy, 0 is falsey
So, normally. we wouldn't have a problem with our set up, but with the word undefined we have a whoops moment. The if statement in the each iteration hits true, because accumulator === undefined. This is our problem, and to fix this we need to make it so that the if statement only checks on the very first iteration. There are many different ways to do this but here's one below:
_.reduce = function(collection, iterator, accumulator) {
var initial = 0;
_.each(collection, function(item){
if(accumulator === undefined && initial === 0 ){
accumulator = item;
}
else{
accumulator = iterator(accumulator, item);
initial ++;
}
})
return accumulator;
}
In the above example, after running one time, initial is incremented so that the if statement will never again equate to true, and so will never be run again.
Is there anything left to say?
I think we've about covered it all. There is another function named some which returns true if any of the items in an array pass a truth test or are truthy, and false if none of them do or are.
Some can be written in exactly the same way with one small tweak.
Replace the && operator with the || operator.
The difference:
return false && true = returns false
return true && false = returns false
return false || true = returns true
return true || false = returns true
No comments:
Post a Comment