Functional programming is not only about purity, it's also about reusability and separation of concerns. It's difficult to write a big complex function, and even harder to test and maintain it. Following functional principles will help us avoid pain and discomfort.
Let's start by isolating the behaviours we care about. We identify functions push
, update
, and pushKey
-
const identity = x =>
x
const push = (a = [], value) =>
a.concat([ value ])
const update = (o = {}, key = "", t = identity) =>
({ ...o, [key]: t(o[key]) })
const pushKey = (o = {}, key = "", value) =>
update(o, key, a => push(a, value))
This allows you to perform basic immutable transformations easily -
const d1 = { a: [1], b: [] }
const d2 = pushKey(d1, "a", 2)
const d3 = pushKey(d2, "b", 3)
const d4 = pushKey(d3, "c", 4)
console.log(d1) // { a: [1], b: [] }
console.log(d2) // { a: [1, 2], b: [] }
console.log(d3) // { a: [1, 2], b: [3] }
console.log(d4) // { a: [1, 2], b: [3], c: [4] }
Expand the snippet below to run the program in your own browser -
const identity = x =>
x
const push = (a = [], value) =>
a.concat([ value ])
const update = (o = {}, key = "", t = identity) =>
({ ...o, [key]: t(o[key]) })
const pushKey = (o = {}, key = "", value) =>
update(o, key, a => push(a, value))
const d1 = { a: [1], b: [] }
const d2 = pushKey(d1, "a", 2)
const d3 = pushKey(d2, "b", 3)
const d4 = pushKey(d3, "c", 4)
console.log(JSON.stringify(d1)) // { a: [1], b: [] }
console.log(JSON.stringify(d2)) // { a: [1, 2], b: [] }
console.log(JSON.stringify(d3)) // { a: [1, 2], b: [3] }
console.log(JSON.stringify(d4)) // { a: [1, 2], b: [3], c: [4] }
This allows you to separate your complex conditional logic into its own function -
const updateByCondition = (o = {}, conditions = [], ...) =>
{ if (...)
return pushKey(o, "foo", someValue)
else if (...)
return pushKey(o, "bar", someValue)
else
return pushKey(o, "default", someValue)
}
The advantages to this approach are numerous. push
, update
, and pushKey
are all very easy to write, test, and maintain, and they're easy to reuse in other parts of our program. Writing updateByCondition
was much easier because we had better basic building blocks. It's still difficult to test due to whatever complexity you are trying to encode, however it is much easier to maintain due to separation of concerns.