我有一个数组,我想对该数组的子集(任何n个项)执行delete_if
(并在内存中修改数组)
使用完整数组我可以做
array.delete_if do |item|
should_be_deleted?(item)
end
如果我想限制前n个项目,则以下工作
array.take(n).delete_if do |item|
should_be_deleted?(item)
end
因为它将创建一个新数组并在该新数组上执行delete_if
是否有替代方法,例如只删除前n个项目的take_and_delete_if
(如果每个块的块返回true)?
我希望从数组a
和b
,c
处理3个块(并在执行操作后从数组中删除)
by_batch_of(3, until: (proc { a.empty? })) do
# This sets an instance variable @by = 3, and will iterate as long as `a` has any item
process_from_a # Will move @by items in a to either array b or c or fail
process_from_b # Will move @by items in b to c or fail
process_from_c # Should move items or fail and put back in a
end
样品处理方法
process_from_a(by: @by)
a.take_and_delete_if(by: by) do |item| # The +take_and_delete_if+ methods is the one I need
b << item if reason1
c << item if reason2
reason1 or reason2 # Delete if the item was moved away
end
性能是我正在寻找的
示例
a = [1,2,3,4,5,6,7,8,9]
b = []
c = []
第1批3
process_from_a(by:3)
a = [3,4,5,6,7,8,9] # 3 failed so delete_if returned false, it remains in the array (order doesn't matter)
b = [1] # 1 moved to b
c = [2] # 2 moved to c
process_from_b
a = [3,4,5,6,7,8,9]
b = []
c = [1,2] # 2 moved to c
process_from_c
a = [3,4,5,6,7,8,9,1] # 1 was rejected in a
b = []
c = [] # 1,2 processed from c
下一次迭代将例如来自a等的处理[3,4,5]
假设我的数组非常大(10k,100k),并且我想要批量处理10个流程项。我不想要昂贵的解决方案来过滤前10个项目并使用index < 10
删除整个数组...
答案 0 :(得分:1)
逻辑可能如下所示:
array.delete_if do |item|
next if should_be_skipped?(item)
should_be_deleted?(item)
end
示例:
a = [1,2,3,4,5,6]
a.delete_if do |item|
next if item == 2 # would skip 2 because we want so
item % 2 == 0 # would remove all even numbers (except for 2)
end
#=> [1, 2, 3, 5]
只是澄清一下:答案是相当笼统的,只是为了向OP展示如何处理此类案件的想法。
对于特定情况,要跳过4个您要使用的第一个元素:
a = [1,2,3,4,5,6,7,8]
a.delete_if.with_index do |item, index|
index > 3 && item.even?
end
#=> [1, 2, 3, 4, 5, 7]
答案 1 :(得分:1)
[1,2,3,4,5,6,7,8].delete_if.with_index{|e,i| i<3} # => [4, 5, 6, 7, 8]
索引范围为0..2的项目已删除
答案 2 :(得分:1)
您可以使用方法#shift
删除第一个n
元素,例如:
> a = [1, 2, 3, 4, 5]
=> [1, 2, 3, 4, 5]
> a.shift(3)
=> [1, 2, 3]
> a
=> [4, 5]
答案 3 :(得分:1)
应该可以对子集中的过滤元素执行in place replacement:
a = (0..10000).to_a;
a[0, 100] = a[0, 100].delete_if(&:odd?)
基准:
require 'benchmark/ips'
Benchmark.ips do |x|
x.report("with_index") { (0..10000).to_a.delete_if.with_index { |k, i| k.odd? && i < 100 } }
x.report("slice") { a = (0..10000).to_a; a[0, 100] = a[0, 100].delete_if(&:odd?) }
x.compare!
end
在MRI Ruby 2.4.0p0上提供这些结果:
Warming up --------------------------------------
with_index 58.000 i/100ms
slice 273.000 i/100ms
Calculating -------------------------------------
with_index 602.354 (± 6.6%) i/s - 3.016k in 5.033200s
slice 2.775k (±10.0%) i/s - 13.923k in 5.075605s
Comparison:
slice: 2774.9 i/s
with_index: 602.4 i/s - 4.61x slower
答案 4 :(得分:0)
def skip_then_test(arr, nbr_to_skip)
arr.delete_if { |item| (nbr_to_skip -= 1) < 0 && item.even? }
end
skip_then_test [2,3,4,5,6,7,8], 0
#=> [3, 5, 7]
skip_then_test [2,3,4,5,6,7,8], 1
#=> [2, 3, 5, 7]
skip_then_test [2,3,4,5,6,7,8], 2
#=> [2, 3, 5, 7]
skip_then_test [2,3,4,5,6,7,8], 3
#=> [2, 3, 4, 5, 7]
arr = [2,3,4,5,6,7,8]
skip_then_test arr, 4
#=> [2, 3, 4, 5, 7]
arr
#=> [2, 3, 4, 5, 7]
另一种方式如下。
def skip_then_test(arr, nbr_to_skip)
arr.replace(arr[0, nbr_to_skip] + arr[nbr_to_skip..-1].delete_if(&:even?))
end
arr = [2,3,4,5,6,7,8]
skip_then_test arr, 3
#=> [2, 3, 4, 5, 7]
arr
#=> [2, 3, 4, 5, 7]