hashes - ruby hash map




Ruby: Como transformar um hash em parâmetros HTTP? (10)

A melhor abordagem é usar o Hash.to_params, que é o que funciona bem com matrizes.

{a: 1, b: [1,2,3]}.to_param
"a=1&b[]=1&b[]=2&b[]=3"

Isso é muito fácil com um hash simples como

{:a => "a", :b => "b"} 

o que se traduziria em

"a=a&b=b"

Mas o que você faz com algo mais complexo como

{:a => "a", :b => ["c", "d", "e"]} 

que deve se traduzir em

"a=a&b[0]=c&b[1]=d&b[2]=e" 

Ou pior ainda, (o que fazer) com algo como:

{:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]

Obrigado pela ajuda muito apreciada com isso!


Ainda outra resposta, acontece que há:

require 'uri'
URI.encode_www_form({"one" => "value with space", "two" => ["v1", "v2"]})
# => => "one=value+with+space&two=v1&two=v2"

Observe que ele escapa de espaços como "+", o que pode ou não estar tecnicamente correto para URLs / URIs. Tudo fica muito confuso.


Eu gosto de usar esta jóia:

https://rubygems.org/gems/php_http_build_query

Uso da amostra:

puts PHP.http_build_query({"a"=>"b","c"=>"d","e"=>[{"hello"=>"world","bah"=>"black"},{"hello"=>"world","bah"=>"black"}]})

# a=b&c=d&e%5B0%5D%5Bbah%5D=black&e%5B0%5D%5Bhello%5D=world&e%5B1%5D%5Bbah%5D=black&e%5B1%5D%5Bhello%5D=world

Eu sei que esta é uma questão antiga, mas eu só queria postar esse código, pois não encontrei uma simples jóia para fazer exatamente essa tarefa para mim.

module QueryParams

  def self.encode(value, key = nil)
    case value
    when Hash  then value.map { |k,v| encode(v, append_key(key,k)) }.join('&')
    when Array then value.map { |v| encode(v, "#{key}[]") }.join('&')
    when nil   then ''
    else            
      "#{key}=#{CGI.escape(value.to_s)}" 
    end
  end

  private

  def self.append_key(root_key, key)
    root_key.nil? ? key : "#{root_key}[#{key.to_s}]"
  end
end

Enrolado como gem aqui: https://github.com/simen/queryparams


Não há necessidade de carregar o ActiveSupport inchado ou rodar o seu próprio, você pode usar Rack::Utils.build_query e Rack::Utils.build_nested_query . Aqui está uma postagem no blog que dá um bom exemplo:

require 'rack'

Rack::Utils.build_query(
  authorization_token: "foo",
  access_level: "moderator",
  previous: "index"
)

# => "authorization_token=foo&access_level=moderator&previous=index"

Ele ainda lida com matrizes:

Rack::Utils.build_query( {:a => "a", :b => ["c", "d", "e"]} )
# => "a=a&b=c&b=d&b=e"
Rack::Utils.parse_query _
# => {"a"=>"a", "b"=>["c", "d", "e"]}

Ou as coisas aninhadas mais difíceis:

Rack::Utils.build_nested_query( {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}] } )
# => "a=a&b[][c]=c&b[][d]=d&b[][e]=e&b[][f]=f"
Rack::Utils.parse_nested_query _
# => {"a"=>"a", "b"=>[{"c"=>"c", "d"=>"d", "e"=>"e", "f"=>"f"}]}

Para hashes básicos, não aninhados, o Rails / ActiveSupport tem o Object#to_query .

>> {:a => "a", :b => ["c", "d", "e"]}.to_query
=> "a=a&b%5B%5D=c&b%5B%5D=d&b%5B%5D=e"
>> CGI.unescape({:a => "a", :b => ["c", "d", "e"]}.to_query)
=> "a=a&b[]=c&b[]=d&b[]=e"

http://api.rubyonrails.org/classes/Object.html#method-i-to_query


Se você estiver usando o Ruby 1.9.2 ou posterior, você pode usar o URI.encode_www_form se não precisar de matrizes.

Por exemplo (a partir dos documentos Ruby no 1.9.3):

URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
#=> "q=ruby&lang=en"
URI.encode_www_form("q" => "ruby", "lang" => "en")
#=> "q=ruby&lang=en"
URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")
#=> "q=ruby&q=perl&lang=en"
URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
#=> "q=ruby&q=perl&lang=en"

Você notará que os valores da matriz não são configurados com nomes de chaves que contêm [] como todos nos acostumamos em strings de consulta. A especificação que o encode_www_form usa está de acordo com a definição HTML5 de dados application/x-www-form-urlencoded .


Atualização: essa funcionalidade foi removida da gema.

Julien, sua auto-resposta é boa, e eu sem vergonha pedi emprestada, mas ela não escapa adequadamente dos caracteres reservados, e há alguns outros casos extremos em que ela se desfaz.

require "addressable/uri"
uri = Addressable::URI.new
uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
uri.query
# => "a=a&b[0]=c&b[1]=d&b[2]=e"
uri.query_values = {:a => "a", :b => [{:c => "c", :d => "d"}, {:e => "e", :f => "f"}]}
uri.query
# => "a=a&b[0][c]=c&b[0][d]=d&b[1][e]=e&b[1][f]=f"
uri.query_values = {:a => "a", :b => {:c => "c", :d => "d"}}
uri.query
# => "a=a&b[c]=c&b[d]=d"
uri.query_values = {:a => "a", :b => {:c => "c", :d => true}}
uri.query
# => "a=a&b[c]=c&b[d]"
uri.query_values = {:a => "a", :b => {:c => "c", :d => true}, :e => []}
uri.query
# => "a=a&b[c]=c&b[d]"

A gema é " addressable "

gem install addressable

require 'uri'

class Hash
  def to_query_hash(key)
    reduce({}) do |h, (k, v)|
      new_key = key.nil? ? k : "#{key}[#{k}]"
      v = Hash[v.each_with_index.to_a.map(&:reverse)] if v.is_a?(Array)
      if v.is_a?(Hash)
        h.merge!(v.to_query_hash(new_key))
      else
        h[new_key] = v
      end
      h
    end
  end

  def to_query(key = nil)
    URI.encode_www_form(to_query_hash(key))
  end
end

2.4.2 :019 > {:a => "a", :b => "b"}.to_query_hash(nil)
 => {:a=>"a", :b=>"b"}

2.4.2 :020 > {:a => "a", :b => "b"}.to_query
 => "a=a&b=b"

2.4.2 :021 > {:a => "a", :b => ["c", "d", "e"]}.to_query_hash(nil)
 => {:a=>"a", "b[0]"=>"c", "b[1]"=>"d", "b[2]"=>"e"}

2.4.2 :022 > {:a => "a", :b => ["c", "d", "e"]}.to_query
 => "a=a&b%5B0%5D=c&b%5B1%5D=d&b%5B2%5D=e"

{:a=>"a", :b=>"b", :c=>"c"}.map{ |x,v| "#{x}=#{v}" }.reduce{|x,v| "#{x}&#{v}" }

"a=a&b=b&c=c"

Aqui está outro jeito. Para consultas simples.





hashmap