【问题标题】:Join sequence of arrays with array delimeters ("intersperse")使用数组分隔符连接数组序列(“intersperse”)
【发布时间】:2017-02-03 11:14:40
【问题描述】:

是否有一个函数可以让我连接多个数组,它们之间有分隔符(分隔符也是数组),类似于join 的工作方式,但不限于字符串?

该函数可以是标准 JS 或主要库的一部分,例如 lodash(这就是它在标签中引用的原因)。

这是一个使用示例:

let numbers = [[1], [2], [3]];
let result = _.joinArrays(numbers, [0]);
console.log(result); 
//printed: [1, 0, 2, 0, 3]

这类似于:

let strings = ["a", "b", "c"];
let result = strings.join(",");
console.log(result);
//printed: "a,b,c";

但是,join 不能使用,因为它将值转换为字符串,这是我不希望发生的。

但它适用于任何类型。

【问题讨论】:

  • 请添加更多示例。
  • 您想要的功能名称是 intersperse 带有另一个元素的数组。它目前是 Lodash 的 feature-request,所以如果你想看到它被添加到库中,就去投票吧!
  • @4castle 太酷了。也许我会提交一个拉取请求。谢谢!!
  • this 问题可能重复。
  • 是的,非常相似。不确定基于生成器的答案的全部范围。我的意思是废话......将其打入 babel.js 的输出复杂得令人难以置信。

标签: javascript arrays lodash


【解决方案1】:

Matrix Interspersion

这是完整的蒙蒂。发疯。

var numbers = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
],
  delimiters = [
  ',', '-', 'x'
];

// matrix interspersion, delimiters into numbers's children
// the rank/order/whatevs of the matrix can be arbitrary and variable
numbers.forEach((x, i) => {
  for (var j = 1, l = x.length; j <= l; j+=2 )
    x.splice(j, 0, delimiters[i]);
})

alert( "Matrix interspersed: " + JSON.stringify(numbers) );

// normal interspersion, a static delimiter into numbers
for (var j = 1, l = numbers.length; j <= l; j+=2 )
    numbers.splice(j, 0, ' AND ');

alert( "Outer array interspersed: " + JSON.stringify(numbers) );

// flattening a 2 rank array into a single array
var flattened = Array.prototype.concat.apply([], numbers);

alert( "Flattened: " + JSON.stringify(flattened) );

【讨论】:

  • 我不能使用 join,因为它会把东西变成字符串。
  • 哦,所以你希望结果是数组?您只需要将其展平并穿插分隔符吗?你能给我一个要加入的数组数组和要穿插的分隔符数组的工作示例吗?
  • 啊,我明白了。另一个使用 Lodash 的例子是正确的。
  • 我添加了一个带有矩阵散布的额外示例。
【解决方案2】:

您可以简单地使用array.reduce 来连接数组,然后推送您想要用作分隔符的任何内容。

let numbers = [[1], [2], [3]];

let n = numbers.reduce((a, b) => a.concat(0, b))

console.log(n)

【讨论】:

  • 更简单:return a.concat(0, b)
  • @4castle - 很不错,很明显。谢谢。 ; )
  • 遗憾的是,这并不能回答问题。这是我的第一个想法。尽管一次将 concat 应用于所有子数组可能会更好 (Array.prototype.concat.apply)。
【解决方案3】:
var result = [].concat.apply([], numbers);

console.log(result)

