【问题标题】:Duplicating an array's elements using functional programming使用函数式编程复制数组的元素
【发布时间】:2019-04-14 03:47:53
【问题描述】:

我正在尝试复制数组中的每个元素,但使用的是函数式样式。

我目前有这个:

["a", "b", "c"]

我得到了这个:

["a","a","b","b","c","c"]

到目前为止,我已经尝试了以下方法,将每个元素映射到一个数组,然后使用 flat() 获取一维数组。有没有更清洁的方法,因为感觉就像我在滥用地图和平面。

["a", "b", "c"].map(item => [item, item]).flat();

有没有更好的方法来做到这一点?


我试图提供一个尽可能简单的示例,但遗漏了一些细节。实际输入未排序,因为元素不可比较。 是这样的:

[
  {
    a:"a"
    b:"b"
  },
  {
    c: 1
    d: 2
  },
  {
    apple: {},
    sellers: ["me", "her"]
  }
]

重复的结果应该是这样的,其中重复的元素彼此相邻:

[
  {
    a:"a"
    b:"b"
  },
  {
    a:"a"
    b:"b"
  },
  {
    c: 1
    d: 2
  },
  {
    c: 1
    d: 2
  },
  {
    apple: {},
    sellers: ["me", "her"]
  },
  {
    apple: {},
    sellers: ["me", "her"]
  }
]

【问题讨论】:

  • 如果您将问题编辑为包括以下内容会很有帮助: 1. 输入数组是否总是按照您的示例进行排序? 2.输出的顺序应该和输入的顺序一致吗?一些答案假设输入是有序的。
  • 如果不考虑复杂性和性能,那么基本上可以使用解构[...arr,...arr].sort()。顺便说一句,函数式编程不是指箭头函数(如您在示例中使用的那样),而是指函数的行为和含义,它应该通过接收参数和返回结果来像数学函数一样工作。所以在你的情况下,你想写这样的东西(与函数式编程保持一致): const duplicateArrayItems = (arr) => [...arr,...arr].sort();让数组 = ['a','b','c'], 结果 = duplicateArrayItems(array);
  • @MattMorgan 感谢您指出这一点,我试图提供一个尽可能简单的示例,但这导致重要的细节被遗漏了。 1)输入未排序,因为真正的实际输入是不可排序的复杂对象数组。 2) 输出的顺序应该是 a,a,b,b,c,c 重复的元素彼此相邻
  • @Reek 感谢您的澄清。我已编辑我的答案以反映您的新输入数据。

标签: javascript arrays object functional-programming


【解决方案1】:

您可以使用函数reduce 并在每次迭代时连接相同的对象。

let array = ["a", "b", "c"],
    result = array.reduce((a, c) => a.concat(c, c), []);
    
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

  • Array.prototype.concat.call... 真的有必要吗?这不能由array.concat(array).sort()完成吗?
  • 有道理。仍然有不止一种方法可以做到这一点。例如,使用 slice 复制原始数组
  • array.concat(array) 将如何改变原版?文档说 “此方法不会更改现有数组,而是返回一个新数组。”
  • 这也假设原始数组已排序。如果原件是['b', 'a', 'c']怎么办?
  • 好吧,使用['b', 'a', 'c'],这将导致["a","a","b","b","c","c"],但所需的输出可能是['b', 'b', 'a', 'a', 'c', 'c'](虽然我不确定)。
【解决方案2】:

你可以这样做:

var arr = ["a", "b", "c"];
arr = arr.concat(arr).sort();

这是执行您要求做的最简单的方法之一。

