When working with objects in JavaScript sometimes it's convenient to transform them to arrays. In this short post I will show you how to do it in plain JavaScript and also with Rambda.js.
Let's say that we have a nested JavaScript object that looks like this. If you work with Firebase Firestore, or some of Google Cloud Platform's or AWS API, you might have stumbled on structures like these.
const obj = {
id: 'one',
services: {
'service-foo': {
slug: 'foo',
public: true,
readonly: true,
},
'service-bar': {
slug: 'bar',
public: false,
readonly: true,
users: ['john', 'jane'],
},
'service-baz': {
slug: 'baz',
public: false,
readonly: false,
users: ['bo', 'bill', 'brooke'],
},
},
};
Say we want to display the services
property as a list on the page. While it's possible to loop over the object itself, it's more convenient to loop over a list that looks like this.
[
{
id: 'service-foo',
slug: 'foo',
public: true,
readonly: true,
users: [ 'frank', 'finn', 'fernando' ]
},
{
id: 'service-bar',
slug: 'bar',
public: false,
readonly: true,
users: [ 'john', 'jane' ]
},
{
id: 'service-baz',
slug: 'baz',
public: false,
readonly: false,
users: [ 'bo', 'bill', 'brooke' ]
}
]
How can we transform our our services
property object to an array?
Transforming with plain JavaScript
One obvious way is to do it in plain JavaScript. Like this.
const transform = obj => Object.keys(obj).map(id => ({ id, ...obj[id] }));
console.log(trasform(obj.services));
We map over object's keys and construct a new object consisting of the object's key and its properties with the help of the ES spread operator.
Transforming object with Rambda.js
I was curious how to achieve the same results with Rambda - a functional programming library for JavaScript.
Turns out there are two ways to do it.
First one is to use Ramda's pipe operator.
const R = require('rambda');
const transform= R.pipe(
R.toPairs,
R.map(([id, props]) => ({ id, ...props }))
);
console.log(trasform(obj.services));
Second is to use Ramda's compose operator.
const R = require('rambda');
const transform = R.compose(
R.map(([id, props]) => ({ id, ...props })),
R.toPairs
);
console.log(trasform(obj.services));
Both results in the same output. It's only a matter of taste. If you are coming form an FP background, compose
will look more natural to you.
Let's break down Rambda's toPairs operator. Maybe a better name for it would be toTuple
.
console.log(R.toPairs(obj.services));
[
[
'service-foo',
{ slug: 'foo', public: true, readonly: true, users: [Array] }
],
[
'service-bar',
{ slug: 'bar', public: false, readonly: true, users: [Array] }
],
[
'service-baz',
{ slug: 'baz', public: false, readonly: false, users: [Array] }
]
]
The toPairs
operator loops over object's keys and returns key and property a list with two items in it - [key, prop]
.
In functional programming you often work with lists as they are easier to manipulate.
Rambda - pipe vs compose
What the difference when it comes to the two composition operators in Rambda - pipe
and compose
? It's subtle. In my opinion it's a matter of taste.
The pipe
operator feels most intuitive to developers. It works like a pipe in Unix (ls -l | grep js
), passing the results from one function to another in the chain from left to right. First function can take multiple arguments, but the rest of the functions in the chain can only have one argument as input. It reminds me a lot about the proposed pipeline operator in JavaScript.
The compose
operator does the opposite of pipe. It performs its operations right to left.
I find it intuitive to think of it as the Russian Doll pattern - f4(f3(f2(f1([1,2,3]))))
, where each function is called inside out.
There are probably a few more ways to transform an object to array. Let me know then so I can learn something new.
Top comments (0)