0

I have a hash like this:

config = {
  :assets => {
    :path => '/assets',
    :aliases => {
      '/assets/' => '/assets/index.html',
      '/assets/index.htm' => '/assets/index.html'
    }
  },
  :api => {
    :path => '/auth/api',
    :aliases => {
      '/auth/api/me' => '/auth/api/whoami'
    }
  }
}

Is it possible to remove duplicates and have config[...][:aliases] assigned in terms of config[...][:path] like "#{dict_im_in()[:path]}/index.html"?

4

2 に答える 2

1

What you are trying to do smells of premature optimization. I'd recommend concentrating on using symbols as much as possible, both as keys and values, in your hash. Symbols are very memory efficient and fast to lookup because Ruby will only create one memory slot for a given symbol, effectively doing what you're trying to do.

Your example structure could be rewritten using symbols:

config = {
  :assets => {
    :path => :'/assets',
    :aliases => {
      :'/assets/' => :'/assets/index.html',
      :'/assets/index.htm' => :'/assets/index.html'
    }
  },
  :api => {
    :path => :'/auth/api',
    :aliases => {
      :'/auth/api/me' => :'/auth/api/whoami'
    }
  }
}

:'/assets/index.html', no matter where it's seen, would always point to the same symbol, unlike a string.

Any time you need to access a value in something expecting a string, do a to_s to it. When you add a value, do a to_sym to it.

Behind the scenes, you'll be using memory more efficiently in your Hash, and using what should be the fastest value lookup available in Ruby.

Now, if you're using a hash structure to configure your application, and it's being stored on disk, you can get some space savings using YAML, which supports aliases and anchors inside its data file. They won't make pointers inside the hash after parsing into a Ruby object, but they can reduce the size of the file.


EDIT:

If you need to dynamically define a configuration on the fly, then create variables for all the sub-parts of the various strings, then add a method that generates it. Here's the basics for dynamically creating such a thing:

require 'awesome_print'

def make_config(
  assets = '/assets',
  index_html = 'index.html',
  auth_api = '/auth/api'
)
  assets_index = File.join(assets, index_html)
  {
    :assets => {
      :path => assets,
      :aliases => {
        assets + '/' => assets_index,
        assets_index[0..-2] => assets_index
      }
    },
    :api => {
      :path => auth_api,
      :aliases => {
        File.join(auth_api, 'me') => File.join(auth_api, 'whoami')
      }
    }
  }
end

ap make_config()
ap make_config(
  '/new_assets',
  ['new','path','to','index.html'],
  '/auth/new_api'
)

With what would be generated:

{
    :assets => {
          :path  => "/assets",
        :aliases => {
                    "/assets/"  => "/assets/index.html",
            "/assets/index.htm" => "/assets/index.html"
        }
    },
      :api => {
          :path  => "/auth/api",
        :aliases => {
            "/auth/api/me" => "/auth/api/whoami"
        }
    }
}

and:

{
    :assets => {
          :path  => "/new_assets",
        :aliases => {
                                "/new_assets/"  => "/new_assets/new/path/to/index.html",
            "/new_assets/new/path/to/index.htm" => "/new_assets/new/path/to/index.html"
        }
    },
      :api => {
          :path  => "/auth/new_api",
        :aliases => {
            "/auth/new_api/me" => "/auth/new_api/whoami"
        }
    }
}

Edit:

An alternate way of duplicating value pointers, is to create lambdas or procs that return the information you want. I think of them like they're function pointers in C or Perl, allowing us to retain the state of a variable that was in scope, or to manipulate the passed-in values. This isn't a perfect solution, because of variable scoping that occurs, but it's another tool for the box:

lambda1 = lambda { 'bar' }
proc2   = Proc.new { |a,b| a + b }
proc3   = Proc.new { proc2.call(1,2) }

foo = {
  :key1 => lambda1,
  :key2 => proc2,
  :key3 => proc2,
  :key4 => proc3
}

foo[:key1].object_id # => 70222460767180
foo[:key2].object_id # => 70222460365960
foo[:key3].object_id # => 70222460365960
foo[:key4].object_id # => 70222460149600

foo[:key1].call          # => "bar"
foo[:key2].call(1, 2)    # => 3
foo[:key3].call(%w[a b]) # => "ab"
foo[:key4].call          # => 3

See "When to use lambda, when to use Proc.new?" for more information on lambdas and procs.

于 2013-01-08T14:25:04.683 に答える
-1

This is an interesting question ;).

I think you could "reopen" the hash and then just use the variable name.

For example:

config = {
  :assets => {
    :path => '/assets',
    :aliases => {
      '/assets/' => '/assets/index.html',
      '/assets/index.htm' => '/assets/index.html'
    }
  }
}
# Then "re-open" the Hash
config[:api] = {
  :path => '/auth/api',
  :aliases => {
    '/auth/api/me' => config[:assets][:aliases]['/assets/index.htm']
  }
}

You could also write a helper method to extract a given alias from that hash. But I'm not sure if it's worth it.

于 2013-01-08T12:43:34.720 に答える