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 << i
并添加一个新的空嵌套数组 n << []
如果数据不是' 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 - 当值更改时将数组拆分为子数组并忽略/删除该值的主要内容,如果未能解决你的问题,请参考以下文章