views:

49

answers:

2

I want to convert something like this:

class NestedItem
  attr_accessor :key, :children
  def initialize(key, &block)
    self.key = key
    self.children = []
    self.instance_eval(&block) if block_given?
  end

  def keys
    [key] + children.keys
  end
end

root = NestedItem.new("root") do
  children << NestedItem.new("parent_a") do
    children << NestedItem.new("child_a")
    children << NestedItem.new("child_c")
  end
  children << NestedItem.new("parent_b") do
    children << NestedItem.new("child_y")
    children << NestedItem.new("child_z")
  end
end

require 'pp'
pp root
#=>
# #<NestedItem:0x1298a0
#  @children=
#   [#<NestedItem:0x129814
#     @children=
#      [#<NestedItem:0x129788 @children=[], @key="child_a">,
#       #<NestedItem:0x12974c @children=[], @key="child_c">],
#     @key="parent_a">,
#    #<NestedItem:0x129738
#     @children=
#      [#<NestedItem:0x129698 @children=[], @key="child_y">,
#       #<NestedItem:0x12965c @children=[], @key="child_z">],
#     @key="parent_b">],
#  @key="root">

Into this:

root.keys #=>
[
  "root",
  "root.parent_a",
  "root.parent_a.child_a",
  "root.parent_a.child_c",
  "root.parent_b",
  "root.parent_b.child_y",
  "root.parent_b.child_z",
]

...using a recursive method.

What's the simplest way to go about this?

Update

I did this:

def keys
  [key] + children.map(&:keys).flatten.map do |node|
    "#{key}.#{node}"
  end
end

Anything better?

A: 

Would Array.flatten work for you?

self.children.flatten should return the flattened results.

Anurag
yeah, I think I got it. thanks. updated the response!
viatropos
A: 

Yes, .flatten will produce what I think you really want.

But if you want exactly the string output you typed, this will do it:

  def keys x
    here = key
    here = x + '.' + here if x
    [ here ] + children.inject([]) { |m,o| m += o.keys here }
  end

pp root.keys nil

Or, alternatively, replace the last line in #keys with:

([ here ] + children.map { |o| o.keys here }).flatten
DigitalRoss