简体   繁体   English

使用 Ramda 重构 if 语句

[英]Refactoring if statements using Ramda

I'm new to using Ramda so when I performed a map function (in my gatsby app) on an array of blog posts, within that map I used simple if statements.我是使用 Ramda 的新手,所以当我在一系列博客文章中执行 map function(在我的 gatsby 应用程序中)时,在 map 中,我使用了简单的 if 语句。 But I want to do this the right way, using Ramda all the way through, but I'm finding its many options overwhelming.但我想以正确的方式做到这一点,一直使用 Ramda,但我发现它有很多选择。 I've definitely found and tried many examples from StackOverflow and elsewhere and have been able to grab the categories but not the whole blog post based on those categories.我确实从 StackOverflow 和其他地方找到并尝试了许多示例,并且能够获取类别,但不能获取基于这些类别的整个博客文章。 So while I realize there are examples out there I am struggling to apply them correctly in my instance.因此,虽然我意识到有一些例子,但我正在努力在我的实例中正确应用它们。 Any opinions about how I would refactor this code?关于我将如何重构此代码的任何意见?

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

Any help is much appreciated.任何帮助深表感谢。

To my mind, this is a misuse of Ramda (disclaimer: I'm a Ramda founder.)在我看来,这是对 Ramda 的滥用(免责声明:我是 Ramda 的创始人。)

Ramda is all about working with immutable data. Ramda 致力于处理不可变数据。 Although the internals of Ramda functions may mutate local variables, they never alter anything else.尽管 Ramda 函数的内部结构可能会改变局部变量,但它们永远不会改变其他任何东西。

Using map to create side-effects such as pushing to global variables is definitely a mismatch for Ramda.使用map来创建诸如推送到全局变量之类的副作用对于 Ramda 来说绝对是不匹配的。

Besides that, I am not a fan of this repetitive code.除此之外,我不喜欢这种重复的代码。 My take would be radically different.我的看法会完全不同。 I wouldn't use all those global collections of posts.我不会使用所有这些全球 collections 的帖子。

Here's one possibility:这是一种可能性:

 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}

But if you want those individual subcollections, we could extract from this the function that matches a single set of them:但是,如果您想要这些单独的子集合,我们可以从中提取与其中一组匹配的 function:

const matchSlugs = (name) => (posts) =>
  posts .filter (
    ({node: {categories}}) => categories .some (({slug}) => slug == name)
  ) .map (({node}) => node)

and we would use it like const featured_posts = matchSlugs ('featured') (posts) and our original function would be an easy gloss:我们会像const featured_posts = matchSlugs ('featured') (posts)一样使用它,而我们原来的 function 将是一个简单的光泽:

const sortPosts = (names) => (posts) =>
  Object .fromEntries (names .map (name => [name, matchSlugs (name) (posts)]))

Note that this has not used Ramda anywhere.请注意,这并没有在任何地方使用 Ramda。 We could start changing it to use Ramda's nicer map and filter functions, to use toPairs in place of Object.fromEntries , possibly to use Ramda's any in place of .some .我们可以开始将其更改为使用 Ramda 更好的mapfilter功能,使用toPairs代替Object.fromEntries ,可能使用 Ramda 的any代替.some It might make this a bit cleaner, and I would recommend that you try if you're in the process of learning Ramda.它可能会使这更干净一些,如果您正在学习 Ramda,我建议您尝试一下。 But that is the icing.但这就是锦上添花。 The cake is in simplifying the data structures and the code that use them.最重要的是简化数据结构和使用它们的代码。

Update更新

The OP posted a Ramda-based refactoring of matchSlugs and asked for feedback. OP 发布了基于 Ramda 的matchSlugs重构并征求反馈意见。

