-2

I need an operation which I call shake_tree. I have implemented it using a recursive algorithm, using only basic ruby-Fortran (referencing the old quote "You can write Fortran code in any language."), but I suspect there is a much more concise and idiomatic ruby way.

Since I'm unaware of a common name for this operation, let me describe it briefly. I have a hash of hashes like this example:

{
  "-cutoff:" =>
  {
    :flag => {:set_ie1 => [:useCutoff, true]},
    :arg => {:vector_ie1 => :double}
  },
  "-depth:" =>
  {
    :flag => {:set_ie2 => [:useInconsistent, true]},
    :arg => :double,
    :default => 2.0
  },
  "-maxclust:" =>
  {
    :flag => {:set_ie3 => [:useCutoff, false]},
    :arg => {:vector_ie2 => :index}
  },
  :fn => "arrayTypeOptions"
}

There are unique symbols like :vector_ie1 and :set_ie3 embedded within the structure of the tree. I need to remove all branches of the tree other than the path from the root to the leave with the symbol. Given the example above:

shake_tree(specs, :vector_ie1)

would return:

{
  "-cutoff:" =>
  {
    :flag => {:set_ie1 => [:useCutoff, true]},
    :arg => {:vector_ie1 => :double}
  }
}

and

shake_tree(specs, :set_ie2)

would return:

{
  "-depth:" =>
  {
    :flag => {:set_ie2 => [:useInconsistent, true]},
    :arg => :double,
    :default => 2.0
  }
}

How would a more experienced ruby coder approach this task?

Kaelin Colclasure
  • 3,855
  • 1
  • 22
  • 36
  • Why aren't you using a tree library like [RubyTree](http://rubytree.rubyforge.org/rdoc/index.html)? It has a `parentage` method that will give you this. – Mark Thomas Apr 26 '13 at 01:55
  • @MarkThomas: Thanks for the link, but at a glance it looks like overkill. The whole idea of a **ruby** DSL is to let **ruby** do most of the work… Right now the "parser" of my DSL consists of calling `vMATCodeMonkey#instance_eval` on the "source" string! :-) – Kaelin Colclasure Apr 26 '13 at 02:32

1 Answers1

1

Here is my recursive implementation. I decided to call it shake_tree to keep RubyMine's spell-checker happy (and because I liked the sound of shake_tree specs key):

def shake_tree(specs, key)
  parent = find_parent(specs, key)
  parent ? { parent => specs[parent] } : nil
end

def find_parent(specs, key, keypath = [])
  specs.each do |k, v|
    if k == key
      return (keypath + [k])[0]
    elsif v.is_a?(Hash)
      branch = find_parent(v, key, keypath + [k])
      if !branch.nil?
        return branch
      end
    end
  end
  nil
end

This returns exactly the output specified above.

I'm still curious to know if there's a common name for this.

Kaelin Colclasure
  • 3,855
  • 1
  • 22
  • 36
  • I was too late by a 15 seconds.. so I'll paste my answer here. Sorry for compression. but comments are, well, tight – quetzalcoatl Apr 26 '13 at 08:13
  • I am not entirely sure whether your data is really a tree. This example seems more like a set/list of structured data entries in form of KEY=>ENTRY, and the Entry is either a simple value of well-defined structure. Of course, speaking strictly, it is a tree, but not only a tree :) Unless your "entries" are more complicated than a simple-value or simple-hash, you can easily **filter** that just by a SELECT or REJECT functions without recursion. – quetzalcoatl Apr 26 '13 at 08:13
  • 1
    Example (data = your-input-data): `data.select{ |key,entry| [:flag,:arg].map {|k| entry[k].keys rescue nil }.flatten.include?( :vector_ie2 ) }` – quetzalcoatl Apr 26 '13 at 08:14
  • Effectively, this is almost oneliner. Note that ":flag, :arg" list in the middle and .include? at end - you might want to adjust them better. Excuse me for that ugly rescue nil, I wanted to keep the idea short, not "fully-featured" with responds_to(operator[]) and etc. However, if your your keys-to-find may happen to be deeper than always at depth=3, then surely recursion and tree-node filtering is required. – quetzalcoatl Apr 26 '13 at 08:15
  • BTW. don't get me wrong: the shake_tree is a great operation for filtering what-ever-is-a-tree and I'm not strongly suggesting you to replace it with list/set-based filtering. Just think about your exact structure of data and pick matches the best! – quetzalcoatl Apr 26 '13 at 08:23
  • Well, if mods hadn't closed this question I would ask that you put your suggestion in a second answer. Exactly as I suspected **ruby** offers a more idiomatic way to do this that I hadn't known about. In fact, if you do post your answer I will accept yours; it's better than mine. – Kaelin Colclasure Apr 26 '13 at 11:40
  • Thanks. I actually find it hard to understand why it was closed. Probably the voters did not understood what is 'three shaking' term and took it as 'vague', or it was "he did not write what he has tried"-syndrome which often gets closed as not-a-question. However, I think it's not that worth to reopen either, as your answer refers to what you asked for: tree shaking, whereas mine maybe solves your problem but does not answer the question ;-) – quetzalcoatl Apr 26 '13 at 11:54