使用 Ramda 重构 if 语句
Refactoring if statements using Ramda
我刚开始使用 Ramda,所以当我在博客数组 post 上执行映射函数(在我的 gatsby 应用程序中)时,在该映射中我使用了简单的 if 语句。但我想以正确的方式做到这一点,一直使用 Ramda,但我发现它有很多选择。我肯定已经从 Whosebug 和其他地方找到并尝试了许多示例,并且已经能够获取类别但不是基于这些类别的整个博客 post。因此,虽然我意识到那里有示例,但我正在努力在我的实例中正确应用它们。关于我将如何重构这段代码有什么意见吗?
const featured_post = [];
const hrt_category = [];
const work_category = [];
const prep_category = [];
const ed_category = [];
{map(
({ node }) => {
if (node.categories.some(e => e.slug === 'featured')) {
featured_post.push(node);
}
if (node.categories.some(e => e.slug === 'hormones')) {
hrt_category.push(node);
}
if (node.categories.some(e => e.slug === 'estrogen')) {
work_category.push(node);
}
if (node.categories.some(e => e.slug === 'prep')) {
prep_category.push(node);
}
if (node.categories.some(e => e.slug === 'ed')) {
ed_category.push(node);
}
},posts
)}
非常感谢任何帮助。
在我看来,这是对 Ramda 的滥用(免责声明:我是 Ramda 的创始人。)
Ramda 就是处理不可变数据。尽管 Ramda 函数的内部可能会改变局部变量,但它们绝不会改变任何其他东西。
使用 map
来创建诸如推送到全局变量之类的副作用对于 Ramda 来说绝对是不匹配的。
除此之外,我不喜欢这种重复的代码。我的看法会完全不同。我不会使用所有这些全球帖子集合。
这是一种可能性:
const groupPosts = (names, posts) =>
Object .fromEntries (names .map (name => [
name,
posts .filter (
({node: {categories}}) => categories .some (({slug}) => slug == name)
) .map (({node}) => node)
]))
const posts = [{node: {id: 1, categories: [{slug: 'prep'}, {slug: 'ed'}]}}, {node: {id: 2, categories: [{slug: 'estrogen'}]}}, {node: {id: 3, categories: [{slug: 'ed'}]}}, {node: {id: 4, categories: [{slug: 'hormones'}, {slug: 'prep'}]}}, {node: {id: 5, categories: [{slug: 'featured'}]}}, {node: {id: 6, categories: [{slug: 'prep'}]}}, {node: {id: 7, categories: [{slug: 'estroogen'}]}},]
console .log (JSON .stringify (
groupPosts (['featured', 'hormones', 'estrogen', 'prep', 'ed'], posts)
, null, 2))
.as-console-wrapper {max-height: 100% !important; top: 0}
但是如果你想要那些单独的子集合,我们可以从中提取匹配单个子集合的函数:
const matchSlugs = (name) => (posts) =>
posts .filter (
({node: {categories}}) => categories .some (({slug}) => slug == name)
) .map (({node}) => node)
我们会像 const featured_posts = matchSlugs ('featured') (posts)
一样使用它,我们的原始函数将是一个简单的注释:
const sortPosts = (names) => (posts) =>
Object .fromEntries (names .map (name => [name, matchSlugs (name) (posts)]))
请注意,这还没有在任何地方使用 Ramda。我们可以开始更改它以使用 Ramda 更好的 map
和 filter
函数,使用 toPairs
代替 Object .fromEntries
,可能使用 Ramda 的 any
代替.some
。它可能会使它更清晰一些,如果您正在学习 Ramda,我建议您尝试一下。但这只是锦上添花。关键在于简化数据结构和使用它们的代码。
更新
OP 发布了 matchSlugs
的基于 Ramda 的重构并寻求反馈。
以下是将该版本重构为完全无点版本的一系列操作:
OP的解决方案(由于注释不允许我们显示布局,因此使用我自己的布局):
const matchSlugs = (name) => (posts) =>
map (
({node}) => node,
filter(
({node: {categories}}) => any ((({slug}) => slug == name)) (categories),
posts
)
);
拉出posts
参数后:
const matchSlugs2 = (name) =>
pipe (
filter(({node: {categories}}) => any ((({slug}) => slug == name)) (categories)),
map (prop ('node'))
)
此版本将现有节点的过滤与结果的最终映射分开,并且通过将这两个步骤放入 pipe
调用中,允许我们删除第二个参数。请注意 pipe
需要一些函数,而 returns 需要一个函数。这保留了上述行为。
清理filter
中的解构参数后:
const matchSlugs3 = (name) =>
pipe (
filter (
pipe (
path (['node', 'categories']),
any (({slug}) => slug == name)
)
),
map (prop ('node'))
)
这里我们用path (['node', 'categories'])
来代替({node: {categories}}
参数。这涉及到另一个 pipe
调用,我希望稍后清理它。
用propEq
替换匿名函数后
const matchSlugs4 = (name) =>
pipe (
filter (pipe (
path (['node', 'categories']),
any (propEq ('slug', name))
)),
map (prop ('node'))
)
这只是一个小修复,但 propEq
对我来说比 lambda 函数更清晰。它还将允许我们进行下一次重构。
重构移除name
参数后:
const matchSlugs5 = pipe (
propEq ('slug'),
any,
flip (o) (path (['node', 'categories'])),
filter,
o (map (prop ('node')))
)
这是最大的一步。我们使用 o
的两个实例,Ramda 的柯里化二进制版本 compose
,将其变成一个无点函数。这对于使函数无点化非常有用,但感觉相当晦涩。 (o
这个名字是为了提醒我们数学组合符号,∘
。)第一次我们需要 flip
o
,因为 Ramda 没有 pipe
风格的 o
.
对于某些Ramda用户来说,这将是最终目标,实现功能的无积分版本。有时这非常可读,但我个人认为这最后一步 降低了 可读性。我可能会回到早期的有针对性的版本,或者更好的是,回到受此版本启发的版本,但要点已恢复:
const matchSlugs6 = (name) => (posts) =>
map (
prop('node'),
filter (
compose (any (propEq ('slug', name)), path (['node', 'categories'])),
posts
)
)
对我来说,这个版本是最易读的版本。但是口味不同,您可能会发现其中的另一个版本或完全不同的版本最具可读性。
您可以通过 slug 将节点缩减为节点的对象,然后按规定的顺序选择请求的 slug:
const { mergeWith, concat, reduce, pick } = R
const mergeConcat = mergeWith(concat)
const groupPosts = (names, posts) => pick( // pick the names you want in the correct order
names,
reduce((p, { node }) => mergeConcat(p, // merge with the general categories
reduce((c, { slug }) => // create an object of node by categories
({ ...c, [slug]: [node] }), {}, node.categories)
), {}, posts)
)
const posts = [{node: {id: 1, categories: [{slug: 'prep'}, {slug: 'ed'}]}}, {node: {id: 2, categories: [{slug: 'estrogen'}]}}, {node: {id: 3, categories: [{slug: 'ed'}]}}, {node: {id: 4, categories: [{slug: 'hormones'}, {slug: 'prep'}]}}, {node: {id: 5, categories: [{slug: 'featured'}]}}, {node: {id: 6, categories: [{slug: 'prep'}]}}, {node: {id: 7, categories: [{slug: 'estroogen'}]}},]
console.log (JSON.stringify (
groupPosts (['featured', 'hormones', 'estrogen', 'prep', 'ed'], posts)
, null, 2))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>
我刚开始使用 Ramda,所以当我在博客数组 post 上执行映射函数(在我的 gatsby 应用程序中)时,在该映射中我使用了简单的 if 语句。但我想以正确的方式做到这一点,一直使用 Ramda,但我发现它有很多选择。我肯定已经从 Whosebug 和其他地方找到并尝试了许多示例,并且已经能够获取类别但不是基于这些类别的整个博客 post。因此,虽然我意识到那里有示例,但我正在努力在我的实例中正确应用它们。关于我将如何重构这段代码有什么意见吗?
const featured_post = [];
const hrt_category = [];
const work_category = [];
const prep_category = [];
const ed_category = [];
{map(
({ node }) => {
if (node.categories.some(e => e.slug === 'featured')) {
featured_post.push(node);
}
if (node.categories.some(e => e.slug === 'hormones')) {
hrt_category.push(node);
}
if (node.categories.some(e => e.slug === 'estrogen')) {
work_category.push(node);
}
if (node.categories.some(e => e.slug === 'prep')) {
prep_category.push(node);
}
if (node.categories.some(e => e.slug === 'ed')) {
ed_category.push(node);
}
},posts
)}
非常感谢任何帮助。
在我看来,这是对 Ramda 的滥用(免责声明:我是 Ramda 的创始人。)
Ramda 就是处理不可变数据。尽管 Ramda 函数的内部可能会改变局部变量,但它们绝不会改变任何其他东西。
使用 map
来创建诸如推送到全局变量之类的副作用对于 Ramda 来说绝对是不匹配的。
除此之外,我不喜欢这种重复的代码。我的看法会完全不同。我不会使用所有这些全球帖子集合。
这是一种可能性:
const groupPosts = (names, posts) =>
Object .fromEntries (names .map (name => [
name,
posts .filter (
({node: {categories}}) => categories .some (({slug}) => slug == name)
) .map (({node}) => node)
]))
const posts = [{node: {id: 1, categories: [{slug: 'prep'}, {slug: 'ed'}]}}, {node: {id: 2, categories: [{slug: 'estrogen'}]}}, {node: {id: 3, categories: [{slug: 'ed'}]}}, {node: {id: 4, categories: [{slug: 'hormones'}, {slug: 'prep'}]}}, {node: {id: 5, categories: [{slug: 'featured'}]}}, {node: {id: 6, categories: [{slug: 'prep'}]}}, {node: {id: 7, categories: [{slug: 'estroogen'}]}},]
console .log (JSON .stringify (
groupPosts (['featured', 'hormones', 'estrogen', 'prep', 'ed'], posts)
, null, 2))
.as-console-wrapper {max-height: 100% !important; top: 0}
但是如果你想要那些单独的子集合,我们可以从中提取匹配单个子集合的函数:
const matchSlugs = (name) => (posts) =>
posts .filter (
({node: {categories}}) => categories .some (({slug}) => slug == name)
) .map (({node}) => node)
我们会像 const featured_posts = matchSlugs ('featured') (posts)
一样使用它,我们的原始函数将是一个简单的注释:
const sortPosts = (names) => (posts) =>
Object .fromEntries (names .map (name => [name, matchSlugs (name) (posts)]))
请注意,这还没有在任何地方使用 Ramda。我们可以开始更改它以使用 Ramda 更好的 map
和 filter
函数,使用 toPairs
代替 Object .fromEntries
,可能使用 Ramda 的 any
代替.some
。它可能会使它更清晰一些,如果您正在学习 Ramda,我建议您尝试一下。但这只是锦上添花。关键在于简化数据结构和使用它们的代码。
更新
OP 发布了 matchSlugs
的基于 Ramda 的重构并寻求反馈。
以下是将该版本重构为完全无点版本的一系列操作:
OP的解决方案(由于注释不允许我们显示布局,因此使用我自己的布局):
const matchSlugs = (name) => (posts) => map ( ({node}) => node, filter( ({node: {categories}}) => any ((({slug}) => slug == name)) (categories), posts ) );
拉出
posts
参数后:const matchSlugs2 = (name) => pipe ( filter(({node: {categories}}) => any ((({slug}) => slug == name)) (categories)), map (prop ('node')) )
此版本将现有节点的过滤与结果的最终映射分开,并且通过将这两个步骤放入
pipe
调用中,允许我们删除第二个参数。请注意pipe
需要一些函数,而 returns 需要一个函数。这保留了上述行为。清理
filter
中的解构参数后:const matchSlugs3 = (name) => pipe ( filter ( pipe ( path (['node', 'categories']), any (({slug}) => slug == name) ) ), map (prop ('node')) )
这里我们用
path (['node', 'categories'])
来代替({node: {categories}}
参数。这涉及到另一个pipe
调用,我希望稍后清理它。用
替换匿名函数后propEq
const matchSlugs4 = (name) => pipe ( filter (pipe ( path (['node', 'categories']), any (propEq ('slug', name)) )), map (prop ('node')) )
这只是一个小修复,但
propEq
对我来说比 lambda 函数更清晰。它还将允许我们进行下一次重构。重构移除
name
参数后:const matchSlugs5 = pipe ( propEq ('slug'), any, flip (o) (path (['node', 'categories'])), filter, o (map (prop ('node'))) )
这是最大的一步。我们使用
o
的两个实例,Ramda 的柯里化二进制版本compose
,将其变成一个无点函数。这对于使函数无点化非常有用,但感觉相当晦涩。 (o
这个名字是为了提醒我们数学组合符号,∘
。)第一次我们需要flip
o
,因为 Ramda 没有pipe
风格的o
.
对于某些Ramda用户来说,这将是最终目标,实现功能的无积分版本。有时这非常可读,但我个人认为这最后一步 降低了 可读性。我可能会回到早期的有针对性的版本,或者更好的是,回到受此版本启发的版本,但要点已恢复:
const matchSlugs6 = (name) => (posts) =>
map (
prop('node'),
filter (
compose (any (propEq ('slug', name)), path (['node', 'categories'])),
posts
)
)
对我来说,这个版本是最易读的版本。但是口味不同,您可能会发现其中的另一个版本或完全不同的版本最具可读性。
您可以通过 slug 将节点缩减为节点的对象,然后按规定的顺序选择请求的 slug:
const { mergeWith, concat, reduce, pick } = R
const mergeConcat = mergeWith(concat)
const groupPosts = (names, posts) => pick( // pick the names you want in the correct order
names,
reduce((p, { node }) => mergeConcat(p, // merge with the general categories
reduce((c, { slug }) => // create an object of node by categories
({ ...c, [slug]: [node] }), {}, node.categories)
), {}, posts)
)
const posts = [{node: {id: 1, categories: [{slug: 'prep'}, {slug: 'ed'}]}}, {node: {id: 2, categories: [{slug: 'estrogen'}]}}, {node: {id: 3, categories: [{slug: 'ed'}]}}, {node: {id: 4, categories: [{slug: 'hormones'}, {slug: 'prep'}]}}, {node: {id: 5, categories: [{slug: 'featured'}]}}, {node: {id: 6, categories: [{slug: 'prep'}]}}, {node: {id: 7, categories: [{slug: 'estroogen'}]}},]
console.log (JSON.stringify (
groupPosts (['featured', 'hormones', 'estrogen', 'prep', 'ed'], posts)
, null, 2))
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js" integrity="sha512-rZHvUXcc1zWKsxm7rJ8lVQuIr1oOmm7cShlvpV0gWf0RvbcJN6x96al/Rp2L2BI4a4ZkT2/YfVe/8YvB2UHzQw==" crossorigin="anonymous"></script>