Javier Arroyo
Kilo Guru

From time to time the need arises to check if a value exists. Not if it's explicitly this or that rather, whether the value "exists" or not. In JS when a value is evaluated in a condition, the result is said to be either: true or false. A true or false evaluate is known as either truthy or falsy.  

Anything not in the falsy values below is considered a truthy.

Falsy Values
  1. undefined
  2. null
  3. NaN
  4. 0
  5. ""
  6. false

The basic approach to solving the problem is to use the if/else statement to carry out whatever logic it is required. Simple enough, no biggy.

if (someValue) {
    doSometing

} else { 
    doSomethingElse 
  
}

If you are anything like me. You begin wondering if anything can increase flexibility to the goal of passing around the value along with the functions to execute against it. See, I only want to code what is happening, not how to get there. I basically want to pass a value along with what to do with it based on the result of truthy an falsy. and I don't want to do it every time i need it.

The solution can eventually be targeted to any if/else, but that's for another time.

function onIffy (value) {
    return function fork (failure, success) {

        return value ?
            success(value) :
            failure(value);

    };

}

So the if/else solution above, in form of a ternary, jumps into my mind. There is the iffy check..

It's fair to ask, Javier you lazy bastard. Why don't you type the darn basic thing, and are so worry about even reading it. It's so basic.

I know, I know. It's just a preference to be able to pass the value and the algorithms to execute as parameters.. I want to be able to compose something, hand it to something else and let it do the check for me. It also makes my eyes hurt when code is riddle with if/else every-time there is a decision to make. Didn't I do just that over there, and over there, and there, and over here, etc.

