markdown lodash / fp设置和流程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了markdown lodash / fp设置和流程相关的知识,希望对你有一定的参考价值。

# lodash/fp - set / flow

In this gist we are going to learn about basic goodies that we get from the library lodash/fp 
(_fp_ stands for *functional programming*, great for ensuring immutability).We'll learn just by doing (step by step guided sample + codepens).

We'll cover lodash *set* and *flow* functions

# Steps

- We'll use _codepen_ as our playground, navigate to this page:

https://codepen.io/

- Click on _create >> new pen_  (a new blank pen will be created for you to play with).

- Import _lodash/fp_ from the CDN: go to _settings_, click on the _javascript_ tags and on Add Extrernal Javascript, include this CDN link:

```
https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)
```

> If we are working locally, we can _npm install lodash_ and then import it using something like _import {flow, set} from 'lodash/fp';_

- In this sample, we want to manage a client hotel booking, let's suposse we are working with a complex entity that contains nested objects, let paste this entity (for the sake of this sample, this is a simplified entity):

```javascript
const myBooking = {
  id: '23484',
  client: {
      id: '458982',
      name: 'John Doe',
      email: 'john.doe@test.com',
      vip: false,
      business: false,
      family: false,
  }, 
  rooms: [
     {
       type: 'standard',
       beds: 2,       
     },
     {
       type: 'standard',
       beds: 1,       
     }
  ],
  extras: {
     lateCheckout: false,
     spa: false,
     minibar: false,
     seasights: false,
  }
}
```

- Now let's assume another developer has build a smart system based on client reputation, it will offer some extras at no cost, he will provide us with a list of settings to toggle (upgrade), some dynamic patterns could be _"client.vip"_, or _["lateCheckout", "spa"]_ or even  _"room[0].type"_ and on the other hand we want to keep our original booking object immutable, what can we do? split / loops / parsing...? lodash to the rescue !

- Let's start by creating a booking upgrade method, here we'll dynamically receive the property and value that requires to be updated and using lodash _set_ we'll just traverse through the properties, set the right value and return a new object including the changes. Let's add the following code in our code pen:

```javascript 
const upgradeBooking = (propertypath, value, booking) => {
   return _.set(propertypath, value, booking);
}
```
> [Link](https://lodash.com/docs/4.17.4#set) to the lodash official documentation (_set_), this is the mutable version, fp(immutable) version move the "object" value to the last position of the params list instead of the first.

- We are going to give a try to the function:

```javascript
const updatedBookingA = upgradeBooking('client.vip', true, myBooking);

console.log('##### Original Booking (does not mutate) #####');
console.log(myBooking);
console.log('#########');
console.log('##### Updated Booking #####');
console.log(updatedBookingA);
console.log('#########');
```

The result that we get dumped into the console:

```diff
"##### Original Booking (does not mutate) #####"

Object {
  client: Object {
    business: false,
    email: "john.doe@test.com",
    family: false,
    id: "458982",
    name: "John Doe",
    vip: false
  },
  extras: Object {
    lateCheckout: false,
    minibar: false,
    seasights: false,
    spa: false
  },
  id: "23484",
  rooms: [Object {
  beds: 2,
  type: "standard"
}, Object {
  beds: 1,
  type: "standard"
}]
}
"#########"
"##### Updated Booking #####"

Object {
  client: Object {
    business: false,
    email: "john.doe@test.com",
    family: false,
    id: "458982",
    name: "John Doe",
+    vip: true
  },
  extras: Object {
    lateCheckout: false,
    minibar: false,
    seasights: false,
    spa: false
  },
  id: "23484",
  rooms: [Object {
  beds: 2,
  type: "standard"
}, Object {
  beds: 1,
  type: "standard"
}]
}
"#########"
```

- It's time to test the sample (click on Run button, or if you have Auto-Updating the sample will already be run). In the console we can see the original object and the updated one.

- What if the "smart system" recommends us to upgrade the first room booked to a superior one? do we need to update or _upgradeClient_ function to handle the array? The answer is no, we can do it by calling:

```javascript
const updatedBookingB = upgradeBooking('rooms[0].type', 'suite', updatedBookingA);
console.log(updatedBookingB);
```
- Let's say on the extras side we only get the name of the extra property but not the full path, what could we do to get the full path? string interpolation to the rescue:


```javascript
const extraToUpgrade = 'lateCheckout';
const updatedBookingC = upgradeBooking(`extras.${extraToUpgrade}`, true, updatedBookingB);
console.log(updatedBookingC);
```

> Another option could be to use the param array approach implementation from set.

```javascript
const updatedBookingC = upgradeBooking(['extras',extraToUpgrade], true, updatedBookingB);
```


The final object that we get after applying the transformations:

```diff
Object {
  client: Object {
    business: false,
    email: "john.doe@test.com",
    family: false,
    id: "458982",
    name: "John Doe",
+    vip: true
  },
  extras: Object {
+    lateCheckout: true,
    minibar: false,
    seasights: false,
    spa: false
  },
  id: "23484",
  rooms: [Object {
  beds: 2,
+  type: "suite"
}, Object {
  beds: 1,
  type: "standard"
}]
}
```

> Want to give a try? Check the working [codepen sample](https://codepen.io/Lemoncode/pen/WEzxYZ?editors=1111)

- _Performing this dynamic updates and in an immutable manner is great, but I wouldn't like to be creating a BookingA, BookingB, BookingC... objects, is there any easy wat to chain / compose this_ The answer is yes, lodash _flow_ does that job for you, we could simplify all the previous work by doing:

```javascript
const extraToUpgrade = 'lateCheckout';

const finalBooking = _.flow(
  _.set('client.vip', true),
  _.set('rooms[0].type', 'suite'),
  _.set(`extras.${extraToUpgrade}`, true),
)(myBooking);

console.log(finalBooking);
```

> [About flow](https://lodash.com/docs/4.17.4#flow): What this does under the hood: it invokes the first function passing _myBooking_ as last parameter (applying currying), then each successive invocation is supplied the return value of the previous.

> Want to give a try? Check the working [codepen sample](https://codepen.io/Lemoncode/pen/gxeMZw?editors=1112)

- _That's awesome, but I would like to keep my 'upgradeBooking' function that I have created before, what can I do?_ In order to do this we need to currify our _upgradeClient function_ ([more info about currying plus sample](https://gist.github.com/brauliodiez/1e358bd8745f846d1e11518b5abc4e73)).

```diff
- const upgradeBooking = (propertypath, value, booking) => {
+ const upgradeBoooking = (propertypath, value) => (booking) => {
   return _.set(propertypath, value, booking);
}
```

- Now we can use it inside lodash flow:

```javascript
const finalBooking = _.flow(
  upgradeBooking('client.vip', true),
  upgradeBooking('rooms[0].type', 'suite'),
  upgradeBooking(`extras.${extraToUpgrade}`, true),
)(myBooking);

console.log(finalBooking);
```

> We could use as well lodash curry helper and keep our function curry free.

```javascript
const upgradeBooking = _.curry((propertypath, value, booking) => {
   return _.set(propertypath, value, booking);
});


const finalBooking = _.flow(
  upgradeBooking('client.vip', true),
  upgradeBooking('rooms[0].type', 'suite'),
  upgradeBooking(`extras.${extraToUpgrade}`, true),
)(myBooking);

```

> Want to give a try? Check the working [codepen for this sample](https://codepen.io/Lemoncode/pen/jLzrdZ?editors=1112)

- So far so good, that was cool, but as a colleague says _code is read more than is written_, it would be a good idea to group all this "magic" into something more human readable, to help the next developer that will jump into this code (or yourself in two months time ;)) on understanding what is this code doing. What do you think about this approach?

Creating some helper functions (adding semanthic)

```javascript
const upgradeBasic = (key, value) => (booking) =>
  upgradeBooking(`client.${key}`, value)(booking);

const upgradeRoom = (key, value) => (booking) =>
  upgradeBooking(`rooms[0].${key}`, value)(booking);

const upgradeExtras = (key, value) => (booking) =>
  upgradeBooking(`extras.${key}`, value)(booking);
```

- Now the sequence inside flow it's more readable:

```javascript
const finalBooking = _.flow(
  upgradeBasic('vip', true),
  upgradeRoom('type', 'suite'),
  upgradeExtras(extraToUpgrade, true),
)(myBooking);

console.log(finalBooking);
```
> Want to give a try? Check the working [codepen for this sample](https://codepen.io/Lemoncode/pen/OjvXqp?editors=1112)

Hope you have enjoyed this gist, I have compiled the working sample in several codepens:

- [Basic sample](https://codepen.io/Lemoncode/pen/WEzxYZ?editors=1111)

- [Refactor A](https://codepen.io/Lemoncode/pen/gxeMZw?editors=1112)

- [Refactor B](https://codepen.io/Lemoncode/pen/jLzrdZ?editors=1112)

- [Refactor C](https://codepen.io/Lemoncode/pen/OjvXqp?editors=1112)





以上是关于markdown lodash / fp设置和流程的主要内容,如果未能解决你的问题,请参考以下文章

2048:使用 lodash/fp 的地图中 reduceRight 的奇怪行为

如何在 HTML 中使用来自 lodash/fp 的管道?

进阶学习2:函数式编程FP——闭包纯函数Lodash柯里化

函数式 js 接口实现原理,以及 lodash/fp 模块

markdown lodash.md

markdown [Lodash] #cheatsheet