【问题标题】:Return iterating object and index while iterating迭代时返回迭代对象和索引
【发布时间】:2011-07-23 15:03:08
【问题描述】:
array = [1,2,3,{:name => "Peter"}, "hello"]
array.each do |element| # it can be "inject", "map" or other iterators
  # How to return object "array" and position of "element"
  # also next and priviouse "element"
end

当然我可以通过array.index[element] 返回索引,但我正在寻找更自然的解决方案。就像 Rails 关联中的 proxy_owner 一样

Ruby 1.8.7

我想输出什么?我想返回我迭代的对象(在我的例子中是数组),还有迭代次数(在 each_with_index 的情况下的索引)next 和迭代的 priviouse 元素。

作为输入,我有一个数组和迭代器(每个,映射,注入等)

【问题讨论】:

  • each_with_index 不起作用吗?如果是这样,你可以这样做array[i-1]array[i+1]
  • inject_with_indexselect_with_index 怎么样? :) 我认为有共同的方法
  • 想要的输出是什么?显示实际的输入/输出比试图用文字解释更好。

标签: ruby


【解决方案1】:

使用Enumerable#each_cons。以下是来自ruby1.8.7 rdoc 的副本。它应该适用于 ruby​​ 1.8.7。


  • each_cons(n) {...}
  • each_cons(n)

为每个连续元素数组迭代给定块。如果没有给出块,则返回一个枚举器。


有了这个,你可以给一个数组:

['a', 'b', 'c', 'd', 'e'].each_cons(3).to_a

# => ["a", "b", "c"], ["b", "c", "d"], ["c", "d", "e"]]

或者做:

['a', 'b', 'c', 'd', 'e'].each_cons(3) {|previous, current, nekst|
    puts "#{previous} - #{current} - #{nekst}"
}

# => a - b - c
# => b - c - d
# => c - d - e

如果你想要指数,

['a', 'b', 'c', 'd', 'e'].each_cons(3).to_a.each_with_index {|(previous, current, nekst), i|
    puts "#{i + 1}. #{previous} - #{current} - #{nekst}"
}

# => 1. a - b - c
# => 2. b - c - d
# => 3. c - d - e

您可以很一般地将数组传递给其他枚举器,例如使用inject

['a', 'b', 'c', 'd', 'e'].each_cons(3).to_a.inject(''){|str, (previous, current, nekst)|
    str << previous+current+nekst
}

# => "abcbcdcde"

【讨论】:

  • 非常聪明和熟练:) 我会用它。但它实际上并不是我想要的
  • 你能修复链接,并澄清它是先给'a', 'b', 'c'然后给'b', 'c', 'd',还是给'a', 'b', 'c'然后给'd', 'e', 'f'
  • sawa,我终于使用了这种方法。唯一我不喜欢这里的是,我无法将第一个和最后一个 con 设置为 [nil, a, b] 和 [d, e, nil] 或任何将第一个元素确定为中间元素的东西。因为现在我无法正确迭代,因为我得到了Array.count - 2 cons
  • @fl00r 很高兴您采用我的解决方案。您遇到的问题是一件小事:迭代 [nil, *original_array, nil] 而不是 original_array
【解决方案2】:

编辑:这是针对 1.9 的,我现在看到您的问题明确提到了 1.8.7。我会把它留在这里以供参考,但如果它打扰到任何人,我可以删除它。

sawa 已经指出 Enumerator#with_index:

http://www.ruby-doc.org/core/classes/Enumerator.html#M000303

例子:

>> [1,2,3].map.with_index { |x, i| [x,i] }
=> [[1, 0], [2, 1], [3, 2]]
>> [1,2,3].each_cons(2).with_index { |(x, y), i| p [x,y,i] }
[1, 2, 0]
[2, 3, 1]
=> nil

与这个问题也相关的是Enumerator#nextEnumerator#peek,它们将返回枚举器中的下一个对象,分别不向前移动内部位置。

http://www.ruby-doc.org/core/classes/Enumerator.html#M000307

http://www.ruby-doc.org/core/classes/Enumerator.html#M000308

【讨论】:

  • Rails 1.8.7?我相信 1.9
  • 是的,这是 Ruby 1.9。顺便说一句,Rails 1.8.7 已经很老了。 ;-)
  • 对于 1.8.7,您可以将 .map.with_index 替换为 .each_with_index.map
  • 当然可以,但我宁愿最终看到每个人都升级到 1.9。它确实已经发布了一段时间,坦率地说,代码仍然无法与最新的 Ruby 版本一起工作的借口已经不多了。
  • 我认为是peek,而不是poke
【解决方案3】:

Ruby 的each_with_index 功能可以轻松重新创建:

ary = %w[zero one two three]
ary.zip((0 .. (ary.size - 1)).to_a).to_a # => [["zero", 0], ["one", 1], ["two", 2], ["three", 3]]

ary.zip((0 .. (ary.size - 1)).to_a).each do |a, i|
  puts "this element: #{a}"
  puts "previous element: #{ary[i - 1]}" if (i > 0)
  puts "next element: #{ary[i + 1]}" if (i < (ary.size - 1))
  puts
end
# >> this element: zero
# >> next element: one
# >> 
# >> this element: one
# >> previous element: zero
# >> next element: two
# >> 
# >> this element: two
# >> previous element: one
# >> next element: three
# >> 
# >> this element: three
# >> previous element: two
# >> 

一旦您知道当前对象的索引,您就可以查看您正在迭代的数组并轻松获取上一个和下一个值。

所以,你可以这样做:

module Enumerable
  def my_each_with_index
    self.zip((0 .. (self.size - 1)).to_a).each do |a, i|
      yield a, i
    end
  end
end

ary.my_each_with_index { |a,i| puts "index: #{i} element: #{a}" }
# >> index: 0 element: zero
# >> index: 1 element: one
# >> index: 2 element: two
# >> index: 3 element: three

【讨论】:

  • 这是我在 StackOverflow 问题的低排名答案中发现的最有用的红宝石智慧 sn-ps 之一。
【解决方案4】:

无法测试,但如果没记错的话:

a = [4, 3, 3, 1, 6, 6,1]
p a.enum_for(:each_with_index).inject([]){ |m,args| m<<args }
#=> [[4, 0], [3, 1], [3, 2], [1, 3], [6, 4], [6, 5], [1, 6]]

将注入替换为选择、拒绝或其他。使用 Ruby 1.9:

p a.each_with_index.inject([]){ |m,args| m<<args }

【讨论】:

  • 请注意 xs.inject([]) { |m, args| m
【解决方案5】:

在ruby1.8中,有each_with_index

如果您希望在 injectmap、...等其他迭代器上使用它,并且如果您使用的是 ruby​​1.9,则可以将 Enumerator#with_index 方法附加到各种迭代器。

Enumerator#with_index

【讨论】:

  • 所以没有办法知道哪个对象在迭代,什么是 priviouse 和下一个值?
  • 您使用的是 ruby​​ 1.9 吗?如果使用with_index,则可以同时获取对象和索引。使用索引i,您可以通过array[i-1] 获得前一个。您想要更简单的方法来访问上一个/下一个元素吗?
  • 我明白了。这可能需要更多的想法。我现在没有答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-29
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-03
相关资源
最近更新 更多