ruby - 当值更改时将数组拆分为子数组并忽略/删除该值

Posted

技术标签:

【中文标题】ruby - 当值更改时将数组拆分为子数组并忽略/删除该值【英文标题】:ruby - split an array into sub arrays when a value changes and ignore/delete that value 【发布时间】:2015-07-19 18:54:09 【问题描述】:

我想将以下数组拆分为子数组,以便子数组在 1 的开始和结束时开始和结束...

a=[1,1,0,0,1,0,1,1,1]

所以我最终将它作为一个新数组...

=>  [[1,1],[1],[1,1,1]]

有人有什么想法吗...?

【问题讨论】:

【参考方案1】:

最简单和最易读的方式可能是:

a.chunk |x| x==1 || nil.map(&:last)
  #=> [[1, 1], [1], [1, 1, 1]]

如果您可以使用 Ruby on Rails,您可以使用更简单的解决方案:

a.split(0).reject(&:empty?)
  #=> [[1, 1], [1], [1, 1, 1]]

【讨论】:

聪明!我学到了一些新东西。我从docs 看到您也可以将nil 替换为:_separator。注意你可以写map(&:last)【参考方案2】:

有很多方法可以实现这一点。一种方法是将数组转换为字符串,拆分组并将其重新映射为数组(忽略任何空组):

