反正在Ramda中有使用占位符的路径吗?

时间:2019-07-15 21:55:39

标签: javascript ramda.js

我正在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放入该对象?

3 个答案:

答案 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并切换参数。但是,其他库对composepipe的定义更严格,这会将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.insertR.__可用于表示已知和未知信息:

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 } } } } })

因此,将它们放在一起:

  1. 直到我们用丢失的密钥调用fn时,我们才知道完整的路径。
  2. 一旦我们知道了路径,就可以调用R.path来检索值。

这种逐步的数据流是基本功能组成,可以用R.pipe简洁地表示。

同样,您会知道数据和除一个以外的所有路径键,这很奇怪。但是,上述方法和思考过程可以转移到您的实际情况中,这可能依赖R.insert,但要求您构建自己的道路逐步,然后将所述路径传递到R.path

答案 1 :(得分:0)

在这种情况下,用函数包装是一个很好的解决方案,这就是我要用的。

为了实验起见,您可以使用镜头设置第二个索引。将R.pathR.useWith换行,并使用R.setR.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>

很显然,如果您不希望公开它们,可以内联countBlanksfillArgs。显然,有许多不同的编写fillArgs的方式。我只是觉得这种递归比循环更令人愉快。