使用Ramda.js从对象中提取项目

时间:2019-06-19 23:00:42

标签: ramda.js

这个问题是关于如何使用RamdaJS执行任务的。

首先,假设我有一个具有以下结构的对象:

let myObj = {
  allItems: [
    {
      name: 'firstthing',
      args: [
        {
          name: 'arg0'
        },
        {
          name: 'arg1'
        }
      ],
      type: {
        name: 'type_name_1'
      }
    },
    {
      name: 'otherthing',
      args: [
        {
          name: 'arg0'
        }
      ]
    }
  ]
}

我正在尝试创建一个看起来像这样的对象:

{
    arg0: 'arg0', // myObj.allItems[0].args[0].name
    typeName: 'type_name_1' // myObj.allItems[0].type.name
}

(我知道名称很愚蠢,arg0,typeName。这并不重要)

因此,如果我们不使用Ramda,这就是我必须执行的操作:

// The thing I'm searching for in the array (allItems)
let myName = 'firstthing';
// Here's how I'd find it in the array
let myMatch = myObj.allItems.find(item => item.name === myName);
// Here is the desired result, by manually using dot 
// notation to access properties on the object (non-functional)
let myResult = {
  arg0: myMatch.args[0].name,
  typeName: myMatch.type.name
};
// Yields: {"arg0":"arg0","typeName":"type_name_1"}
console.log(myResult)

最后,就我的目的而言,这只是一个很好的措施。请注意,我真的很想在一个撰写/管道中完成此操作。 (一个对象进入,一个带有所需数据的对象出来)

const ramdaResult = R.compose(
  R.path(['type', 'name']),
  R.find(
    R.propEq('name', myName)
  )
)(R.prop('allItems', myObj))

谢谢

1 个答案:

答案 0 :(得分:1)

applySpecpath组合使用:

const transform = applySpec ({
  arg0: path (['allItems', 0, 'args', 0, 'name']),
  typeName: path (['allItems', 0, 'type', 'name'])
})

const myObj = {allItems: [{name: 'firstthing', args: [{name: 'arg0'}, {name: 'arg1'}], type: {name: 'type_name_1'}}, {name: 'otherthing', args: [{name: 'arg0'}]}]}

console .log (
  transform (myObj)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {applySpec, path} = R                                  </script>

但是根据您的喜好,帮助程序功能可能有助于使API稍微简单一些:

const splitPath = useWith (path, [split('.'), identity] )
// or splitPath = curry ( (str, obj) => path (split ('.') (str), obj))

const transform = applySpec({
  arg0: splitPath('allItems.0.args.0.name'),
  typeName: splitPath('allItems.0.type.name'),
})

const myObj = {allItems: [{name: 'firstthing', args: [{name: 'arg0'}, {name: 'arg1'}], type: {name: 'type_name_1'}}, {name: 'otherthing', args: [{name: 'arg0'}]}]}

console .log (
  transform (myObj)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {applySpec, path, useWith, split, identity} = R                                        </script>

splitPath不适合Ramda,但它经常是我有用的功能,尤其是当路径来自我控制范围之外的来源时。

更新

是的,我确实错过了这项要求。为我服务,仅查看输入和请求的输出。总是存在多个不兼容的算法,它们对于特定的输入给出相同的结果。这是我的 mea culpa ,试图将其分解为几个可重用的功能。

为此可能最好的选择是透镜。 Ramda具有通用的lens函数,以及针对对象属性(lensProp),数组索引(lensIndex)和更深层路径(lensPath)的特定函数。 ,但其中不包括通过id在数组中查找匹配值的方法。不过,自己打造并不难。

镜头是通过将两个函数传递给lens来制成的:一个获取对象并获取相应值的吸气剂,以及获取新值和对象并返回对象的更新版本的塞特眼镜。

关于镜头的一个重要事实是它们的构成,尽管出于技术原因,您提供它们的顺序与您期望的相反。

在这里,我们编写lensMatch来查找或设置数组中的值,其中给定路径上的值与提供的值匹配。我们编写了applyLensSpec,它的作用类似于applySpec,但是代替了普通功能。

使用任何镜头,我们都有viewsetover函数,它们分别获取,设置和更新值。在这里,我们只需要view,因此从理论上讲我们可以制作一个更简单的lensMatch版本,但这可能是一个有用的可重用函数,因此我将其保留完整。

const lensMatch = (path) => (key) => 
  lens
    ( find ( pathEq (path, key) )
    , ( val
      , arr
      , idx = findIndex (pathEq (path, key), arr)
      ) =>
        update (idx > -1 ? idx : length (arr), val, arr)
    )

const applyLensSpec = (spec) => (obj) =>
  map (lens => view (lens, obj), spec)

const lensName = (name) => lensMatch (['name']) (name)

const transform = (
  name, 
  nameLens = compose(lensProp('allItems'), lensName(name))
) => applyLensSpec({
  arg0: compose (nameLens, lensPath (['args', 0, 'name']) ),
  typeName: compose (nameLens, lensPath (['type', 'name']) )
})

const myObj = {allItems: [{name: 'firstthing', args: [{name: 'arg0'}, {name: 'arg1'}], type: {name: 'type_name_1'}}, {name: 'otherthing', args: [{name: 'arg0'}]}]}


console .log (
  transform ('firstthing') (myObj)
)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {lens, find, pathEq, findIndex, update, length, map, view, compose, lensProp, lensPath} = R                                        </script>

尽管与其他解决方案相比,这可能需要更多的工作,但主要功能transform非常简单,而且很明显如何通过其他行为对其进行扩展。 lensMatchapplyLensSpec确实有用。