【问题标题】:Equivalent of Ruby's each_cons in JavaScriptJavaScript 中 Ruby 的 each_cons 等价物
【发布时间】:2019-08-13 12:39:50
【问题描述】:

该问题已针对多种语言提出,但未针对 javascript。

Ruby 有 Enumerable#each_cons 方法,看起来像这样:

puts (0..5).each_cons(2).to_a
# [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]]
puts (0..5).each_cons(3).to_a
# [[1, 2, 3], [2, 3, 4], [3, 4, 5]]

我怎么能在 Array 的 javascript 中使用类似的方法?

【问题讨论】:

    标签: javascript arrays ruby enumerator equivalent


    【解决方案1】:

    这是一个可以做到的函数(ES6+):

    // functional approach
    const eachCons = (array, num) => {
        return Array.from({ length: array.length - num + 1 },
                          (_, i) => array.slice(i, i + num))
    }
    
    // prototype overriding approach
    Array.prototype.eachCons = function(num) {
      return Array.from({ length: this.length - num + 1 },
                        (_, i) => this.slice(i, i + num))
    }
    
    
    const array = [0,1,2,3,4,5]
    const log = data => console.log(JSON.stringify(data))
    
    log(eachCons(array, 2))
    log(eachCons(array, 3))
    
    log(array.eachCons(2))
    log(array.eachCons(3))

    你必须猜测结果数组的长度 (n = length - num + 1),然后你可以利用 JavaScript 的 array.slice 来获得你需要的块,迭代 n 次。

    【讨论】:

    • Array.from 的替代语法使用内部映射器参数Array.from({length:array.length - num + 1}, (_, i) => array.slice(i, i + num))
    • @UlysseBN,您可以完全排除您的map 并使用内部映射器本身,以防您没有注意到。与您现在混合在一起的解决方案相比,性能肯定会有所提高。
    • @assoron - 已编辑。我很高兴我发布了这个问答,它每分钟都在变得更好。
    【解决方案2】:

    您可以获取长度,构建一个新数组并使用Array.from 和内置映射器映射切片数组。

    Number.prototype[Symbol.iterator] = function* () {
        for (var i = 0; i < this; i++) yield i;
    };
    
    Array.prototype.eachCons = function (n) {
        return Array.from({ length: this.length - n + 1}, (_, i) => this.slice(i, i + n));
    }
    
    console.log([...10].eachCons(2));
    console.log([...10].eachCons(3));
    .as-console-wrapper { max-height: 100% !important; top: 0; }

    【讨论】:

    • Number 上的 [Symbol.iterator] 绝对是一个有趣的方法,我不知道它真的会起作用。
    • (顺便说明一下,在 Number 迭代器中应该是 &lt;= this,因为 (0..5) 实际上是 [0,1,2,3,4,5])。
    【解决方案3】:

    这是一个依赖函数生成器的单行解决方案。可能略有不同,但仍然值得一试。

    我也添加了原型,不知道你为什么要这样做,但仍然..

    这个解决方案不需要 ES6,它也可以和 ES5 一起使用,稍微注意一点不支持函数生成器的 IE。

    function* eachCons(arr, num) {
      for (let counter = 0; counter <= arr.length - num; counter++) yield arr.slice(counter, counter + num);
    }
    
    Array.prototype.eachCons = function* (num) {
      for (let counter = 0; counter <= this.length - num; counter++) yield this.slice(counter, counter + num);
    }
    
    const array = [0,1,2,3,4,5];
    const log = data => console.log(JSON.stringify(data))
    log([...eachCons(array, 2)]);
    log([...eachCons(array, 3)]);
    
    // Iterable way
    for (let [...pack] of eachCons(array, 2)) {
      console.log('pack is', pack);
    }
    // Prototype...
    for (let [...pack] of array.eachCons(2)) {
      console.log('pack is', pack);
    }

    【讨论】:

    • 我非常喜欢这个!但是,我宁愿使用根据情况返回数组的解决方案。例如,生成器解决方案不允许eachCons([1,2,3,3], 3).map((a) =&gt; a[0] + a[1])。但是,+1,因为该解决方案的多功能性和巧妙性。我不会使用原型解决方案,但我知道有些人会这样......
    • @UlysseBN 好吧,你可以使用:Array.from 但你是对的,这是生成器的“缺点”:P
    • @UlysseBN 也许不返回原型上的生成器是有意义的,而是返回一个数组,虽然这会带来混乱和不一致,但仍然可以解决问题(如果对数组使用会有意义)。
    • @UlysseBN 作为旁注,我现在才注意到 to_a 在 ruby​​ 中被调用,因此它也可能在 ruby​​ 中返回一个迭代器,但我不太确定。
    • @charlietfl 你说得对,确实,这有点过分了。让我编辑一下:)。
    猜你喜欢
    • 2011-08-18
    • 2011-02-12
    • 2012-04-07
    • 1970-01-01
    • 1970-01-01
    • 2011-07-11
    • 2016-06-21
    • 2010-09-20
    • 1970-01-01
    相关资源
    最近更新 更多