我正在Ramda中尝试一些操作,并且坚持使用纯Ramda尝试深入获取对象内部的属性值。
我只能将函数包装起来以接收路径的缺失部分:
const fn = property => path(['a', 'b', property, 'd', 'e']);
fn('c')({ a: { b: { c: { d: { e: 1 } } } } });
我也尝试使用R.__
,但它根本不起作用:
path(['a', 'b', __, 'd', 'e'])
如果通过结果函数缺少道具,我怎么能纯粹使用Ramda将1
放入该对象?
答案 0 :(得分:4)
回答“问题的电话”(指导性):
const fn = R.pipe(
R.insert(2, R.__, ['a', 'b', 'd', 'e']),
R.path(R.__, { a: { b: { c: { d: { e: 1 } } } } })
)
fn('c') //=> 1
“实际问题”的答案(假定数据流不合理):
const fn = R.compose(
R.path, // see NOTE
R.insert(2, R.__, ['a', 'b', 'd', 'e'])
)
fn ('c') ({ a: { b: { c: { d: { e: 1 } } } } }) //=> 1
您的问题暗示,您将知道要查询的对象,然后才知道缺少的路径键(“问题的呼叫”)。如果您在知道数据之前先知道路径键,则只需使用以下方法而不是路径来求解数据。如果您将同时知道丢失的键和数据,则可以只调用该函数,或者根据情况可能要使用超出问题范围的组合器。
注意:在Ramda中,pipe
的工作原理与compose
相反,因此您可以改用pipe
并切换参数。但是,其他库对compose
和pipe
的定义更严格,这会将pipe
的参数限制为一元函数,因此尽管在这种情况下等效,compose
是首选的
说明:
您走在正确的道路上,只需要对几个概念稍作调整即可。
首先,R.__
旨在代替未知参数(整个参数)。在这里,您错误地尝试在参数中使用R.__
。请考虑以下内容:
const add = (x, y) => x + y
const curriedAdd = R.curry(add)
// the following are equivalent
const add2 = placeholder => add(placeholder, 2)
const add2 = curriedAdd(R.__, 2)
希望从上面可以清楚地看出,R.__
只是一种表达方式:“嘿,JavaScript,我还不知道该参数的值,请保存我确实知道的参数,然后等待运行此功能,直到我知道其余参数为止。”是您在问题中提出的解决方案周围的语法糖(顺便说一句,这是解决问题的一种完全可接受的方法)。
第二,您只需要进一步分解问题即可。从您知道的信息开始,朝着您不知道的方向努力。您的问题假设您知道路径中除一个元素之外的所有元素。但是,您还知道缺少的属性的索引,即2
:
// from question
['a', 'b', property, 'd', 'e']
奇怪的是,您会知道路径的所有其他元素以及未知密钥的索引,但是,我想这是假设的。在这种情况下,R.insert
和R.__
可用于表示已知和未知信息:
R.insert(2, R.__, ['a', 'b', 'd', 'e'])
您的问题暗示:在您知道缺少的路径键(“问题的调用”)之前,您将知道要查询的对象。
// from question
{ a: { b: { c: { d: { e: 1 } } } } }
如果您不知道路径的所有元素,那么在调用R.insert
之前,您将不知道完整的路径。相反,一旦调用R.insert
,您将知道完整的路径。首先,考虑一下R.path
看起来像什么,您尚不知道路径:
R.path(R.__, { a: { b: { c: { d: { e: 1 } } } } })
因此,将它们放在一起:
fn
时,我们才知道完整的路径。R.path
来检索值。这种逐步的数据流是基本功能组成,可以用R.pipe
简洁地表示。
同样,您会知道数据和除一个以外的所有路径键,这很奇怪。但是,上述方法和思考过程可以转移到您的实际情况中,这可能不依赖R.insert
,但将要求您构建自己的道路逐步,然后将所述路径传递到R.path
。
答案 1 :(得分:0)
在这种情况下,用函数包装是一个很好的解决方案,这就是我要用的。
为了实验起见,您可以使用镜头设置第二个索引。将R.path
与R.useWith
换行,并使用R.set
与R.lensIndex
创建路径数组:
const { useWith, path, lensIndex, set, __, identity } = R;
const fn = useWith(path, [
set(lensIndex(2), __, ['a', 'b', undefined, 'd', 'e']),
identity
]);
console.log(fn('c', { a: { b: { c: { d: { e: 1 } } } } }));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
答案 2 :(得分:0)
这是占位符的有趣用法。它当然不受直接支持,但是如何编写以这种方式使用它的函数相当简单。尽管Ramda的_isPlaceholder
是严格的内部函数,但我们可以在自己的代码中使用相同的机制来构建一个函数,如您所建议的:
const countBlanks = compose (length, filter (prop ('@@functional/placeholder')))
const fillArgs = ([p = undefined, ...ps], [a, ...as], res = []) =>
p == undefined
? res
: p ['@@functional/placeholder']
? fillArgs (ps, as, [...res, a])
: fillArgs (ps, [a, ...as], [...res, p])
const partialPath = (ps) =>
curryN(countBlanks (ps) + 1, (...args) => path (fillArgs (ps, args), last (args)))
const obj = {a: {b: {c: {d: {e: 42}}}}}
console .log (
partialPath (['a', 'b', __, 'd', 'e']) ('c') (obj),
partialPath (['a', __, 'c', __, 'e']) ('b', 'd') (obj),
partialPath ([__, 'b', __, __, 'e']) ('a') ('c', 'd', obj)
)
<script src="https://bundle.run/ramda@0.26.1"></script>
<script> const {__, compose, length, filter, prop, curryN, path, last} = ramda </script>
很显然,如果您不希望公开它们,可以内联countBlanks
和fillArgs
。显然,有许多不同的编写fillArgs
的方式。我只是觉得这种递归比循环更令人愉快。