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
以仅包含 startingPoint
或 true
的那些,然后我们按 pageNumber
对结果进行排序。然后,我们根据原始数据和将节点值映射到初始 nodes
属性 中的元素的函数,为每个元素创建 name
和 nodes
元素。最后,对于每一个,我们使用 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}
这与上面的工作方式类似,除了它在 name
和 nodes
.
的同时分配 id
我是 Ramda 的创始人,并且一直是它的忠实粉丝。但它并不总能为普通的现代 JS 添加任何内容。
我会将解决方案分为两个步骤:
- 使用
R.evolve
将tableItems
和nodes
准备到所需的最终状态 - 过滤、排序,然后使用R.toPairs
和tableItems
得到包含索引和对象的数组。将 nodes
按 id
分组,以便您可以在组合步骤中按 id
选择相关节点。
- 通过映射新
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>
我正在尝试在此示例中使用多个 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
以仅包含 startingPoint
或 true
的那些,然后我们按 pageNumber
对结果进行排序。然后,我们根据原始数据和将节点值映射到初始 nodes
属性 中的元素的函数,为每个元素创建 name
和 nodes
元素。最后,对于每一个,我们使用 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}
这与上面的工作方式类似,除了它在 name
和 nodes
.
id
我是 Ramda 的创始人,并且一直是它的忠实粉丝。但它并不总能为普通的现代 JS 添加任何内容。
我会将解决方案分为两个步骤:
- 使用
R.evolve
将tableItems
和nodes
准备到所需的最终状态 - 过滤、排序,然后使用R.toPairs
和tableItems
得到包含索引和对象的数组。将nodes
按id
分组,以便您可以在组合步骤中按id
选择相关节点。 - 通过映射新
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>