Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I have a model section with a column parent_id - the only thing identifying a parent/child relationship. I wrote a helper method to output all sections, all their possible subsections and include their "depth". This is the helper method:

# Calls itself for each section recursively to fetch all possible children
def fetch_all_sections(standard, section = nil, depth = 0)
  all_sections = []
  if section.nil?
    rootsections = roots(standard.sections.sorted)
    if ! rootsections.nil?
      rootsections.each do |section|
        all_sections.push(fetch_all_sections(standard, section))
      end
    end
  else
    all_sections.push({:id => section.id, :sortlabel => section.sortlabel, :title => section.title, :depth => depth})
    section.children.sorted.each_with_index do |section, index|
      all_sections.push(fetch_all_sections(standard, section, (depth += 1) - index))
    end
  end
  return all_sections
end

sorted is a module method which sorts based on the sort_key column using regular expressions. The output of debug(fetch_all_sections(@standard)) is:

- - "1 Collaborase Test Document depth: 0"
  - - "1.1 Section One Dot One depth: 1"
  - - "1.a Testing 123 depth: 1"
  - - "1.b Section One dot B depth: 1"
    - - "1.b.1 Testing One B One depth: 2"
- - "2 Identify Project GHG Sources, Sinks, and Reservoirs (SSRs) depth: 0"
  - - "2.a Section Two dot A depth: 1"
- - "3 Identify the Baseline depth: 0"
- - "4 Identify the Baseline GHGs depth: 0"
- - "5 Identify SSRs Relevant for Quantification depth: 0"
  - - "5.1 Section Five Dot One depth: 1"
- - "6 GHG Quantification depth: 0"
- - "7 Monitoring depth: 0"
- - "8 Data and Information Retention depth: 0"
- - "9 QA/QC depth: 0"

My questions is this: In this loop:

<% fetch_all_sections(@standard).each do |section| %>
  <%= debug section %>
<% end %>

I want each section and all their children to be separated. Instead, each section in the above loop contains their children. Instead of this:

--- 
- - :id: 122
    :sortlabel: "5"
    :title: Identify SSRs Relevant for Quantification
    :depth: 0
- - - :id: 412
      :sortlabel: "5.1"
      :title: Section Five Dot One
      :depth: 1

I want this:

--- 
- - :id: 122
    :sortlabel: "5"
    :title: Identify SSRs Relevant for Quantification
    :depth: 0
---
- - :id: 412
    :sortlabel: "5.1"
    :title: Section Five Dot One
      :depth: 1

I hope that's clear.

share|improve this question
    
Derp. The syntax is all_sections.push([section.sortlabel, section.title, depth]). I'll leave the question open to see if anyone comes up with something better. –  Archonic Jan 16 '13 at 19:38
    
I opted for pushing keys for easier retrieval: all_sections.push({:id => section.id, :sortlabel => section.sortlabel, :title => section.title, :depth => depth}) –  Archonic Jan 16 '13 at 20:39
add comment

1 Answer 1

up vote 1 down vote accepted

What I needed was the pipe operator! It was of course just doing what I was telling it to - pushing the results of itself onto the current element in the array. Only when the rootsections loop completes does it start a new array element.

The 'pipe' (|) is a different array merge operator which appends what's to the right of it onto the end of the array on the left.

all_sections | fetch_all_sections(standard, section, depth)

The pipe operator changed how depth worked so instead I just counted the number of "." in sortlabel:

depth = section.sortlabel.split('.').length -1

The full method:

# Calls itself for each section recursively to fetch all possible children
def fetch_all_sections(standard, section = nil, depth = 0)
  all_sections = []

  if section.nil?
    rootsections = standard.sections.sorted
    if ! rootsections.nil?
      rootsections.each_with_index do |section, i|
        depth = section.sortlabel.split('.').length - 1
        all_sections << fetch_all_sections(standard, section, depth).first
      end
    end
  else

    all_sections << {:id => section.id, :sortlabel => section.sortlabel, :title => section.title, :depth => depth}

    section.children.sorted.each do |section|
      all_sections | fetch_all_sections(standard, section)
    end
  end

  return all_sections
end

If someone seeing this is considering something similar but starting from scratch, consider an Active Record Nesting gem: https://www.ruby-toolbox.com/categories/Active_Record_Nesting

UPDATE: I added .first within the rootsections loop. This is because I inadvertently built an array of arrays, each containing 1 hash. What I wanted was an array of hashes. Much easier retrieval now!

share|improve this answer
    
Accept your own answer. :) –  Quentin Pradet Feb 19 '13 at 7:03
    
Thanks for the reminder! –  Archonic Feb 19 '13 at 16:52
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.