Archonic
Archonic

Reputation: 5382

How do I check property of next item in each/do loop in Ruby?

Using RoR, I would like a helper to write a table of contents menu where root sections are dropdown menus for their subsections. In an each/do loop I would need to check if a section has subsections before outputting class="dropdown" on li and class="dropdown-toggle" data-toggle="dropdown" on the link.

Is there a way to check the properties of the next item (if any) in an each/do loop? Or do I need to switch to a loop with an index?

Here's my table of contents helper as is.

def showToc(standard)
  html = ''
  fetch_all_sections(standard).each do |section|
    html << "<li>" << link_to("<i class=\"icon-chevron-right\"></i>".html_safe + raw(section[:sortlabel]) + " " + raw(section[:title]), '#s' + section[:id].to_s) << "</li>"
    end
  end
  return html.html_safe
end

Upvotes: 0

Views: 149

Answers (3)

tokland
tokland

Reputation: 67900

You can use the abstraction Enumerable#each_cons. An example:

>> xs = [:a, :b, :c]
>> (xs + [nil]).each_cons(2) { |x, xnext| p [x, xnext] }
[:a, :b]
[:b, :c]
[:c, nil]

That said, note your code is full of unidiomatic Ruby, you should probably post it to https://codereview.stackexchange.com/ for review.

Upvotes: 2

Archonic
Archonic

Reputation: 5382

I found a way to have class="dropdown" on the <li> and class="dropdown-toggle" data-toggle="dropdown" on the link not affect the anchor tag. Therefore, in this case, I can just check if section depth is 0 and act accordingly. The other answers are probably more relevant to most people but here's what worked for me.

def showToc(standard, page_type, section = nil, nav2section = false, title = nil, wtf=nil)
  html = ''
  new_root = true

  fetch_all_sections(standard).each do |section|
    if section[:depth] == 0
      if !new_root
        # end subsection ul and root section li
        html << "</li>\n</ul>"
        new_root = true
      end
      html << "<li class=\"dropdown\">" << link_to("<i class=\"icon-chevron-right\"></i>".html_safe + raw(section[:sortlabel]) + " " + raw(section[:title]), '#s' + section[:id].to_s, :class => "dropdown-toggle", :data => {:toggle=>"dropdown"})
    else
      # write ul if new root
      if new_root
        new_root = false
        html << "<ul class=\"dropdown-menu\">\n" << "<li>" << link_to(raw(section[:sortlabel]) + " " + raw(section[:title]), '#s' + section[:id].to_s) << "</li>"
      else
        html << "<li>" << link_to(raw(section[:sortlabel]) + " " + raw(section[:title]), '#s' + section[:id].to_s) << "</li>"
      end
    end
  end
  return html.html_safe
end

Upvotes: 0

booleys1012
booleys1012

Reputation: 731

If i'm reading your question correctly -- lets say fetch_all_sections(standard) returns an enumerable, such as Array, you could add a custom iterator to get what you want:

class Array
   #yields |current, next|
   def each_and_next
      @index ||= 0
      yield [self[@index], self[@index +=1]] until (@index == self.size)
      @index = 0
   end
end

p.s. I like @tokland's inline answer


a = [1,2,3,4]
a.each_and_next { |x,y| puts "#{x},#{y}" }

produces:
1,2
2,3
3,4
4,

Upvotes: 0

Related Questions