0

I need to import the fruit nodes in a specific order (Orange, Apple, Cherry), not alphabetical or numerical:

<fruits>
  <fruit>
    <name>Apple</name>
    <expiration>2018-08-21</expiration>
  </fruit>
  <fruit>
    <name>Banana</name>
    <expiration>2018-08-29</expiration>
  </fruit>
  <fruit>
    <name>Cherry</name>
    <expiration>2018-08-29</expiration>
  </fruit>
  <fruit>
    <name>Orange</name>
    <expiration>2018-08-20</expiration>
  </fruit>
</fruits>

Prior to the new sorting requirement, we enumerated through the nodes like this, and it worked great:

doc.css('fruit').each do |fruit|
  Pantry.create({name:fruit.css('name').first.text,
                 expiration: fruit.css('expiration').text})
end

I attempted using xpath, based on this answer but it only returns the name subnode, not the parent fruit node, resulting in the expiration subnode not being accessible:

fruit_basket = %w[Orange Apple Cherry]
fruit_basket.each do |temp_fruit|  
  fruit = doc.xpath("//name[contains(text(), '#{temp_fruit}')]")
  Pantry.create({name:fruit.css('name').text,
                 expiration: fruit.css('expiration').text})
end

What's the proper way (nokogiri-way?) to enumerate through the fruit nodes in the order of my a fruit_basket array, ignoring fruit nodes that are not in the fruit_basket?

webaholik
  • 1,619
  • 1
  • 19
  • 30

1 Answers1

0

Nokogiri returns an Enumerable, you might sort it as you need:

fruit_basket = %w[Orange Apple Cherry]

doc.
  xpath('//fruit').
  sort_by do |e| # HERE
    fruit_basket.index(e.xpath('name').text) || Float::INFINITY
  end.
  each { |e| puts e.text }

#⇒ Orange
#  2018-08-20
#  Apple
#  2018-08-21
#  Cherry
#  2018-08-29
#  Banana
#  2018-08-29

Whether you need to skip fruits not listed, filter it upfront:

doc.
  xpath('//fruit').
  select do |e| # HERE
    fruit_basket.index(e.xpath('name').text)
  end.
  sort_by do |e|
    fruit_basket.index(e.xpath('name').text)
  end.
  each { |e| puts e.text }
Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160