开发者

handling data structures (hashes etc) gracefully in ruby

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 stra开发者_JS百科ight 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!


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.


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!


h["cool"].keys

to then iterate the tree would be

h["cool"].keys.each |outer| { h["cool"][outer].each { |inner| puts inner }}


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?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