Data Transformations with Lodash
Kyle Coberly
kylecoberly.com
"Compose a data transformation pipeline with `lodash/fp`"
What's a data transformation?
{
users: {
1: {
name: "Amelia Bedelia",
},
2: {
name: "Bob Barker",
}
},
availabilities: [{
date: "2020-09-01",
startTime: 12,
duration: 1,
user_id: 1,
},{
date: "2020-09-01",
startTime: 14,
duration: 2,
user_id: 1,
},{
date: "2020-09-03",
startTime: 18,
duration: 2,
user_id: 2,
}],
}
{
users: [{
id: 1,
name: "Amelia Bedelia",
days: {
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
},
},{
id: 2,
name: "Bob Barker",
days: {
"2020-09-03": [{
startTime: 18,
duration: 2,
}],
},
}],
}
Common Transformations
- map/flatMap/mapValues - Same count, but changed
- filter/reject/where - Same or fewer count
- find - First match
- groupBy - Turn values into keys
- assign/merge/concat - Combine two things
What is Lodash?
Stop Writing Functions That...
- Get the difference between two arrays
- Map over the values of an object
- Debounce a function
- Get random values from an array
- Sort a collection by a property
- Clamp numbers
Functional Functions
// Plain ole JS
[1, 2, 3].map(number => number * 2)
// Sexy Lodash
const { map } = require("lodash/fp")
map(number => number *2 )([1, 2, 3])
Not a method
Curried
Data Last
What is composing?
Custom Functions!
const { map } = require("lodash/fp")
const double = number => number * 2
const doubleAll = map(double)
doubleAll([1, 2, 3])
What is a pipeline?
Transformation Pipelines!!
const { flow } = require("lodash/fp")
const someMath = flow([
doubleAll,
doubleAll,
onlyOver10,
halfAll
sum,
])
someMath(numbers)
{
users: {
1: {
name: "Amelia Bedelia",
},
2: {
name: "Bob Barker",
}
},
availabilities: [{
date: "2020-09-01",
startTime: 12,
duration: 1,
user_id: 1,
},{
date: "2020-09-01",
startTime: 14,
duration: 2,
user_id: 1,
},{
date: "2020-09-03",
startTime: 18,
duration: 2,
user_id: 2,
}],
}
{
users: [{
id: 1,
name: "Amelia Bedelia",
days: {
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
},
},{
id: 2,
name: "Bob Barker",
days: {
"2020-09-03": [{
startTime: 18,
duration: 2,
}],
},
}],
}
The Goal
function transformImperative({ users, availabilities }){
const transformedUsers = []
for (let id in users){
const user = users[id]
user.id = +id
user.days = {}
for (let availability of availabilities){
if (availability.user_id != id){
continue;
}
const date = availability.date
if (!user.days[date]){
user.days[date] = []
}
delete availability.date
delete availability.user_id
user.days[date].push(availability)
}
transformedUsers.push(user)
}
return { users: transformedUsers }
}
function transformImperative({ users, availabilities }){
const transformedUsers = []
for (let id in users){
const user = users[id]
user.id = +id // Mutation
user.days = {} // Mutation
for (let availability of availabilities){
if (availability.user_id != id){
continue;
}
const date = availability.date
if (!user.days[date]){
user.days[date] = [] // Mutation
}
delete availability.date // Mutation
delete availability.user_id // Mutation
user.days[date].push(availability) // Mutation
}
transformedUsers.push(user) // Mutation
}
return { users: transformedUsers }
}
function transformImperative({ users, availabilities }){
const transformedUsers = []
for (let id in users){
const user = users[id]
user.id = +id // Mutation
user.days = {} // Mutation
for (let availability of availabilities){
if (availability.user_id != id){
continue;
}
const date = availability.date
if (!user.days[date]){
user.days[date] = [] // Mutation
}
delete availability.date // Mutation
delete availability.user_id // Mutation
// vvv ALSO RELIES ON MUTABLE STATE!! vvv
user.days[date].push(availability) // Mutation
}
transformedUsers.push(user) // Mutation
}
return { users: transformedUsers }
}
Imperative Solution
Imperative Solution
- Hard to decompose
- Hard to test
- Hard to debug
- Hard to reuse
- Hard to read
Step 1: Group Availabilities by User
[{
date: "2020-09-01",
startTime: 12,
duration: 1,
user_id: 1,
},{
date: "2020-09-01",
startTime: 14,
duration: 2,
user_id: 1,
},{
date: "2020-09-03",
startTime: 18,
duration: 2,
user_id: 2,
}]
{
1: [{
date: "2020-09-01",
startTime: 12,
duration: 1,
},{
date: "2020-09-01",
startTime: 14,
duration: 2,
}],
2: [{
date: "2020-09-03",
startTime: 18,
duration: 2,
}]
}
Step 2: Group Availabilities by Date
[{
date: "2020-09-01",
startTime: 12,
duration: 1,
},{
date: "2020-09-01",
startTime: 14,
duration: 2,
},{
date: "2020-09-03",
startTime: 18,
duration: 2,
}]
{
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
"2020-09-03": [{
startTime: 18,
duration: 2,
}],
}
Step 3: Wrap Availabilities
{
1: {
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
},
2: {
"2020-09-03": [{
startTime: 14,
duration: 2,
}],
}
}
{
1: {
days: {
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
},
},
2: {
days: {
"2020-09-03": [{
startTime: 14,
duration: 2,
}],
},
},
}
Step 4: Merge Users and Availabilities
{
1: {
days: {
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
},
},
}
{
1: {
name: "Amelia Bedelia",
},
}
{
1: {
name: "Amelia Bedelia",
days: {
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
},
},
}
Step 5: Hash to Array
{
1: {
name: "Amelia Bedelia",
days: {
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
},
},
}
[{
name: "Amelia Bedelia",
id: 1,
days: {
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
},
}]
{
users: {
1: {
name: "Amelia Bedelia",
},
2: {
name: "Bob Barker",
}
},
availabilities: [{
date: "2020-09-01",
startTime: 12,
duration: 1,
user_id: 1,
},{
date: "2020-09-01",
startTime: 14,
duration: 2,
user_id: 1,
},{
date: "2020-09-03",
startTime: 18,
duration: 2,
user_id: 2,
}],
}
{
users: [{
id: 1,
name: "Amelia Bedelia",
days: {
"2020-09-01": [{
startTime: 12,
duration: 1,
},{
startTime: 14,
duration: 2,
}],
},
},{
id: 2,
name: "Bob Barker",
days: {
"2020-09-03": [{
startTime: 18,
duration: 2,
}],
},
}],
}
Booyah Achieved
Questions?
kylecoberly.com
Data Transformations with Lodash
By Kyle Coberly
Data Transformations with Lodash
Meetup talk for DenverScript, August 2020
- 1,303