a=[1,1,0,0,1,0,1,1,1]
a.join.split(/0/).map |group| group.split(//).map(&:to_i) unless group == ''.compact
#=> [[1,1],[1],[1,1,1]]

【讨论】:

我喜欢这种方法,但我使用您的代码得到的是字符串而不是数字。为了获得数字,我使用了:` a.join.split(/0/).map |group| group.split(//) 除非 group == ''.compact .each |ia| ia.map! |我| i.to_i ` 虽然方法很漂亮,但它可能会耗费大量资源(请参阅我在回答中运行的基准测试)...我认为这是由于分配字符串和使用中间对象。 【参考方案3】:

我喜欢许多不同的答案!所以我花时间测试了其中一些。

下面是我的做法:

new_array = a.each_with_object([ [] ]) |i, n| i == 1 ? ( n.last << i) : (n.last.empty? ? true : (n << []))

#each_with_object 方法允许我迭代数组,同时使用一个对象来存储我沿途收集的任何数据(该对象分配了变量 n,它代表 'n ew_array')。

在这种方法中,我将数据收集到嵌套数组 [ [] ] 的数组中,当它们被识别时将 1 添加到最后一个嵌套数组 n.last &lt;&lt; i 并添加一个新的空嵌套数组 n &lt;&lt; [] 如果数据不是' t 我要收集的内容(并且现有的嵌套数组不为空)。

我使用两个内联 if:else 语句,使用简写:

condition ? do_if_true : do_if_false

对一些答案进行基准测试

在我的 MacBook Pro 上测试了一些答案,看来我的方法是迄今为止最快的……但也许我有偏见。

关于报告的注意事项:结果以秒为单位。少即是快。

粗体显示两个最佳结果。

报告包含 10 项、100,000 次迭代的数组:

                    user     system      total        real

@tykowale 的方法 0.210000 0.000000 0.210000 (0.209799)

@infused 的方法 1.300000 0.010000 1.310000 (1.304084)

@CarySwoveland 的方法 0.830000 0.000000 0.830000 (0.839012)

@Myst 的方法 0.170000 0.000000 0.170000 (0.169915)

@Sid 的方法 0.590000 0.000000 0.590000 (0.595671)

报告包含 100 个项目、10,000 次迭代的数组:

                    user     system      total        real

@tykowale 的方法 0.160000 0.000000 0.160000 (0.155997)

@infused 的方法 1.030000 0.000000 1.030000 (1.030392)

@CarySwoveland 的方法 0.420000 0.010000 0.430000 (0.424801)

@Myst 的方法 0.150000 0.000000 0.150000 (0.143403)

@Sid 的方法 0.260000 0.000000 0.260000 (0.255548)

报告包含 1,000 个项目、1,000 次迭代的数组:

                    user     system      total        real

@tykowale 的方法 0.150000 0.000000 0.150000 (0.160459)

@infused 的方法 1.030000 0.000000 1.030000 (1.033616)

@CarySwoveland 的方法 0.310000 0.000000 0.310000 (0.312325)

@Myst 的方法 0.130000 0.000000 0.130000 (0.133339)

@Sid 的方法 0.210000 0.000000 0.210000 (0.217960)

报告包含 10,000 个项目、100 次迭代的数组:

                    user     system      total        real

@tykowale 的方法 0.250000 0.000000 0.250000 (0.252399)

@infused 的方法 1.020000 0.000000 1.020000 (1.017766)

@CarySwoveland 的方法 0.320000 0.000000 0.320000 (0.321452)

@Myst 的方法 0.130000 0.000000 0.130000 (0.128247)

@Sid 的方法 0.210000 0.000000 0.210000 (0.212489)

基准代码

以下是用于基准测试的脚本:

module Enumerable
  def split_by
    result = [a=[]]
    each |o| yield(o) ? (result << a=[]) : (a << o) 
    result.pop if a.empty?
    result.delete_if  |x| x.empty? 
    result
  end
end

require 'benchmark'

[10, 100, 1000, 10000].each do |items|

  a = (Array.new(items)  rand 2 )
  cycles = 1_000_000 / items

  puts "report for array with #items items, #cycles iterations:"

  Benchmark.bm do |bm|
    bm.report("@tykowale's approach") cycles.times  a.split_by |x| x == 0  
    bm.report("@infused's approach") cycles.times  a.join.split(/0/).map |group| group.split(//).map(&:to_i) unless group == ''.compact  
    bm.report("@CarySwoveland's approach")  cycles.times  a.chunk(&:itself).select  |a| a.first==1 .map(&:last)  
    bm.report("@Myst's approach")  cycles.times  a.each_with_object([[]]) |i, n| i == 1 ? ( n.last << i) : (n.last.empty? ? true : (n << []))   
    bm.report("@Sid's approach")  cycles.times  a.chunk |x| x==1 || nil.map|y,ys| ys  
  end
end

【讨论】:

也许可以尝试使用 a = Array.new(n) rand 2 进行基准测试以获得较大的 n 您的意思是“越少越好”。 :-) 请使用越来越多的项目运行基准测试。如果我的答案最快,请停在那里。 所以,有人否决了我的回答……有人(尤其是那个人,但不一定)愿意告诉我下次如何做得更好吗? 极少数反对者坦白。最好继续前进。 (我赞成。) “Movin' on”更习惯用语。【参考方案4】:

这是一种使用Enumerable#chunk的方法:

a.chunk  |n| n==1 .select(&:first).map(&:last)
  #=> [[1, 1], [1], [1, 1, 1]] 

另一个,使用 v2.2 中引入的Enumerable#slice_when:

a.slice_when  |bef,aft| bef!=aft .reject  |e| e.first != 1 
  #=> [[1, 1], [1], [1, 1, 1]]

【讨论】:

【参考方案5】:

您可以将其修补为可枚举并将其传递给一个块,以便更多地用于您想要的任何数字或表达式

module Enumerable
  def split_by
    result = [a=[]]
    each |o| yield(o) ? (result << a=[]) : (a << o) 
    result.delete_if  |a| a.empty? 
  end
end

a=[1,1,0,0,1,0,1,1,1]

p a.split_by |x| x == 0
#=> [[1,1],[1],[1,1,1]]

从Split array into sub-arrays based on value找到(大部分)这个

编辑:更改了删除空集的工作方式result.pop if a.empty? 并从末尾删除了不必要的结果行

【讨论】:

有趣的方法! 好点,我想到它会返回所有正在删除的内容。类似于 pop 的功能。【参考方案6】:
a.join.split('0').select |b| b if not b.empty?.map |c| c.split(//).map|d| d.to_i

【讨论】:

以上是关于ruby - 当值更改时将数组拆分为子数组并忽略/删除该值的主要内容,如果未能解决你的问题,请参考以下文章

根据 NSDictionary 键值将 NSArray 拆分为子数组

将数组拆分为子数组5次,同时在所有子数组中保持唯一对]]

比较数组中的值并删除 Ruby/Rails 中不同的项目

Jquery在解析时将巨大的数组拆分为许多新的回调

Ruby将数组拆分为X组

如何拆分字符串 C# 并忽略字符串中的不完整单词