lambshaanxy
lambshaanxy

Reputation: 23062

Determining child index of a Nokogiri element

if I have a Nokogiri::XML::Element, how do I figure its child index in relation to its parent? That is:

<foo>     <-- parent
  <bar/>  <-- 1st child
  <bar/>  <-- 2nd child
</foo>

In Javascriptland, jQuery has index(), but Nokogiri doesn't. Nokogiri does provide a path method, but that returns an XPath string like "/foo/bar[2]" and truncates bar[1] to bar to boot, so turning that back into a number is a little hairy:

element.path.split('/').last.slice(/[0-9]+/) || '1'          # quick'n'dirty
element.path.split('/').last.slice(/\[([0-9]+)\]/, 1) || '1' # a bit safer

Upvotes: 3

Views: 2376

Answers (3)

Phrogz
Phrogz

Reputation: 303244

Technique 1: Find my siblings, see where I fit within them:

idx = element.parent.element_children.index(element)

# Alternatively:
idx = element.parent.xpath('*').index(element)

Technique 2: Count the number of element (not text) nodes before me:

idx = element.xpath('count(preceding-sibling::*)').to_i

# Alternatively, if you want to waste time and memory:
# idx = element.xpath('preceding-sibling::*').length

Technique 2 is measurably faster.

Upvotes: 3

unnu
unnu

Reputation: 754

This selects nodes of the same element name and returns the index:

element.parent.xpath(element.name).index(element)

Upvotes: 1

pguardiario
pguardiario

Reputation: 54984

How about:

element.parent.children.index(element)

To consider only non-text nodes:

element.xpath('../*').index(element)

To consider only bar nodes:

element.xpath('../bar').index(element)

Upvotes: 7

Related Questions