编写 Ramda 函数

Compose Ramda Functions

您好,我正在学习 Ramda 库并且很喜欢它。我正在尝试练习一些函数概念,例如 curryimmutability。下面我有一些代码,基本上是尝试 assoc 来自一个 object 的值并将其复制到另一个 object。第一个 object kdlJsonObj 具有 cost 值,我想将其附加到另一个对象

//object from API
var kdlJsonObj = [
  {name: 'AAA COOPER TRANSPORTATION', cost: 33},
  {name: 'OLD DOMINION FREIGHT LINE, INC', cost: 22},
  {name: 'ROADRUNNER  TRANSPORTATION SERVICES', cost: 31}
]

// objects to assoc 
var aaa = {shortName: 'AAA Cooper', name: 'AAA COOPER  TRANSPORTATION' }
var odlf = {shortName: 'Old Dominion', name: 'OLD DOMINION FREIGHT LINE, INC'}
var rr = {shortName: 'Road Runner', name: 'ROADRUNNER  TRANSPORTATION SERVICES'}

// Ramda functions that I would like to compose    
var namePropEq = R.propEq('name')
var namePropEqAAA = namePropEq('AAA COOPER TRANSPORTATION')
var findAAA = R.find(namePropEqAAA, kdlJsonObj) 
var costProp = R.prop('cost')
var costAAA = costProp(findAAA)
var assocCost = R.assoc('cost')
var assocCostAAA = assocCost(costAAA)(aaa)
assocCostAAA // => {shortName: "AAA Cooper", name: "AAA COOPER TRANSPORTATION", cost: 33}

我希望能够编写这些函数集,使其成为一种更加无意义的编码风格,在我进行调用之前不提供任何数据。理想情况下,它应该是 var assocCostAAA = composeAssoc(namePropEqAAA)(aaa) 之类的东西,我可以只调用一个函数。由于 arity 规则

,我不确定 compose 这个函数是否可行
var composeAssoc = R.compose(
   R.assoc('cost'),
   R.find(name, kdlJsonObj),   // has two arity so i believe this is not correct
   R.propEq(name))

我愿意用不同的方式来做这件事。例如使用 Ramda 函数,例如 R.pluckR.filter 甚至 R.lens。但我希望它成为一个 composed/declarative 函数。

我希望有更优雅的方式,但这是无意义的:

const source = [
  { name: 'Aaa', cost: 1 },
  { name: 'Bee', cost: 2 },
  { name: 'Cee', cost: 3 },
];

const target = { shortName: 'A', name: 'Aaa' };

const func =
  R.chain(
    R.assoc('cost'),          // ('cost', 1, target)  ->  output
    R.compose(
      R.prop('cost'),         // ('cost', {name: 'Aaa', cost: 1})  ->  1
      R.compose(
        R.find(R.__, source), // (predicate, source)  ->  {name: 'Aaa', cost: 1}
        R.compose(
          R.propEq('name'),   // ('name', 'Aaa' )  ->  predicate
          R.prop('name'),     // ('name', target)  ->  'Aaa'
        ),
      ),
    ),
  );

const targetWithCost = func(target);

输出:{"cost": 1, "name": "Aaa", "shortName": "A"}

Run with Ramda REPL here!!

哦,是的……这个好一点:

const func =
  R.chain(
    R.assoc('cost'),        // ('cost', 1, target)  ->  :)
    R.compose(
      R.prop('cost'),       // ('cost', {name: 'Aaa', cost: 1})  ->  1
      R.find(R.__, source), // (predicate, source)  ->  {name: 'Aaa', cost: 1}
      R.eqProps('name'),    // ('name', target)  ->  predicate
    ),
  );

输出:{"cost": 1, "name": "Aaa", "shortName": "A"}

Run with Ramda REPL here!!

正如 Scott 指出的那样,我的解决方案并非完全无点,因此作为实验,我想出了这个完全无点的代码:

const func =
  R.converge(
    R.assoc('cost'),      // ('cost', 1, target)  -> {shortName: 'A', name: 'Aaa', cost: 1}
    [
      R.useWith(          // (target, source)  ->  1
        R.compose(        // (predicate, source)  ->  1
          R.prop('cost'), // ('cost', {name: 'Aaa', cost: 1})  ->  1
          R.find,         // (predicate, source)  ->  {name: 'Aaa', cost: 1}
        ),
        [
          R.eqProps('name'), // ('name', target)  ->  predicate
          R.identity,     // source  ->  source
        ],
      ),
      R.identity,         // (target, source)  ->  target
    ],
  );

const targetWithCost = func(target, source);

Run it here in the Ramda REPL

这样做非常简单,不用担心无点:

const {curry, assoc, prop, find, eqProps} = R;

const kdlJsonObj = [
  {name: 'AAA COOPER TRANSPORTATION', cost: 33},
  {name: 'OLD DOMINION FREIGHT LINE, INC', cost: 22},
  {name: 'ROADRUNNER  TRANSPORTATION SERVICES', cost: 31}
]

const companies = [
  {shortName: 'AAA Cooper', name: 'AAA COOPER TRANSPORTATION' },
  {shortName: 'Old Dominion', name: 'OLD DOMINION FREIGHT LINE, INC'},
  {shortName: 'Road Runner', name: 'ROADRUNNER  TRANSPORTATION SERVICES'},
  {shortName: 'Flintstone', name: 'FRED FLINTSTONE HAULING'}
]

const [aaa, odfl, rr, ff] = companies

const addCost = curry((costs, company) => assoc(
  'cost', 
  prop('cost', find(eqProps('name', company), costs)), 
  company
))

console.log(addCost(kdlJsonObj)(rr))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

虽然这可以变得毫无意义,但结果的可读性可能会差很多。我尝试将 point-free 视为一种工具,当(且仅当)它提高可读性时使用,但你的里程可能会有所不同。

这也不做任何真正的错误检查。如果没有找到匹配的值,就加上cost: undefined.

还要注意这是多么脆弱。 post 中 AAA 的两个版本之间存在拼写差异。如果它们不是从同一源生成的,那么如果您依赖于长字符串之间的匹配,您可能会遇到这样的问题。我对此没有特别的建议,但值得研究。

@Scott Sauyet 我更新了你的一些原因有几个。 1) 我在 NetSuite 上,他们使用 Rhino 6 而没有使用 ES6 规范。 2)我是新手,所以虽然我喜欢你非常简洁的风格,但我把我的风格分解了一点,这样我就能更好地理解它。谢谢您的帮助。

var kdlJsonObj = [
  {name: 'AAA COOPER TRANSPORTATION', cost: 33},
  {name: 'OLD DOMINION FREIGHT LINE, INC', cost: 22},
  {name: 'ROADRUNNER  TRANSPORTATION SERVICES', cost: 31}
]

 var aaa = {shortName: 'AAA Cooper', name: 'AAA COOPER TRANSPORTATION' }
 var odlf = {shortName: 'Old Dominion', name: 'OLD DOMINION FREIGHT LINE, INC'}
 var rr = {shortName: 'Road Runner', name: 'ROADRUNNER  TRANSPORTATION SERVICES'}


var addCost = function(costs, company) {
  var comp = R.find(R.eqProps('name', company), costs)
  var cost = R.prop('cost', comp)
  return R.assoc('cost', cost, company)
}

var addCostCurried = R.curry(addCost)(kdlJsonObj)
var rrAssocatied = addCostCurried(rr)
var odflAssocatied = addCostCurried(odlf)
var aaaAssocatied = addCostCurried(aaa)