无论如何在 Ramda 中使用路径和占位符?

Is there anyway to use path along with placeholder in Ramda?

我在 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'])

我如何才能 1 使用纯 Ramda 获取该对象内部,前提是通过结果函数缺少 prop?

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

为了实验方便,可以用镜头设置第二个指标。用 R.useWith 包装 R.path,并使用 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>

对"Call of the Question"的回答(指导):

const fn = R.pipe(
  R.insert(2, R.__, ['a', 'b', 'd', 'e']),
  R.path(R.__, { a: { b: { c: { d: { e: 1 } } } } })
)

fn('c') //=> 1

对"Actual Question"的回答(假设数据流不合理):

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

你的问题暗示你会在知道丢失的路径键("Call of the Question")之前知道将要查询的对象。如果您在知道数据之前就知道路径键,那么只需使用以下方法来解决数据而不是路径。如果您同时知道丢失的键和数据,那么您可以只调用该函数,或者根据情况可能需要使用超出问题范围的组合器。

注意:在 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.__ 只是一种表达方式: "Hey Javascript, I don't know the value of this argument yet, please save the arguments that I do know and wait to run this function until I know the rest of the arguments." 它是围绕您在问题中提出的解决方案的语法糖(顺便说一句,是解决问题的完全可以接受的方法。

其次,你只需要再分解一下你的问题。从你知道的信息开始,向你不知道的方向努力。您的问题假定您知道路径的所有要素,除了一个;但是,您还知道缺失的 属性 的索引,即 2:

// from question
['a', 'b', property, 'd', 'e']

奇怪的是,您会知道路径的所有其他元素和未知键的索引,但是嘿,我假设这是假设的。在这种情况下,可以使用R.insertR.__来表示已知和未知信息:

R.insert(2, R.__, ['a', 'b', 'd', 'e'])

你的问题暗示你会在知道丢失的路径键("Call of the Question")之前知道将要查询的对象。

// 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来简洁地表达。

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

占位符的一个有趣用法。它当然不直接受支持,但是如何编写以这种方式使用它的函数相当简单。虽然 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>

显然,如果您不想暴露它们,您可以内联 countBlanksfillArgsfillArgs 显然有很多不同的写法。我只是觉得这种递归比循环更令人愉快。