从数组开头删除与值匹配的元素,直到数组达到指定大小

时间:2018-11-20 01:42:26

标签: arrays ruby

我正在尝试通过删除与唯一ID匹配的值来修改数组,但是一旦该数组的前5个值有效(即它们的ID与不想要的ID不匹配),然后停止。如果数组的计数<= 5,则满足以下条件:

all_items = [{"id" => "id01"},{"id" => "id02"},{"id" => "id03"},{"id" => "id04"}]
exclude_ids = ["id01","id02"]
all_items.delete_if { |item| exclude_ids.include?(item["id"])}

,期望的输出为[{"id" => "id03"},{"id" => "id04"}]

但是在数组的总数大于等于5的情况下,我只想删除 尽可能多的第一项,直到前5个元素(all_items[0..4] )在设定的条件内均有效,然后停止迭代。如果我要这样做:

all_items = [{"id" => "id01"},{"id" => "id02"},{"id" => "id03"},{"id" => "id04"},{"id" => "id05"},{"id" => "id06"},{"id" => "id07"},{"id" => "id08"},{"id" => "id09"},{"id" => "id10"}]
exclude_ids = ["id01","id02","id07"]
all_items.delete_if { |item| exclude_ids.include?(item["id"])}
return all_items[0..4]

我确实获得了所需的输出[{"id" => "id03"},{"id" => "id04"},{"id" => "id05"},{"id" => "id06"},{"id" => "id08"}]。但是我希望delete_if一旦到达all_items[0..4]已经包含5个有效元素的位置就完全停止。在这种情况下,我希望它在{"id" => "id08"}之后中断,甚至不检查其余值。 (但是,如果delete_if在运行时,总计数降至5以下,则它应继续遍历所有剩余元素,因为它属于我上面发布的第一段代码的类别,其中计数<= 5。)

我知道有一个delete_if.with_index,但是我不确定在这种情况下是否合适,尤其是因为删除项目后索引会发生变化。

总结一下我的问题:有没有一种方法可以从数组中删除所需的首项,直到array[0..x]被所需的值填充,然后一旦x的计数停止,就停止检查/删除所有剩余项到达了?

3 个答案:

答案 0 :(得分:1)

如果从原始对象中删除,则会得到所有尚未删除的项目。因此,我认为最好只将所需的项目移动到新对象中:

all_items.each_with_object([]) { |item, ary| ary << item if !exclude_ids.include?(item["id"]) and ary.size < 5 }

#=> [{"id"=>"id03"}, {"id"=>"id04"}, {"id"=>"id05"}, {"id"=>"id06"}, {"id"=>"id08"}]

答案 1 :(得分:1)

termination_threshold = 5
all_items = (1..20).to_a
  #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
exclusions = [1,2,7]

all_items.reject! do |n|
  break if termination_threshold.zero?
  exclude = exclusions.include? n
  termination_threshold -= 1 if exclude
  exclude
end
  #=> [3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

all_items
  #=> [3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

如果只需要包含termination_threshold的前all_items个有效元素的数组,则可以使用以下内容:

all_items.each_with_object([]) do |e,arr|
  arr << e unless exclusions.include?(e)
  break arr if arr.size == termination_threshold
end
  #=> [3, 4, 5, 6, 8]

答案 2 :(得分:1)

您可以使用Enumerator::Lazy来实现所需的目标:

all_items = [{"id" => "id01"},{"id" => "id02"},{"id" => "id03"},{"id" => "id04"},{"id" => "id05"},{"id" => "id06"},{"id" => "id07"},{"id" => "id08"},{"id" => "id09"},{"id" => "id10"}]
exclude_ids = ["id01","id02","id07"]
all_items.lazy.reject {|item| exclude_ids.include?(item['id']) }.first(5)