1

For arrays like [ true , 1, 'car', { a: '1' }] how can I use reduce to evaluate that the entire array is true or false? It has to consider truthy and falsey values.

Moreover, using reduce, if all values are truthy return true. If the elements are mixed with truthy and falsey values return false. If the elements are all falsey return false.

I also cannot use every. I am actually trying to implement the every method by using reduce as an assignment. It works it just doesn't pass for all-truthy results nor does it cast the result to a bool bonus question :) if I have

_.every([1], _.identity)).to.be.true;
_.every([0], _.identity)).to.be.false;

Also is {} truthy? bonus bonus question

Here's my code but it doesn't use reduce which is probably making it harder to evaluate the truthy, falsey and mixed values. Thanks for your help!


    _.every = function(collection, iterator) {
      // TIP: Try re-using reduce() here.
      var results = [];
      if ( iterator === undefined ) {
        _.each( collection ,  function( item ) { 
          results.push( item );
        });
      } else { 
        if ( Array.isArray( collection ) === true ) {
          var copiedArr = collection.slice();
          // iterate through each item using each
          _.each( collection , function( item ) { 
          // input our own iterator calling truth test
          if ( iterator( item ) === true ) { 
        // store results in array
        results.push( true )
          } else {
        results.push( false );
          }
        } );
      } else if ( typeof collection === 'object' ) {
        var copiedObj = JSON.parse(JSON.stringify( collection ));

        // iterate through each item using each
        _.each( copiedObj , function( item ) { 

            // input our own iterator calling truth test
            if ( iterator( item ) === true ) { 
              // store results in array
              results.push( true )
            } else {
              results.push( false );
            }
          });
        }
      } // end of iterator defined else

      // if array contains false return false
      if ( _.contains( results , false) ) {
        return false
      } else {
      // else return true
        return true;
      }
    } // end of _.every
    ```


Sankofa
  • 337
  • 1
  • 5
  • 15
  • 1
    *"Also is {} truthy?"* - You can check if something is truthy just by putting it in an `if`: `if({}){console.log("1")}` logs `1`, hence `{}` is truthy. AFAIK the only falsey values in JS are `false`, `null`, `0`, `NaN`, `undefined` and `""` – Nick Jan 25 '19 at 01:31
  • 1
    Is using `reduce` a requirement? – codejockie Jan 25 '19 at 01:33
  • It isn't a requirement to use reduce but I am having trouble passing all of the tests in mocha for this function. "passes for a collection of all-truthy results" is one, the other test it failed was "should cast the result to a boolean". There were 9 tests in total. Also I want to get better at understanding how to reuse code because I don't see how to use it here. In the assignment I had to write how reduce was implemented as well. @JohnKennedy – Sankofa Jan 25 '19 at 14:09
  • Ahh... Thanks @NickA I was wondering how {} could be truthy. I agree on the falsey values. I tried to change the first line that has if ( iterator( item ) === true ) to be if ( iterator ( item ) !== falsey values ) but then my code failed for All falsy results or mixed falsy results. I should be able to complete the answer today and when I do I will post the answer. I like what CertainPerformance did though. I am going to test that now. – Sankofa Jan 25 '19 at 14:13

2 Answers2

2

It sounds like you want to check whether all values nested anywhere in the structure are truthy. You can use a recursive function to check whether each value is truthy, coercing to boolean with Boolean, or if a value is an object, use .reduce and iterate over its .values (which will work for both objects and arrays). The reduce callback can be

(a, b) => a && Boolean(checkTruthy(b))

which will return the previous value accumulator if it was false, else it will return the truthyness of the current item being iterated over.

const checkTruthy = param =>
  typeof param !== 'object'
  ? Boolean(param)
  : Object.values(param)
      .reduce(
        (a, b) => a && Boolean(checkTruthy(b)),
        true
      );


console.log(checkTruthy([ true , 1, 'car', { a: '1' }]));
console.log(checkTruthy([ true , 1, 'car', { a: '1', b: 0 }]));
console.log(checkTruthy([ true , 1, 'car', { a:  0,  b: '1' }]));

Or, to be a bit more efficient but possibly less readable (YMMV), you can move the Boolean outside the .reduce:

const checkTruthy = param => Boolean(
  typeof param !== 'object'
  ? param
  : Object.values(param)
      .reduce(
        (a, b) => a && checkTruthy(b),
        true
      )
);


console.log(checkTruthy([ true , 1, 'car', { a: '1' }]));
console.log(checkTruthy([ true , 1, 'car', { a: '1', b: 0 }]));
console.log(checkTruthy([ true , 1, 'car', { a:  0,  b: '1' }]));

Or, if you don't need recursion, it's much simpler:

const checkTruthy = obj => Boolean(
  Object.values(obj)
    .reduce((a, b) => a && b)
);


console.log(checkTruthy([ true , 1, 'car', { a: '1' }]));
console.log(checkTruthy([ true , 1, 'car', true, { a: '1' }]));
console.log(checkTruthy([ true , 1, 'car', false, { a: '1' }]));
CertainPerformance
  • 260,466
  • 31
  • 181
  • 209
  • The only issue with checkTruthy is that when it gets passed falsey values it errors out. I know the question asks how to evaluate if elements are truthy but when one of those values happens to be falsey it throws an error. console.log(checkTruthy([null, 0, undefined])); @CertainPerformance – Sankofa Jan 25 '19 at 17:38
  • 1
    Oops, yep, that's because `null`'s `typeof` is `object` despite being a falsey primitive, fixed (works fine for falsey non-null values) – CertainPerformance Jan 25 '19 at 19:59
2

To implement every, you will want to use reduce with the && operation as the reducer and true as the initial value. On every value, call the predicate function, and - if you don't trust it - cast its result to a boolean:

function every(arr, predicate) {
    return arr.reduce((acc, val) => acc && Boolean(predicate(val)), true);
}

If you need your every to work on "object collections" as well, not just arrays, you probably have written your own version of reduce already that works on arbitrary collections:

function every(collection, predicate) {
    return reduce(collection, (acc, val) => acc && Boolean(predicate(val)), true);
}
Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • I had to look up Boolean because I am still a novice but to make sure I understand it correctly does the Boolean function evaluate elements if they are truthy or falsey? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean – Sankofa Jan 25 '19 at 17:45
  • I see that the accumulator was set up to start off as true. Edited previous comment. – Sankofa Jan 25 '19 at 17:47
  • 1
    @Sankofa Yes, `Boolean` does exactly that. Alternatively [you could use `!!`](https://stackoverflow.com/q/784929/1048572) – Bergi Jan 25 '19 at 18:28
  • Aw man that's cool. Got it. I also like this pneumonic lol Remember it by "bang, bang you're boolean" – Sankofa Jan 25 '19 at 18:36