Ramda - 如何在同一个数据结构上使用多个函数

Ramda - how to use multiple functions on the same data structure

我正在尝试在此示例中使用多个 ramda 函数:

const data = {
  "tableItems": [
    {
      "id": 1,
      "name": "1",
      "startingPoint": true,
      "pageNumber": 15,
      "nodes": [
        100,
        200
      ]
    },
    {
      "id": 2,
      "name": "2",
      "startingPoint": true,
      "pageNumber": 14,
      "nodes": [
        300,
        400
      ]
    }
  ],
  "nodes": [
    {
      "id": 100,
      "tableItemId": 1,
      "content": "test"
    },
    {
      "id": 200,
      "tableItemId": 1,
      "content": "test"
    },
    {
      "id": 300,
      "tableItemId": 2,
      "content": "test"
    },
    {
      "id": 400,
      "tableItemId": 2,
      "content": "test"
    }
  ]
}

我正在尝试创建新的 JSON,它应该看起来像这样,其中 nodes 数组应该用另一个 ramda 函数填充:

const newJSON = [
  {
    "id": "chapter-1",
    "name": "2",
    "nodes": []
  },
  {
    "id": "chapter-2",
    "name": "1",
    "nodes": []
  }
]

我开始于:

let chapters = [];
let chapter;

const getChapters = R.pipe(
  R.path(['tableItems']),
  R.sortBy(R.prop('pageNumber')),
  R.map((tableItem) => {
    if(tableItem.startingPoint) {
      chapter = {
        id: `chapter-${chapters.length+1}`,
        name: tableItem.name,
        nodes: []
      }
      chapters.push(chapter);
    }
    return tableItem
  })
)

但是如何组合需要访问 data 的整个范围的 getNodes

我尝试了 pipe 但有些东西不起作用。

示例:

const getNodes = R.pipe(
  R.path(['nodes']),
  R.map((node) => {
    console.log(node)
  })
)

R.pipe(
  getChapters,
  getNodes
)(data)

如有任何帮助,我们将不胜感激。

您可以使用 curried function。因为管道总是会将上一个函数调用的结果通过管道传递给下一个函数。如果要跳过,可以使用R.tap

但是,我猜您想在 getNodes 函数中同时拥有 data 对象和上一个函数调用的输出。在这种情况下,您可以使用柯里化函数,将前一个函数的响应作为最后一个参数传递。

const getNodes = R.curryN(2, function(data, tableItemList){
  console.log(tableItemList) // result of previous function call

  return R.pipe(
     R.path(['nodes']),
     R.map((node) => {
       console.log('node:', node);
    })
  )(data)
})

并像这样使用它:

R.pipe(
 getChapters,
  getNodes(data)
)(data)

我们可以使用 Ramda 编写这样的东西:

const {pipe, sortBy, prop, filter, map, applySpec, identity, propEq, find, __, addIndex, assoc} = R

const transform = ({tableItems, nodes}) => pipe (
  filter (prop ('startingPoint')),
  sortBy (prop ('pageNumber')),
  map (applySpec ({
    name: prop('name'),
    nodes: pipe (prop('nodes'), map (pipe (propEq ('id'), find (__, nodes))), filter (Boolean))
  })),
  addIndex (map) ((o, i) => assoc ('id', `chapter-${i + 1}`, o))
) (tableItems)


const data = {tableItems: [{id: 1, name: "1", startingPoint: true, pageNumber: 15, nodes: [100, 200]}, {id: 2, name: "2", startingPoint: true, pageNumber: 14, nodes: [300, 400]}], nodes: [{id: 100, tableItemId: 1, content: "test"}, {id: 200, tableItemId: 1, content: "test"}, {id: 300, tableItemId: 2, content: "test"}, {id: 400, tableItemId: 2, content: "test"}]}

console .log (transform (data))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>

首先我们过滤 tableItems 以仅包含 startingPointtrue 的那些,然后我们按 pageNumber 对结果进行排序。然后,我们根据原始数据和将节点值映射到初始 nodes 属性 中的元素的函数,为每个元素创建 namenodes 元素。最后,对于每一个,我们使用 addIndex (map).

添加 chapter-# id 元素

这有效,而且并不可怕。我相信,要使这完全没有意义,需要做一些工作。而且我觉得不值得......特别是因为这个 Ramda 版本没有为更简单的香草实现添加任何东西:

const transform = ({tableItems, nodes}) =>
  tableItems 
    .filter (x => x .startingPoint)
    .sort (({pageNumber: a}, {pageNumber: b}) => a - b)
    .map (({name, nodes: ns}, i) => ({
      id: `chapter-${i + 1}`,
      name,
      nodes: ns .map (n => nodes .find (node => node .id == n)) .filter (Boolean)
    }))

const data = {tableItems: [{id: 1, name: "1", startingPoint: true, pageNumber: 15, nodes: [100, 200]}, {id: 2, name: "2", startingPoint: true, pageNumber: 14, nodes: [300, 400]}], nodes: [{id: 100, tableItemId: 1, content: "test"}, {id: 200, tableItemId: 1, content: "test"}, {id: 300, tableItemId: 2, content: "test"}, {id: 400, tableItemId: 2, content: "test"}]}

console .log (transform (data))
.as-console-wrapper {max-height: 100% !important; top: 0}

这与上面的工作方式类似,除了它在 namenodes.

的同时分配 id

我是 Ramda 的创始人,并且一直是它的忠实粉丝。但它并不总能为普通的现代 JS 添加任何内容。

我会将解决方案分为两个步骤:

  1. 使用R.evolvetableItemsnodes准备到所需的最终状态 - 过滤、排序,然后使用R.toPairstableItems得到包含索引和对象的数组。将 nodesid 分组,以便您可以在组合步骤中按 id 选择相关节点。
  2. 通过映射新 tableItems 并使用 R.applySpec 创建属性,将两个属性组合起来创建最终结果。

const {pipe, evolve, filter, prop, sortBy, toPairs, groupBy, map, applySpec, path, flip, pick} = R

const transform = pipe(
  evolve({ // prepare
    tableItems: pipe(
      filter(prop('startingPoint')),
      sortBy(prop('pageNumber')),
      toPairs
    ),
    nodes: groupBy(prop('id'))
  }),
  ({ tableItems, nodes }) => // combine
    map(applySpec({
      id: ([i]) => `chapter-${+i + 1}`,
      name: path([1, 'name']),
      nodes: pipe(path([1, 'nodes']), flip(pick)(nodes)),
    }))(tableItems)
)

const data = {tableItems: [{id: 1, name: "1", startingPoint: true, pageNumber: 15, nodes: [100, 200]}, {id: 2, name: "2", startingPoint: true, pageNumber: 14, nodes: [300, 400]}], nodes: [{id: 100, tableItemId: 1, content: "test"}, {id: 200, tableItemId: 1, content: "test"}, {id: 300, tableItemId: 2, content: "test"}, {id: 400, tableItemId: 2, content: "test"}]}

console.log(transform(data))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>