如何根据ruby中的条件加入一些(不是全部)数组元素?

时间:2014-07-12 11:09:30

标签: ruby arrays string

假设我有一个像这样的字符串数组:

array = ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]

我需要加入以"开头的项目("与之前的项目一起获得输出,如下所示:

["foo (bar)", "baaz", "quux", "herp (derp)"]

我想它必须类似于获取与/^\(/匹配的数组项的索引,然后在块中迭代原始数组,加入index-1..index处的项目,并在index处删除

4 个答案:

答案 0 :(得分:5)

array.slice_before{|s| !s.start_with?("(")}.map{|a| a.join(" ")}
# => ["foo (bar)", "baaz", "quux", "herp (derp)"]

答案 1 :(得分:4)

从另一个方向看 - 连接整个字符串,然后沿着后面没有(的空格拆分:

array.join(' ').split(/ (?!\()/)
# => ["foo (bar)", "baaz", "quux", "herp (derp)"]

答案 2 :(得分:1)

我无法改进@ sawa的答案,但我可以提供一种替代方法,一些读者可能会在另一个地方找到其他有用的方法:

array = ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]

arr = []
enum = array.each
loop do
  arr << enum.next
  next_up = enum.peek
  if next_up[0] == ?(
    arr[-1] += (" " + next_up)
    enum.next
  end
end

arr #=> ["foo (bar)", "baaz", "quux", "herp (derp)"]

这就是发生的事情。

arr = []
enum = array.each
  #=> #<Enumerator: ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]:each>

现在让我们逐步完成循环,直到引发StopIteration异常:

s = enum.next                #=> "foo"
arr << s                     #=> ["foo"]
next_up = enum.peek          #=> "(bar)"
next_up[0] == ?(             #=> true
  arr[-1] += (" " + next_up) #=> "foo (bar)"
    arr                      #=> ["foo (bar)"]
  enum.next                  #=> "(bar)" (discard)

s = enum.next                #=> "baaz"
arr << s                     #=> ["foo (bar)", "baaz"]
next_up = enum.peek          #=> "quux"
next_up[0] == ?(             #=> false

s = enum.next                #=> "quux"
arr << s                     #=> ["foo (bar)", "baaz", "quux"]
next_up = enum.peek          #=> "herp"
next_up[0] == ?(             #=> false

s = enum.next                #=> "herp"
arr << s                     #=> ["foo (bar)", "baaz", "quux", "herp"]
next_up = enum.peek          #=> "(derp)"
next_up[0] == ?(             #=> true
  arr[-1] += (" " + next_up) #=> "herp (derp)"
    arr                      #=> ["foo (bar)", "baaz", "quux", "herp (derp)"]
  enum.next                  #=> "(derp)" (discard)

s = enum.next                #=> StopIteration: iteration reached an end

StopIteration异常由Kernel#loop通过打破循环来处理。

arr                          #=> ["foo (bar)", "baaz", "quux", "herp (derp)"]

答案 3 :(得分:1)

这是另一种方式,使用Enumerable#chunk。我假设数组的第一个元素的第一个字符不是(,但是如果该假设不正确,该方法当然可以被修改。

<强>代码

def doit(array)  
  array.chunk { |s| s[0] == ?( }
       .map(&:last)
       .each_slice(2)
       .map { |arr| (arr.size == 2) ? [arr.first[0..-2],
                      [arr.first.last, *arr.last].join(' ')] : arr }
       .flatten
end

<强>实施例

array = ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]
doit(array) #=> ["foo (bar)", "baaz", "quux", "herp (derp)"]

array = ["foo", "(bar)", "(anther bar)", "quux"]    
doit(array) #=> ["foo (bar) (anther bar)", "quux"]

<强>解释

array = ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]

enum1 = array.chunk { |s| s[0] == ?( }
  #=> #<Enumerator: #<Enumerator::Generator:0x00000101142ce0>:each>

enum1.to_a # elements to be enumerated (for information only)
  #=> [[false, ["foo"]], [true, ["(bar)"]],
  #    [false, ["baaz", "quux", "herp"]], [true, ["(derp)"]]]
a = enum1.map(&:last)
  #=> [["foo"], ["(bar)"], ["baaz", "quux", "herp"], ["(derp)"]]
enum2 = a.each_slice(2)
  #=> #<Enumerator: [["foo"], ["(bar)"], ["baaz", "quux", "herp"],
  #                  ["(derp)"]]:each_slice(2)>
enum2.to_a # elements to be enumerated (for information only)
  #=> [[["foo"], ["(bar)"]], [["baaz", "quux", "herp"], ["(derp)"]]]
c = enum2.map { |arr| (arr.size==2) ? [arr.first[0..-2],
                        [arr.first.last, *arr.last].join(' ')] : arr }
  #=> [[[], "foo (bar)"], [["baaz", "quux"], "herp (derp)"]]
c.flatten
  #=> ["foo (bar)", "baaz", "quux", "herp (derp)"]