【讨论】:

    【解决方案3】:

    Array.reduce 在语义上是合适的方法:获取一个对象(在这种情况下是一个数组)并返回一个不同类型的对象,或者具有不同长度或形状的对象(注意:编辑为使用 Array.push 更快每个@slider 建议的性能):

    编辑:我已编辑我的答案以反映 OP 更新的输入数据。 另请注意,此解决方案是跨浏览器和 NodeJS 兼容,无需转译。

    let data = [
      {
        a:"a",
        b:"b",
      },
      {
        c: 1,
        d: 2
      },
      {
        apple: {},
        sellers: ["me", "her"]
      }
    ];
    
    let result = data
      .reduce((acc, el) => {
        acc.push(el, el);
        return acc;
      }, []);
      
    console.log(JSON.stringify(result, null, 2));

    否则你可以map每个元素,复制它,然后组合它们:

    let data = [
      {
        a:"a",
        b:"b",
      },
      {
        c: 1,
        d: 2
      },
      {
        apple: {},
        sellers: ["me", "her"]
      }
    ];
    
    let result = data.map(item => [item, item]).reduce((acc, arr) => acc.concat(arr));
    
    console.log(JSON.stringify(result, null, 2));

    正如此处其他答案所述,这些方法中的任何一种都具有不需要对原始数组进行排序的优点。

    【讨论】:

    • concat 也接受多个参数...=> acc.concat(el, el)
    • @kemicofa 使用concat().sort(),因为一些人建议仅在对原始数组进行排序时才有效。如果您有更好的建议,请发布答案。
    • acc.concat(el).concat(el) 可能代码更少,但时间复杂度更差。我会将push(el, el)return acc 语句一起使用,因为pushO(1) 而不是concat 这是O(n),因为它正在构建一个新的数组。
    • @slider 感谢您的建议!这听起来像是一个很棒的优化。
    【解决方案4】:

    我会推荐Array.prototype.flatMap -

    const twice = x =>
      [ x, x ]
      
    console .log
      ( [ 'a', 'b', 'c' ] .flatMap (twice) // [ 'a', 'a', 'b', 'b', 'c', 'c' ]
      , [ 1, 2, 3, 4, 5 ] .flatMap (twice) // [ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 ]
      )

    flatMap 对各种事情都有用 -

    const tree =
      [ 0, [ 1 ], [ 2, [ 3 ], [ 4, [ 5 ] ] ] ]
      
    const all = ([ value, ...children ]) =>
      [ value ] .concat (children .flatMap (all))
      
    console .log (all (tree))
    // [ 0, 1, 2, 3, 4, 5 ]

    非常酷的东西 -

    const ranks =
      [ 'J', 'Q', 'K', 'A' ]
      
    const suits =
      [ '♡', '♢', '♤', '♧' ]
    
    console .log
      ( ranks .flatMap (r =>
          suits .flatMap (s =>
            [ [ r, s ] ]
          )
        )
      )
    
    // [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧']
    // , ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧']
    // , ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧']
    // , ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧']
    // ]

    flatMap 只是一个专门的Array.prototype.reduce,并且很容易在尚未支持Array.prototype.flatMap 的环境中实现-

    const identity = x =>
      x
    
    const flatMap = (xs = [], f = identity) =>
      xs .reduce ((r, x) => r . concat (f (x)), [])
    
    const ranks =
      [ 'J', 'Q', 'K', 'A' ]
    
    const suits =
      [ '♡', '♢', '♤', '♧' ]
    
    console.log
      ( flatMap (ranks, r =>
          flatMap (suits, s =>
            [ [ r, s ] ]
          )
        )
      )
    
    // [ ['J','♡'], ['J','♢'], ['J','♤'], ['J','♧']
    // , ['Q','♡'], ['Q','♢'], ['Q','♤'], ['Q','♧']
    // , ['K','♡'], ['K','♢'], ['K','♤'], ['K','♧']
    // , ['A','♡'], ['A','♢'], ['A','♤'], ['A','♧']
    // ]

    【讨论】:

    • 这真的很好,尽管在 Microsoft 和 Node 支持之前实际用途有限。
    • @MarkMeyer flatMap 只是一个专门的reduce - 我已经更新了答案以展示如何在不支持Array.prototype.flatMap 的环境中使用flatMap
    【解决方案5】:

    最简单的解决方案是使用flatMap():

    const source = ["a", "b", "c"];
    const result = source.flatMap(item => [item, item]);
    

    ['a','a','b','b','c','c']

    一点经典:

    let source = ["a", "b", "c"];
    
    const originalLength = source.length;
    for(let i = 0; i <= originalLength + (originalLength - 2); i++) {
      source.splice(i, 0, source[i++]);
    }
    

    ['a','a','b','b','c','c']

    【讨论】:

      猜你喜欢
      • 2018-03-13
      • 2019-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多