Search code examples
javascriptfunctional-programming

Find if a js object only has 'falsey' values recursively


Let us define a falsey value as one of the following (my own definition) JS values:

  • "" (empty string)
  • 0 (zero, or any 'variation' of it, such as 0.0, -0, etc.)
  • false (boolean)
  • [] (empty array)
  • {} (empty object)

I would like to recursively find if an object or array only contains empty values. Here would be a few examples:

  • {"x": 0} (yes, for object only consider leaf-values)
  • {"x": {"y": [0,0,[]]}} (yes)
  • [0,0,[],{}] (yes)
  • [[[]], [], {}] (yes)
  • [1] (no)

What might be a way to do this? I'd prefer a functional style since those answers always seems to be the most creative and fun to read!

So far I have something like:

let arr = [];
function isEmpty(val) {
    if (typeof(value) === 'object') {
        for (subval of val) {
            arr.push(isEmpty(subval));
        }
    } else {
       arr.push(["", 0, false, [], {}].indexOf(val) !== -1);
    }
}
// now see if the arr is something like [false, false, ...]

Solution

  • The problem with your implementation is that [] !== [] and {} !== {} which is the comparison indexOf() uses so you'll never get any matches for empty arrays and objects.

    Instead, for objects and arrays, you can check their iterable values array recursively using Array.prototype.every()

    const isEmpty = (val) => {
      if (!val) {
        // any implicitly falsy value; false, 0, "", null and undefined
        return true;
      }
      
      if (typeof val === "object") {
        // recursively check every value, returns true for an empty collection
        return Object.values(val).every(isEmpty);
      }
      
      // otherwise, it is not empty
      return false;
    }
    
    const test = [
      {"x": 0},
      {"x": {"y": [0,0,[]]}},
      [0,0,[],{}],
      [[[]], [], {}],
      [1],
    ];
    
    test.forEach((val) => {
      console.log(isEmpty(val) ? 'empty' : 'not empty', JSON.stringify(val));
    });

    Note: This only works on object and array literals. Iterators are much harder to reliably extract values from.