如何使用Nokogiri创建一个独特的阵列?

时间:2013-06-06 19:42:31

标签: ruby xml xpath nokogiri

我的代码如下:

file = Nokogiri::XML(File.open('file.xml'))
test = file.xpath("//title") #all <title> elements in xml file

然后当我尝试:

puts test.uniq

我收到以下错误:

 undefined method `uniq' for #<Nokogiri::XML::NodeSet:0x000000011b8bf8> 

test不是数组吗?如果不是,我该怎么做呢?

否则,我如何才能从test数组中获取唯一值?

2 个答案:

答案 0 :(得分:7)

  

测试不是阵列吗?如果不是,我该怎么做呢?

test将是NodeSet

Nokogiri::XML('<xml><foo/></xml>').xpath('//foo').class
=> Nokogiri::XML::NodeSet

foo = Nokogiri::XML('<xml><foo/></xml>').xpath('//foo')
=> [#<Nokogiri::XML::Element:0x8109a674 name="foo">]

foo.is_a? Array
=> false

foo.is_a? Enumerable
=> true

要将其变为数组,请使用to_a

foo.respond_to? :to_a
=> true

但是,这不是必需的,因为它还会响应mapeach以及迭代数组时我们期望的所有正常事物,因为它包含Enumerable。根据定义,map会自动返回一个数组,因此您在评论和问题中就会想到转换。

foo.methods.sort - Object.methods
=> [:%, :&, :+, :-, :/, :<<, :[], :add_class, :after, :all?, :any?, :at, :at_css, :at_xpath, :attr, :attribute, :before, :children, :chunk, :collect, :collect_concat, :count, :css, :cycle, :delete, :detect, :document, :document=, :drop, :drop_while, :each, :each_cons, :each_entry, :each_slice, :each_with_index, :each_with_object, :empty?, :entries, :filter, :find, :find_all, :find_index, :first, :flat_map, :grep, :group_by, :index, :inject, :inner_html, :inner_text, :last, :length, :map, :max, :max_by, :member?, :min, :min_by, :minmax, :minmax_by, :none?, :one?, :partition, :pop, :push, :reduce, :reject, :remove, :remove_attr, :remove_class, :reverse, :reverse_each, :search, :select, :set, :shift, :size, :slice, :slice_before, :sort, :sort_by, :take, :take_while, :text, :to_a, :to_ary, :to_html, :to_xhtml, :to_xml, :unlink, :wrap, :xpath, :zip, :|]

我怀疑uniq没有实现的原因是很难弄清楚如何测试唯一性。一个非常简单的标签,如:

<div class="foo" id="bar">

在功能上与:

相同
<div id="bar" class="foo">

但显而易见的to_s测试将失败,因为它们与字符串相等性测试不匹配。

标签必须在运行中进行标准化,以便将它们的参数放入相同的顺序,然后转换为字符串,但如果class参数在第一个标记中为"foo1 foo2"并且{{ 1}}在第二个? "foo2 foo1"代码是否必须深入了解特定参数并重新排序?而且,如果标签是容器,如uniq,该怎么办?是否应该在div测试中考虑节点的子节点?

我认为这是一种蠕虫,我们大多数人都会迅速退出,而那些试图定义uniq的人会学到关于兔子洞的非常有价值的教训。相反,您可以自由地将uniq定义为适合您的特定应用程序,因此它对您有意义。我认为这对Nokogiri的作者来说是一个很好的设计决定。

答案 1 :(得分:1)

请尝试 -

puts test.map(&:text).uniq

请参阅一个示例代码以演示其工作原理:

require "nokogiri"

doc = Nokogiri::HTML(<<-EOF) 
<a class = "foo" href = "https://example.com"> Click here </a>
EOF

node = 2.times.map{|n| n = Nokogiri::XML::Node.new('title', doc); n.content = "xxx";n }
node # => [#<Nokogiri::XML::Element:0x4637712 name="title" children=[#<Nokogiri::XML::Text:0x4636efc "xxx">]>, #<Nokogiri::XML::Element:0x4637690 name="title" children=[#<Nokogiri::XML::Text:0x4636218 "xxx">]>]


nodeset = Nokogiri::XML::NodeSet.new(doc,node)
nodeset # => [#<Nokogiri::XML::Element:0x4637712 name="title" children=[#<Nokogiri::XML::Text:0x4636efc "xxx">]>, #<Nokogiri::XML::Element:0x4637690 name="title" children=[#<Nokogiri::XML::Text:0x4636218 "xxx">]>]

nodeset.map{|i| i.text }.uniq # => ["xxx"]