以升序和降序循环的优雅方式

时间:2012-12-06 21:59:59

标签: ruby ruby-on-rails-3 nokogiri

我有以下代码解析HTML文本并修剪(或剥离)空的段落。它与String对象上的.strip类似。

doc = Nokogiri::HTML::DocumentFragment.parse(html)

# repetition that I want to collapse
doc.css('p').each do |p|
  if all_children_are_blank?(p)
    p.remove
  else
    break
  end
end

# repetition that I want to collapse
doc.css('p').reverse_each do |p|
  if all_children_are_blank?(p)
    p.remove
  else
    break
  end
end

doc.to_s.strip

是否有一种更优雅的方法可以防止我用注释标记的代码被复制并遵守代码重用的原则?

以下是我的想法,但我对它不满意,想看看是否有更好的东西:

doc = Nokogiri::HTML::DocumentFragment.parse(html)

doc.css('p').each do |p|
  if stop(p) then break end
end

doc.css('p').reverse_each do |p|
  if stop(p) then break end
end

doc.to_s.strip

def self.stop(p)
  if all_children_are_blank?(p)
    p.remove
    false
  else
    true
  end
end

3 个答案:

答案 0 :(得分:1)

如果我了解您要查找的内容,您希望以更简单的方式迭代您正在查看的元素,以便删除空白的p元素。

这是一种直截了当的方式来折叠你所写的内容,而不是做很多不同的事情:

doc.tap do |d|
  [:each, :reverse_each].each do |sym|
    d.css("p").public_send(sym) do |p|
      if blank_children?(p)
        p.remove
      else
        break
      end
    end
  end
end.to_s.strip

我还没有测试过,所以你可能需要调整一下。如果这是生产代码,我可能会将其分解为一个或多个方法调用,以便保持清晰。

答案 1 :(得分:1)

怎么样:

[*doc.css('p'), *doc.css('p').reverse].each do |p|
  if stop(p) then break end
end

在这种情况下,splat运算符(“*”)将两个列表扩展为一个数组,元素按升序排列,然后按降序排列。然后你只是遍历整个小组。


编辑: 由于break语句跳到一切结束,这将无法正常工作。因此,恕我直言,这样做的正确方法是将块分配给变量。你也可以消除stop函数,因为你无论如何都要消除代码的重复:

remover = lambda do |p|
  if all_children_are_blank? p
    p.remove
  else
    break
  end
end

doc.css('p').to_a.each(&remover).reverse_each(&remover)

希望这有帮助。

答案 2 :(得分:1)

可能是这样的:

puts "removing a top p" until stop(doc.at('p'))
puts "removing a bottom p" until stop(doc.search('p').last)

或只是:

puts "removing a p" until stop(doc.at('p')) && stop(doc.search('p').last)