So. The above function does it. It says: 
given a value, execute a function on the left (failure) or the one on the right (success). Right is success, you know because right is, well... correct. So the function that does something on success has to be on the right. (I didn't make the rules. Dont be mad at me. Lefty is wrong, while righty is right) 🙂

To call the function above can be a lil ugly when unfamiliar.  But, before going anywhere, lets create some algorithms to execute against the falsy/truthy check.

function isSuccess (value) {
    gs.debug("{0} was truthy", value);

}

function isFailure (value) {
    gs.debug("{0} was falsey", value);

}

isSuccess and isFailure above will be the functions to be execute when the check for our value fails or succeeds. This means that the next time a need to truthy/falsy anything, I'm only concentrating on behavior to be carried out. I will fully concentrate on what is happening and care not one thought whether if fails or not. I just want it to execute.

How it's called is a different. It's not good or bad. Just different. Depending on all sorts of personal biases is how we'll feel about using it.
 

onIffy(1)(isFailure, isSuccess);
onIffy(true)(isFailure, isSuccess);
onIffy('true')(isFailure, isSuccess);
onIffy('false')(isFailure, isSuccess);
onIffy([])(isFailure, isSuccess);
onIffy({})(isFailure, isSuccess);

onIffy(change.getValue('assignment_group'))(isFailure, isSuccess);

gs.debug('\n\n');

onIffy(false)(isFailure, isSuccess);
onIffy(0)(isFailure, isSuccess);
onIffy(null)(isFailure, isSuccess);
onIffy(undefined)(isFailure, isSuccess);
onIffy('')(isFailure, isSuccess);
onIffy(NaN)(isFailure, isSuccess);

The calls above replace the if/else truthy check. Further articulation is also possible but this is a fine start. The code above says. Hey, I'm going to give you this value, and based on whatever it is, execute one of these two functions. Remember. Because lefties are wrong, because righties are right, the isFailure always goes as the first parameter and isSuccess as the second (left/right). Note the getValue above. Well, that's a GlideRecord[change_requeset]. 

like this:

var change = ChangeRequest().get('cf16b6f3db8b08908870894d0b961964');

 

But, say we really don't like how that looks and think there ought to be something a little more explicit. Say we are railroad workers and only think of the straight and forks on the road. Well, lets wrap that bad boy and make the train riders happy.

function OnIffy2 (value) {

    return {
        fork: fork,
        valueOf: valueOf

    };

    function fork (failure, success) {
        return value ?
            OnIffy2(success(value)) :
            OnIffy2(failure(value));
    }

    function valueOf (defaultValue) {
        return value || defaultValue;

    }

}

Above we've got the beginnings of some sort of api. OnIffy2 takes a value, exposing two functions. Fork and valueOf. Fork is just that. A fork on the road. It either goes left or right depending on that truthy/falsy behavior.  the valueOf is just that. the value of the parameter passed in to OnIffy. Becaue nothing is happening to the value in isSuccess or IsFailure the value will be the same as passed in. If something had morphed value, valueOf would have returned it true state.

Now that the api has changed, the function calls must be changed.

OnIffy2(1).fork(isFailure, isSuccess);
OnIffy2(true).fork(isFailure, isSuccess);
OnIffy2('true').fork(isFailure, isSuccess);
OnIffy2('false').fork(isFailure, isSuccess);
OnIffy2([]).fork(isFailure, isSuccess);
OnIffy2({}).fork(isFailure, isSuccess);

OnIffy2(change.getValue('assignment_group')).fork(isFailure, isSuccess);

gs.debug('\n\n');

OnIffy2(false).fork(isFailure, isSuccess);
OnIffy2(0).fork(isFailure, isSuccess);
OnIffy2(null).fork(isFailure, isSuccess);
OnIffy2(undefined).fork(isFailure, isSuccess);
OnIffy2('').fork(isFailure, isSuccess);
OnIffy2(NaN).fork(isFailure, isSuccess);

 

The outcomes of both onIffy and OnIffy2:

*** Script: [DEBUG] 1 was truthy
*** Script: [DEBUG] true was truthy
*** Script: [DEBUG] true was truthy
*** Script: [DEBUG] false was truthy
*** Script: [DEBUG] {0} was falsey
*** Script: [DEBUG] [object Object] was truthy
*** Script: [DEBUG] 


*** Script: [DEBUG] null was falsey
*** Script: [DEBUG] 


*** Script: [DEBUG] false was falsey
*** Script: [DEBUG] 0 was falsey
*** Script: [DEBUG] null was falsey
*** Script: [DEBUG] {0} was falsey
*** Script: [DEBUG]  was falsey
*** Script: [DEBUG] � was falsey
*** Script: [DEBUG] 


*** Script: [DEBUG] 1 was truthy
*** Script: [DEBUG] true was truthy
*** Script: [DEBUG] true was truthy
*** Script: [DEBUG] false was truthy
*** Script: [DEBUG] {0} was falsey
*** Script: [DEBUG] [object Object] was truthy
*** Script: [DEBUG] 


*** Script: [DEBUG] null was falsey
*** Script: [DEBUG] 


*** Script: [DEBUG] false was falsey
*** Script: [DEBUG] 0 was falsey
*** Script: [DEBUG] null was falsey
*** Script: [DEBUG] {0} was falsey
*** Script: [DEBUG]  was falsey
*** Script: [DEBUG] � was falsey

Note that one of the outcomes is that False is truthy. that's because a string literal of "False" does exist. So it is truthy not falsy.

But, what if you say. You are out of your mind, mate. I don't like where you are going. I like to type if/else. So. for that we switch and instead of doing the same if/else, a function is introduced just to further introduce flexibility with the ability to pass a function around as a parameter. Like this:

function identity (value) {
  return value;
}

now, while this will be a waste in something like

var result = value ? isSuccess(value) : isFailure(value);

It works wonders in other places: say [].filter()

var checks = [true, false, null, undefined, {}, [], NaN, '', 'true', 'false'];
var truthys = checks.filter(identity);

Now there is no need to code a conditional check every time one is needed. 

function identity (value) {
  return value;
}

var checks = [true, false, null, undefined, {}, [], NaN, '', 'true', 'false'];
var truthys = checks.filter(identity);

gs.debug( truthys);

output:

*** Script: [DEBUG] true,[object Object],true,false

In summary

While the idea is not radical, or the benefit not obvious at first, the strategies above can mean a world of cohesion to code. It helps limit code noise to precisely what is needed. Eventually little things like this make code so flexible and easy to read that it will become challenging to return to telling the compiler how each step of an algorithm must be carried out.

Happy SNowing... 

 

Comments
Javier Arroyo
Kilo Guru

All the checks above would translate to the code below when being a lil more imperative. 

if (1) {
    isSuccess(1);

} else {
    isFailure(1);

}

if (true) {
    isSuccess(true);

} else {
    isFailure(true);

}

if ('true') {
    isSuccess('true');

} else {
    isFailure('true');

}

if ('false') {
    isSuccess('false');

} else {
    isFailure('false');

}

if ([]) {
    isSuccess([]);

} else {
    isFailure([]);

}

if ({}) {
    isSuccess([]);

} else {
    isFailure({});

}

if (change.getValue('assignment_group')) {
    isSuccess(change.getValue('assignment_group'));

} else {
    isFailure(change.getValue('assignment_group'));

}


if (false) {
    isSuccess(false);

} else {
    isFailure(false);

}

if (0) {
    isSuccess(0);

} else {
    isFailure(0);

}

if (null) {
    isSuccess(null);

} else {
    isFailure(null);

}

if (undefined) {
    isSuccess(undefined);

} else {
    isFailure(undefined);

}

if ('') {
    isSuccess('');

} else {
    isFailure('');

}

if (NaN) {
    isSuccess(NaN);

} else {
    isFailure(NaN);

}

 

Version history
Last update:
‎08-21-2020 06:58 PM
Updated by: