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:
pred
: A predicate function (must returntrue
orfalse
)whenTrue
: A function to run ifpred
returnstrue
.- 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); // 60
tripleEvenNums(21); // 21
tripleEvenNums(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 toB Money
. - If we find
Bobo
in list 2, change his name toBo-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: