views:

87

answers:

4

I recently did a class assignment where I made a really hacky data structure. I ended up using nested hashes, which seems like a good idea, but is really hard to iterate through and manage.

I was doing general stuff, like one tag maps to a hash of items that map to prices and stuff like that. But some of them were getting more complicated.

I know that rails uses a lot of more elegant seeming stuff with symbols and such (which I never use shameful face) and I was wondering how I could optimize this. For example if I had my nested hashes something like this

 h["cool"][????][1.2]

is there a graceful way of pulling those values out? Maybe I'm just a total newbie in this regard but I wanted to set things straight before I started doing more things. Maybe I'm even looking for something different like a mix of array/hash or something. Please let me know!

A: 
h["cool"].keys

to then iterate the tree would be

h["cool"].keys.each |outer| { h["cool"][outer].each { |inner| puts inner }}
sunkencity
Sure, I was doing something like that I think. But it's pretty cumbersome, and what if you needed nested arrays of up to 5???
Stacia
@Stacia then you write a recursive version.
EmFi
+2  A: 

It looks like you need to think about structuring your data more rigorously. Try creating a class for your items, which can contain prices among other things, and perhaps organising them in the way you need to access them. Think about what you want and place the information in structures in a way that makes sense to you. Anything else is a waste of time, both now and three months down the line when you need to extend the system and find you can't.

Yes, it'll be quite a bit of work, and yes, it'll be worth it.

Samir Talwar
disclaimer: throw course for this quarter ;)If I have time or it gets dreadful on the next assignment I may try something like that.
Stacia
Ruby is purely object oriented. Classes in ruby really aren't as much work as they are in other languages. You might be able to get by with a mostly empty class and a list of attr_accessors.
EmFi
or even openstruct
Martin DeMello
A: 

It really depends on what you're trying to do (nowhere near enough information in the question), but if you need to dive in three or more levels into a Hash, you may very well want a recursive tree traversal algorithm:

def hash_traverse(hash)
  result = ""
  for key, value in hash
    result << key.to_s + ":\n"
    if !value.kind_of?(Hash)
      result << "  " + value.to_s + "\n"
    else
      result << hash_traverse(value).gsub(/^/, "  ")
    end
  end
  return result
end

Are you sure a Hash is the best data structure for what you're trying to do?

Bob Aman
A: 

Edit: Revised to provide the rough path to the item. It can't know the name of the variable though.

Try this:

def iterate_nested(array_or_hash, depth = [], &block)
  case array_or_hash
    when Array:
      array_or_hash.each_with_index do |item, key|
        if item.class == Array || item.class == Hash
          iterate_nested(item, depth + [key], &block)
        else
          block.call(key, item, depth + [key])
        end
      end
    when Hash:
      array_or_hash.each do |key, item|
        if item.class == Array || item.class == Hash
          iterate_nested(item, depth + [key], &block)
        else
          block.call(key, item, depth + [key])
        end
      end
  end
end

It should iterate to any depth necessary, limited by memory, etc, and return the key and item and depth of the returned item. Works with both hashes and arrays.

If you test with:

iterate_nested([[[1,2,3], [1,2,3]], [[1,2,3], [1,2,3]], [[1,2,3], [1,2,3]]]) do |key, item, depth|
  puts "Element: <#{depth.join('/')}/#{key}> = #{item}"
end

It yields:

Element: <0/0/0/0> = 1
Element: <0/0/1/1> = 2
Element: <0/0/2/2> = 3
Element: <0/1/0/0> = 1
Element: <0/1/1/1> = 2
Element: <0/1/2/2> = 3
Element: <1/0/0/0> = 1
Element: <1/0/1/1> = 2
Element: <1/0/2/2> = 3
Element: <1/1/0/0> = 1
Element: <1/1/1/1> = 2
Element: <1/1/2/2> = 3
Element: <2/0/0/0> = 1
Element: <2/0/1/1> = 2
Element: <2/0/2/2> = 3
Element: <2/1/0/0> = 1
Element: <2/1/1/1> = 2
Element: <2/1/2/2> = 3

Cheerio!

The Wicked Flea