【讨论】:

    【解决方案4】:

    您可以使用Array#reduce 并返回一个包含项目的数组并在必要时进行粘合。

    const join = (array, glue) => array.reduce((a, b) => a.concat(glue, b));
    
    var numbers = [[1], [2], [3]];
    
    console.log(join(numbers, [0]));
    console.log(join(numbers, [42, 43]));
    console.log(join([[1]], [0]));
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • 可以从reduction中去掉三元语句和初值:array.reduce((r, a) =&gt; r.concat(glue, a))
    【解决方案5】:

    这是一个执行此操作的函数的实现,带有一些额外的逻辑,以防其他人想要这样做。我真的在问这是否已经存在。

    需要 lodash。

    export function intersperse(arrs, delimeter) {
        let joined = [];
        for (var i = 0; i < arrs.length; i++) {
            let arr = arrs[i];
            if (!arr) continue; //handle sparse arrays
            joined.push(...arr);
            if (i === arrs.length - 1) break;
            if (_.isFunction(delimeter)) {
                joined.push(...delimeter());
            } else if (_.isArray(delimeter)) {
                joined.push(...delimeter);
            } else {
                throw new Error("unknown type");
            }
        }
        return joined;
    }
    

    【讨论】:

      【解决方案6】:

      这里有很多很好的答案,包括 4Castle 的 cmets。至于改变,我想为这份工作开发一个通用的Array.prototype.intersperse()

      在函数式 JS 中,我可以为这项工作提出 3 个替代方案;

      Array.prototype.intersperse_1 = function(s){
        return this.reduce((p,c,i) => (p[2*i]=c,p), new Array(2*this.length-1).fill(s));
      };
      Array.prototype.intersperse_2 = function(s){
        return this.reduce((p,c,i) => (i ? p.push(s,c) : p.push(c),p),[]);
      };
      Array.prototype.intersperse_3 = function(s){
        return this.reduce((p,c,i) => i ? p.concat([s],[c]) : p.concat([c]),[]);
      };
      

      你应该远离第三个,因为.concat() 是函数式 JS 中最昂贵的操作之一。它甚至无法完成 100K 项的工作。

      另一方面,虽然在小尺寸阵列中总是稍微快一些,但在 FF 和 Chrome 中,第一个在非常大的阵列中比第二个快 2 倍甚至更快。即第一个在不到 1000 毫秒的时间内散布一个 10M 项目数组,而对于相同的工作,第二个需要 2000-2500 毫秒。当然,3rd 根本无法处理这种大小。

      因此在这种特殊情况下,与量身定制的解决方案相比,它可能会更昂贵,因为我们必须将结果映射到原始值,但我想下面的代码仍然值得注意。我敢肯定.concat() 采用量身定制的解决方案会在数组长度超过某个数字时落后。

      Array.prototype.intersperse = function(s){
        return this.reduce((p,c,i) => (p[2*i]=c,p), new Array(2*this.length-1).fill(s));
      }
      
      var arr = [[1],[2],[3]],
       result = arr.intersperse([0])
                   .map(e => e[0]);
      console.log(JSON.stringify(result));

      【讨论】:

      • 另一个想法:Array.from({length: 2*this.length-1}, (v,i) =&gt; i&amp;1? s: this[i&gt;&gt;&gt;1]);我想这是大小的反复增加,因此内存的重新分配使您的第二个版本变慢了。
      • @Thomas 有趣的方法然而,当使用 1M+ 项目数组进行测试时,它似乎落后于第二名(在 Chrome 中但不是在 FF 中),第一名似乎总是在 FF 和 Chrome 中做得最好。检查this 10M 项目性能测试。也许你可以修改它。
      【解决方案7】:

      正如这里多次提到的,最简单的方法是

      function intercalate(glue, arr){
          return arr.reduce((acc, v) => acc.concat(glue, v));
      }
      

      但这不是最好的方法,因为它每次迭代都会创建一个新的(中间)数组,然后将其丢弃。这对于这个简短的值数组无关紧要,但是如果您打算在更长的数组上使用它,您可能会注意到影响。

      更好的办法是创建一个数组并将值推入其中。

      function intercalate(glue, arr){
          const push = (acc, v) => (Array.isArray(v)? acc.push(...v): acc.push(v), acc);
          return arr.reduce((acc, v, i) => push(i===0? acc: push(acc, glue), v), []);
      }
      

      但是由于这个 Array 是逐渐增加的,它可能仍然需要分配更大的内存块并复制数据。这些任务非常高效,但仍然没有必要(imo);我们可以做得更好。

      我们首先创建一个包含所有数组和中间分隔符的列表,然后使用concat.apply([], list) 将其展平。所以我们生成了一个中间Array,它的大小我们可以提前计算出来,剩下的就是Array.concat的问题,是底层实现的问题。

      function intersperse(delimiter, arr){
          if(!arr.length) return [];
          let j = 0, push = (acc, v) => (acc[j++] = v, acc);
          return arr.reduce((acc, v) => push(j===0? acc: push(delimiter, glue), v), Array(2*arr.length-1));
      }
      //or
      function intersperse(delimiter, arr){
          if(!arr.length) return [];
          var out = Array(2*arr.length-1);
              out[0] = arr[0];
          for(var i=1, j=1; j<out.length;){
              out[j++] = delimiter;
              out[j++] = arr[i++];
          }
          return out;
      }
      
      //and
      
      function intercalate(glue, arr){
          var emptyArray = [];
          return arr.length? 
              emptyArray.concat.apply(emptyArray, intersperse(glue, arr)): 
              emptyArray;
      }
      

      最终,哪个版本将是最好/最快的,这并不容易判断,因为它可能取决于传递的值,并且 JIT 编译器是否优化了它的 s***。我提出了我的观点,由您选择您使用的版本/实现。

      对于第一个版本:您可能更愿意根本不将其放入库中,而是将其大部分内联(它足够简短),因此 JIT 编译器可能不会尝试在不同的调用(#monomorphic function/code)之间找到一些常见的类型,从而分别优化每个出现。另一方面,这可能是过早的优化。这取决于你。

      【讨论】:

        猜你喜欢
        • 2020-07-27
        • 2016-11-20
        • 1970-01-01
        • 2020-11-29
        • 2018-09-15
        • 1970-01-01
        • 2020-09-16
        • 1970-01-01
        • 2017-12-05
        相关资源
        最近更新 更多