【问题标题】:Use array.reduce to join strings with comma, but avoid the extra comma使用 array.reduce 用逗号连接字符串,但避免额外的逗号
【发布时间】:2020-09-12 04:04:52
【问题描述】:

我有一个对象数组要转换并合并为带有array.reduce 的单个字符串。

const arr = [{id: 7, task: "foo"}, {id: 22, task: "bar"}]

结果应该是7. foo, 22. bar

如果我写这段代码,它会工作,但会产生, 7. foo, 22.bar

arr.reduce((pre,cur)=> pre + `, ${cur.id}. ${cur.task}`, '')

如果没有额外的逗号,我怎样才能正确地做到这一点,最好只在 FP 中?

【问题讨论】:

    标签: javascript arrays typescript functional-programming


    【解决方案1】:

    是减少需求吗?地图更易于理解和阅读。

    arr.map(o => `${o.id}. ${o.task}`).join(',')
    

    【讨论】:

    • map 和 join(我猜 OP 可能不知道)
    【解决方案2】:

    您可以通过在开始时使用三元运算符检查 pre 是否不是假值来轻松解决此问题,例如:

    `${pre ? ', ' : ''}`
    

    const arr = [{id: 7, task: "foo"}, {id: 22, task: "bar"}]
    const res = arr.reduce((pre,cur)=> pre + `${pre ? ', ' : ''}${cur.id}. ${cur.task}`, '')
    console.log(res)

    【讨论】:

      【解决方案3】:

      您可以为第一个数组元素添加检查:

      arr.reduce((pre, cur, index) => {
        if(index === 0) {
          return `${cur.id}. ${cur.task}`
        }; 
        return pre + `, ${cur.id}. ${cur.task}`}
      , '');
      

      【讨论】:

        【解决方案4】:

        你可以使用reducer的index参数:

        const format = object => `${object.id}. ${object.task}`;
        const result = arr.reduce((pre, object, index) => (index ? pre + ', ' : '') + format(object), '');
        

        【讨论】:

          【解决方案5】:

          功能性 101

          惯用的 JavaScript 解决方案是 map-join。但是函数式编程是将程序分解为可重用的模块并创建抽象障碍-

          // Task.js
          
          const empty =
            { id: 0, task: "" }
          
          const task = (id = 0, task = "") =>
            ({ id, task })
          
          const toString = (t = empty) =>
            `${t.id}. ${t.task}`
          
          const toStringAll = ([ first, ...rest ]) =>
            rest.reduce   // <-- reduce
              ( (r, x) => r + ", " + toString(x)  
              , toString(first) 
              )
          
          export { empty, task, toString, toStringAll } 
          

          所以有一个使用reduce 的可能且合理的实现。这个程序的可读性很好,因为模块的每个部分都很小并且只做一件事。

          现在我们关闭模块上的假想盖子,忘记其中的所有复杂性。剩下的是一个干净的界面,可以清楚地传达模块的功能 -

          // Main.js
          
          import { task, toStringAll } from './Task'
          
          const data =
            [ task(7, "foo")
            , task(22, "bar")
            , task(33, "qux")
            ]
          
          console.log(toStringAll(data))
          // 7. foo, 22. bar, 33. qux
          
          console.log(toStringAll(data.slice(0,2)))
          // 7. foo, 22. bar
          
          console.log(toStringAll(data.slice(0,1)))
          // 7. foo
          
          console.log(toStringAll(data.slice(0,0)))
          // 0.
          

          展开下面的sn-p,在浏览器中验证结果-

          const empty =
            { id: 0, task: "" }
            
          const task = (id = 0, task = "") =>
            ({ id, task })
            
          const toString = (t = empty) =>
            `${t.id}. ${t.task}`
            
          const toStringAll = ([ first, ...rest ]) =>
            rest.reduce
              ( (r, x) => r + ", " + toString(x)
              , toString(first) 
              )
              
          const data =
            [ task(7, "foo")
            , task(22, "bar")
            , task(33, "qux")
            ]
          
          console.log(toStringAll(data))
          // 7. foo, 22. bar, 33. qux
          
          console.log(toStringAll(data.slice(0,2)))
          // 7. foo, 22. bar
          
          console.log(toStringAll(data.slice(0,1)))
          // 7. foo
          
          console.log(toStringAll(data.slice(0,0)))
          // 0.

          抽象的障碍

          Main.jsTask 被抽象屏障隔开。现在我们可以为toStringtoStringAll 选择任何实现,而无需Main 自己关心Task 如何在盖子下运行。

          让我们练习进行更改并更新empty 任务的表示方式。在上面的程序中,我们看到0.,但我们会改为使用Empty.。只是为了好玩,让我们尝试toStringAll 的新实现 -

          // Task.js
          
          const empty = //
          
          const task = //
          
          const toString = (t = empty) =>
            t === empty
              ? `Empty.`                // <-- custom representation for empty
              : `${t.id}. ${t.task}`
          
          const toStringAll = ([ t = empty, ...more ]) =>
            more.length === 0
              ? toString(t)
              : toString(t) + ", " + toStringAll(more) // <-- recursive
          
          //
          
          export { empty, task, toString, toStringAll }
          

          Main 不需要做任何不同的事情 -

          // Main.js
          
          import { task, toStringAll } from './Task'
          
          const data = //
          
          console.log(toStringAll(data))
          // 7. foo, 22. bar, 33. qux
          
          console.log(toStringAll(data.slice(0,2)))
          // 7. foo, 22. bar
          
          console.log(toStringAll(data.slice(0,1)))
          // 7. foo
          
          console.log(toStringAll(data.slice(0,0)))
          // Empty.
          

          展开下面的sn-p,在浏览器中验证结果-

          const empty =
            { id: 0, task: "" }
            
          const task = (id = 0, task = "") =>
            ({ id, task })
            
          const toString = (t = empty) =>
            t === empty
              ? `Empty.`
              : `${t.id}. ${t.task}`
            
          const toStringAll = ([ t = empty, ...more ]) =>
            more.length === 0
              ? toString(t)
              : toString(t) + ", " + toStringAll(more)
              
          const data =
            [ task(7, "foo")
            , task(22, "bar")
            , task(33, "qux")
            ]
          
          console.log(toStringAll(data))
          // 7. foo, 22. bar, 33. qux
          
          console.log(toStringAll(data.slice(0,2)))
          // 7. foo, 22. bar
          
          console.log(toStringAll(data.slice(0,1)))
          // 7. foo
          
          console.log(toStringAll(data.slice(0,0)))
          // Empty.

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-03-09
            • 2012-06-26
            • 2017-09-13
            • 2014-05-27
            • 2017-09-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多