Here is a series of refactorings of that version to a fully point-free version:以下是该版本到完全无点版本的一系列重构:

  1. The OP's solution (with my own layout since comments don't allow us to display layout): OP的解决方案(使用我自己的布局,因为评论不允许我们显示布局):

     const matchSlugs = (name) => (posts) => map ( ({node}) => node, filter( ({node: {categories}}) => any ((({slug}) => slug == name)) (categories), posts ) );
  2. After pulling out the posts parameter:拉出posts参数后:

     const matchSlugs2 = (name) => pipe ( filter(({node: {categories}}) => any ((({slug}) => slug == name)) (categories)), map (prop ('node')) )

    This version separates the filtering of existing nodes from the final mapping of the results, and by putting those two steps into a pipe call, allows us to remove the second argument.此版本将现有节点的过滤与结果的最终映射分开,通过将这两个步骤放入pipe调用中,我们可以删除第二个参数。 Note that pipe takes some functions and returns a function.请注意, pipe接受一些函数并返回 function。 This retains the behavior above.这保留了上述行为。

  3. After cleaning up the destructured parameter in filter :清理filter中的解构参数后:

     const matchSlugs3 = (name) => pipe ( filter ( pipe ( path (['node', 'categories']), any (({slug}) => slug == name) ) ), map (prop ('node')) )

    Here we use path (['node', 'categories']) to replace the ({node: {categories}} parameter. That involves another pipe call, which I hope to clean up later.这里我们使用path (['node', 'categories'])来替换({node: {categories}}参数。这涉及到另一个pipe调用,我希望稍后清理。

  4. After replacing the anonymous function with propEqpropEq替换匿名 function 后

    const matchSlugs4 = (name) => pipe ( filter (pipe ( path (['node', 'categories']), any (propEq ('slug', name)) )), map (prop ('node')) )

    This is just a minor fix, but propEq reads more cleanly to me than the lambda function.这只是一个小修复,但propEq比 lambda function 对我来说更清晰。 It also will allow us to do the next refactoring.它还将允许我们进行下一次重构。

  5. After refactoring to remove the name parameter:重构后去掉name参数:

     const matchSlugs5 = pipe ( propEq ('slug'), any, flip (o) (path (['node', 'categories'])), filter, o (map (prop ('node'))) )

    This is the largest step here.这是这里最大的一步。 We turn this into a point-free function, using two instances of o , Ramda's curried binary version of compose .我们使用o的两个实例(Ramda 的compose的咖喱二进制版本)将其变成无点 function。 This is very useful in making functions point-free, but can feel fairly obscure.这对于使函数无点非常有用,但会让人感觉相当晦涩。 (The name o is meant to remind us of the mathematical composition sign, .) The first time we need to flip o , since Ramda does not have a pipe -style version of o . (名字o是为了提醒我们数学合成符号 。)我们第一次需要flip o ,因为 Ramda 没有pipe风格的o版本。

For some Ramda users, this would be the ultimate goal, achieving a point-free version of the function.对于一些 Ramda 用户来说,这将是最终目标,实现 function 的无点版本。 There are times when that is quite readable, but I personally feel that this final step has reduced readability.有时它的可读性很强,但我个人认为这最后一步降低了可读性。 I might go back to an earlier pointed version, or, even better, to a version inspired by this one, but with the points reinstated:我可能会 go 回到更早的指向版本,或者,甚至更好,回到受此启发的版本,但恢复了点:

const matchSlugs6 = (name) => (posts) =>
  map (
    prop('node'), 
    filter (
      compose (any (propEq ('slug', name)), path (['node', 'categories'])),
      posts
    )
  )

To me this version is the most readable of the bunch.对我来说,这个版本是最易读的。 But tastes differ, and you may find another one of these or an entirely different version most readable.但是口味不同,您可能会发现其中的另一种或完全不同的版本最易读。

You can reduce the nodes to an object of nodes by slugs, and then pick the requested slug in the stated order:您可以通过 slug 将节点减少到 object 节点,然后按规定的顺序选择请求的 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>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM