by Yazeed Bzadough

How to conditionally change values with when() in JavaScript

30 Seconds of Code is a brilliant collection of JavaScript snippets, digestible in ≤ 30 seconds. Anyone looking to master JavaScript should go through the entire thing.

Inspired by Ramda, I contributed when() to 30secondsofcode’s official GitHub repo. This is one my favorite functions.

when() takes 3 parameters:

  1. pred: A predicate function (must return true or false)
  2. whenTrue: A function to run if pred returns true.
  3. A value: x.

Here’s the most basic implementation:

when = (pred, whenTrue, x) => {    if (pred(x)) {        return whenTrue(x);    } else {        return x;    }}

Which you can shorten to:

when = (pred, whenTrue, x) => pred(x) ? whenTrue(x) : x;

Let’s say we want to triple even numbers

when(    (x) => x % 2 === 0,    (x) => x * 3,    2);
// 6

We got 6 because 2 is an even number. What if we pass 11?

when(    (x) => x % 2 === 0,    (x) => x * 3,    11);
// 11

A Step Further

when currently needs all 3 parameters at once. What if we could supply just the first 2, and give x later on?

when = (pred, whenTrue) => (x) => pred(x) ? whenTrue(x) : x;

This version’s what I submitted to 30secondsofcode.org. Now our code’s more flexible.

tripleEvenNums = when(    (x) => x % 2 === 0,    (x) => x * 3);
tripleEvenNums(20); // 60tripleEvenNums(21); // 21tripleEvenNums(22); // 66

Even Further Beyond

We can pass x later because when(pred, whenTrue) returns a function expecting x. What if we curry when()?

If you’re new to currying see my article on it.

A curried function doesn’t need all its parameters at once. You can supply some and get a function that takes the rest, allowing for powerful patterns.

A Silly Example

Imagine we have two lists of people, both contain a guy named Bobo.

Bobo wants a nickname for each list.

  • If we find Bobo in list 1, change his name to B Money.
  • If we find Bobo in list 2, change his name to Bo-bob.

Currying when allows us to easily write a function for each concern.

If you’re following along, here’s a curry function from 30secondsofcode.org.

curry = (fn, arity = fn.length, ...args) =>  arity <= args.length ? fn(...args) : curry.bind(null, fn, arity, ...args);

We’ll need a predicate to find Bobo.

isBobo = (person) => person.name === 'Bobo';

To keep our functions pure, we’ll need a way to immutably change a person’s name.

changeName = (newName, obj) => ({    ...obj,    name: newName});

Let’s also curry it so we can supply just newName.

changeName = curry((newName, obj) => ({    ...obj,    name: newName}));

Here’s our lists.

list1 = [{    name: 'Bobo',    id: 1,    iq: 9001}, {    name: 'Jaime',    id: 2,    iq: 9000}, {    name: 'Derek',    id: 3,    iq: 8999}];
list2 = [{    name: 'Sam',    id: 1,    iq: 600}, {    name: 'Bobo',    id: 2,    iq: 9001}, {    name: 'Peter',    id: 3,    iq: 8}];

Let’s map over list1.

doIfBobo = when(isBobo);renameToBMoney = changeName('B Money');
list1.map(doIfBobo(renameToBMoney));

Our result:

[{  "name": "B Money",  "id": 1,  "iq": 9001}, {   "name": "Jaime",   "id": 2,   "iq": 9000 }, {   "name": "Derek",   "id": 3,   "iq": 8999 }];

Because of when, we only changed Bobo and ignored everyone else!

Now map over list2.

renameToBoBob = changeName('Bo-bob');
list2.map(doIfBobo(renameToBoBob));

Our result:

[{  "name": "Sam",  "id": 1,  "iq": 600}, {   "name": "Bo-bob",   "id": 2,   "iq": 9001 }, {   "name": "Peter",   "id": 3,   "iq": 8 }];

Looks good to me! We gave Bobo his nicknames without affecting anyone else.

If you’re further interested, consider these links:

Until next time!

Take care,
Yazeed Bzadough