ruby - 当值发生变化时将数组拆分为子数组并且 ignore/delete 该值
ruby - split an array into sub arrays when a value changes and ignore/delete that value
我想将以下数组拆分为子数组,以便子数组在 1 开始和结束时开始和结束...
a=[1,1,0,0,1,0,1,1,1]
所以我最终将其作为一个新数组...
=> [[1,1],[1],[1,1,1]]
有人有什么想法吗...?
您可以通过多种方式完成此操作。一种方法是将数组转换为字符串,拆分组并将其重新映射为数组(忽略任何空组):
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]]
我喜欢许多不同的答案!所以我花了一些时间来测试其中的一些。
以下是我的处理方式:
new_array = a.each_with_object([ [] ]) {|i, n| i == 1 ? ( n.last << i) : (n.last.empty? ? true : (n << []))}
#each_with_object
方法允许我迭代数组,同时使用一个对象来存储我沿途收集的任何数据(该对象被分配给变量 n
,它代表 'new_array').
在这种方法中,我将数据收集到嵌套数组 [ [] ]
的数组中,在识别到最后一个嵌套数组 n.last << i
后添加 1,并添加一个新的空嵌套数组 n << []
如果数据不是我想要收集的(并且现有的嵌套数组不为空)。
我使用两个内联 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
这是使用 Enumerable#chunk 的方法:
a.chunk { |n| n==1 }.select(&:first).map(&:last)
#=> [[1, 1], [1], [1, 1, 1]]
还有一个,使用Enumerable#slice_when,在v2.2中引入:
a.slice_when { |bef,aft| bef!=aft }.reject { |e| e.first != 1 }
#=> [[1, 1], [1], [1, 1, 1]]
你可以猴子把它修补成可枚举的,然后把它传递给一个块,这样它就可以用于你想要的任何数字或表达式
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?
并从末尾删除了不必要的结果行
最简单和最易读的方式可能是:
a.chunk {|x| x==1 || nil}.map(&:last)
#=> [[1, 1], [1], [1, 1, 1]]
如果您可以在 Rails 上使用 Ruby,您可以使用更简单的解决方案:
a.split(0).reject(&:empty?)
#=> [[1, 1], [1], [1, 1, 1]]
a.join.split('0').select {|b| b if not b.empty?}.map {|c| c.split(//).map{|d| d.to_i}}
我想将以下数组拆分为子数组,以便子数组在 1 开始和结束时开始和结束...
a=[1,1,0,0,1,0,1,1,1]
所以我最终将其作为一个新数组...
=> [[1,1],[1],[1,1,1]]
有人有什么想法吗...?
您可以通过多种方式完成此操作。一种方法是将数组转换为字符串,拆分组并将其重新映射为数组(忽略任何空组):
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]]
我喜欢许多不同的答案!所以我花了一些时间来测试其中的一些。
以下是我的处理方式:
new_array = a.each_with_object([ [] ]) {|i, n| i == 1 ? ( n.last << i) : (n.last.empty? ? true : (n << []))}
#each_with_object
方法允许我迭代数组,同时使用一个对象来存储我沿途收集的任何数据(该对象被分配给变量 n
,它代表 'new_array').
在这种方法中,我将数据收集到嵌套数组 [ [] ]
的数组中,在识别到最后一个嵌套数组 n.last << i
后添加 1,并添加一个新的空嵌套数组 n << []
如果数据不是我想要收集的(并且现有的嵌套数组不为空)。
我使用两个内联 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
这是使用 Enumerable#chunk 的方法:
a.chunk { |n| n==1 }.select(&:first).map(&:last)
#=> [[1, 1], [1], [1, 1, 1]]
还有一个,使用Enumerable#slice_when,在v2.2中引入:
a.slice_when { |bef,aft| bef!=aft }.reject { |e| e.first != 1 }
#=> [[1, 1], [1], [1, 1, 1]]
你可以猴子把它修补成可枚举的,然后把它传递给一个块,这样它就可以用于你想要的任何数字或表达式
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?
并从末尾删除了不必要的结果行
最简单和最易读的方式可能是:
a.chunk {|x| x==1 || nil}.map(&:last)
#=> [[1, 1], [1], [1, 1, 1]]
如果您可以在 Rails 上使用 Ruby,您可以使用更简单的解决方案:
a.split(0).reject(&:empty?)
#=> [[1, 1], [1], [1, 1, 1]]
a.join.split('0').select {|b| b if not b.empty?}.map {|c| c.split(//).map{|d| d.to_i}}