使用 Ramda.js 从对象中提取项目
Use Ramda.js to pull off items from object
这个问题是关于如何使用 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)
最后,为了更好的衡量,这就是我到目前为止所取得的进展。请注意,我真的很想在一个 compose/pipe 中完成此操作。
(一个物体进去,一个带有所需数据的物体出来)
const ramdaResult = R.compose(
R.path(['type', 'name']),
R.find(
R.propEq('name', myName)
)
)(R.prop('allItems', myObj))
谢谢
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
function, and specific ones for an object property (lensProp
), for an array index(lensIndex
), and for a deeper path(lensPath
),但它不包括通过 id 在数组中查找匹配值的方法。不过,我们自己制作并不难。
通过将两个函数传递给 lens
来制作镜头:一个 getter 获取对象和 returns 相应的值,以及一个 setter 获取新值和对象以及 returns 对象的更新版本。
关于镜头的一个重要事实是它们是组成的,尽管由于技术原因,您提供它们的顺序感觉与您的预期相反。
这里我们写 lensMatch
查找或设置数组中给定路径的值与提供的值匹配的值。我们写 applyLensSpec
,它的作用类似于 applySpec
,但用透镜代替了原版函数。
使用任何镜头,我们都有 view
, set
, and over
函数,分别获取、设置和更新值。这里我们只需要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
非常简单,并且很明显如何通过其他行为扩展它。 lensMatch
和 applyLensSpec
真的很有用。
这个问题是关于如何使用 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)
最后,为了更好的衡量,这就是我到目前为止所取得的进展。请注意,我真的很想在一个 compose/pipe 中完成此操作。 (一个物体进去,一个带有所需数据的物体出来)
const ramdaResult = R.compose(
R.path(['type', 'name']),
R.find(
R.propEq('name', myName)
)
)(R.prop('allItems', myObj))
谢谢
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
function, and specific ones for an object property (lensProp
), for an array index(lensIndex
), and for a deeper path(lensPath
),但它不包括通过 id 在数组中查找匹配值的方法。不过,我们自己制作并不难。
通过将两个函数传递给 lens
来制作镜头:一个 getter 获取对象和 returns 相应的值,以及一个 setter 获取新值和对象以及 returns 对象的更新版本。
关于镜头的一个重要事实是它们是组成的,尽管由于技术原因,您提供它们的顺序感觉与您的预期相反。
这里我们写 lensMatch
查找或设置数组中给定路径的值与提供的值匹配的值。我们写 applyLensSpec
,它的作用类似于 applySpec
,但用透镜代替了原版函数。
使用任何镜头,我们都有 view
, set
, and over
函数,分别获取、设置和更新值。这里我们只需要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
非常简单,并且很明显如何通过其他行为扩展它。 lensMatch
和 applyLensSpec
真的很有用。