【问题标题】:Ruby: Delete and return an array value based on block find conditionRuby:根据块查找条件删除并返回数组值
【发布时间】:2011-03-29 23:51:17
【问题描述】:

是否有一种内置方法可以根据返回 true 的块条件从数组中删除值,并返回被删除的值?

这是我正在尝试做的简化版本,但似乎必须有更好的方法:

array = [1,2,3,4,5,6,7,8,9,10]

index = array.index {|v| v == 5} # returns index if block is true

value = array.delete_at(index) # deletes and returns element at index

value 则为 5

【问题讨论】:

  • 你为什么要删除一个项目然后想要它的价值?
  • 这更像是我的好奇心。 Array 有一个 delete 方法,它删除一个值并返回它,还有一个 delete_if 方法,它接受一个块并删除值,但不返回删除的值。似乎很奇怪,没有一种方法可以两者兼得。
  • 这是一个有效的问题。考虑一个必须与某些数据进行比较但只进行一次的大型数组。从数组中删除项目会大大加快这个过程。
  • 我的用例:我正在慢慢地从列表中挑选项目。每次我都需要遍历列表,直到找到合适的候选人,然后我想将其从列表中删除。

标签: ruby-on-rails ruby arrays


【解决方案1】:

您无法就地更新数组并返回一组已删除的不同值。您可以使用delete_if 执行以下操作来删除值并捕获块中的逻辑删除的值:

reject = []
 => [] 
content = [1,2,3,4,5,6,7,8,9]
 => [1, 2, 3, 4, 5, 6, 7, 8, 9] 
content.delete_if {|v| reject << v if v > 5}
 => [1, 2, 3, 4, 5] 
reject
 => [6, 7, 8, 9] 
content
 => [1, 2, 3, 4, 5] 

【讨论】:

  • 这不会返回被删除的对象。您可以在数组之间进行区分,但这可能不是理想的/高效的。
  • @mike 更新更明确
  • 或者你可以使用 Enumerable#partition 几乎就是这样做的。
【解决方案2】:

您真的需要从原始数组中删除项目吗? 你真的只是想根据一些把它分成两部分 健康)状况?如果是后者,那么:

accepted = [ ]
rejected = [ ]
original.each { |e| (want_this_one(e) ? accepted : rejected).push(e) }

parts = original.inject({ :accepted => [ ], :rejected => [ ] }) do |accumulator, e|
  if(want_this_one(e))
    accumulator[:accepted].push(e)
  else
    accumulator[:rejected].push(e)
  end
  accumulator
end

然后是一个简单的方法包装器,可以很容易地提供一个块:

def categorize(array)
  categories = array.inject({ :accepted => [ ], :rejected => [ ] }) do |accumulator, e|
    if(yield e)
      accumulator[:accepted].push(e)
    else
      accumulator[:rejected].push(e)
    end
    accumulator
  end
  return categories[:accepted], categories[:rejected]
end

kept, deleted = categorize([1, 2, 3, 4, 5]) { |n| n % 2 == 0 }
# kept    = [2, 4]
# deleted = [1, 3, 5]

或者您可以使用Enumerable#partition 将数组拆分为两部分。

如果您确实需要就地修改数组,那么这个版本的 Wes 应该可以解决问题:

def slice_out(array)
  dead = [ ]
  array.delete_if do |e|
    if(yield e)
      dead.push(e)
      true
    else
      false  
    end
  end
  dead
end

a = [1,2,3,4]
x = slice_out(a) { |n| n % 2 == 0 }
# a == [1, 3]
# x == [2, 4]

【讨论】:

  • 您的categorize 方法与ruby-doc.org/core/classes/Enumerable.html#M001496 非常相似;)
  • @Mladen:谢谢,我不知道partition。我认为我的categorize 在功能上等同于partition,所以我会添加一个注释;我将把categorize 留在那里,因为我认为它可能会让某人开始对partition 进行简单的概括(即该块可以返回哈希键而不是布尔值)。
【解决方案3】:

Array#extract (Rails 6+)

如果您使用的是 Rails,那么从版本 6 开始,有一个方法 Array#extract!,几乎可以满足您的需要。

它删除并返回块返回真值的元素并修改原始数组。

请看下面的例子:

array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

values = array.extract! { |value| value == 5 }

# array
# => [1, 2, 3, 4, 6, 7, 8, 9, 10]

# values
# => [5]

【讨论】:

    【解决方案4】:

    您可以使用分区。显然这里的块示例并不完全有意义,但会返回已删除的项目和剩余的项目。

    a = [1,2,3,4,5,6,7,8,9]
    b, a = a.partition { |x| [1,3,5].include?(x) }
    
    b # [1, 3, 5]
    a # [2, 4, 6, 7, 8, 9]
    

    【讨论】:

      【解决方案5】:

      这不适用于每个用例,但如果您根据某种条件从数组中一次提取一个项目,您可以这样做:

      array = [1,2,3,4,5,6,7,8,9,10]
      indexed_array = array.index_by { |a| a }  # or whatever your condition is
      item = indexed_array.delete(5)
      array = indexed_array.values
      

      【讨论】:

      【解决方案6】:

      你可以使用values_at例如

      >> array = [1,2,3,4,5,6,7,8,9,10]
      => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
      >> array.values_at(5)
      => [6]
      

      【讨论】:

      • 谢谢,但这不会删除值或占用块,因此它不适用于我正在尝试做的事情。
      猜你喜欢
      • 2016-05-08
      • 2022-11-17
      • 2012-04-26
      • 1970-01-01
      • 1970-01-01
      • 2021-07-16
      • 1970-01-01
      • 2013-04-06
      • 1970-01-01
      相关资源
      最近更新 更多