web-dev-qa-db-fra.com

Aplatir le hachage en chaîne dans Ruby

Existe-t-il un moyen d'aplatir un hachage en une chaîne, avec des délimiteurs facultatifs entre les clés et les valeurs, et des paires clé/valeur?

Par exemple, print {:a => :b, :c => :d}.flatten('=','&') devrait imprimer a=b&c=d

J'ai écrit du code pour le faire, mais je me demandais s'il y avait une façon plus nette:

class Hash
  def flatten(keyvaldelimiter, entrydelimiter)
    string = ""
    self.each do
      |key, value|
      key = "#{entrydelimiter}#{key}" if string != "" #nasty hack
      string += "#{key}#{keyvaldelimiter}#{value}"  
    end
    return string
  end
end

print {:a => :b, :c => :d}.flatten('=','&') #=> 'c=d&a=b'

Merci

48
Fahad Sadah

Je ne remplacerais pas .flatten, qui est déjà défini:

Renvoie un nouveau tableau qui est un aplatissement unidimensionnel de ce hachage. Autrement dit, pour chaque clé ou valeur qui est un tableau, extrayez ses éléments dans le nouveau tableau. Contrairement à Array # flatten, cette méthode ne s'aplatit pas récursivement par défaut. Si l'argument de niveau facultatif détermine le niveau de récursivité à aplatir.

C'est la façon la plus simple de le faire que je connaisse:

{:a => 100, :b => 200}.map{|k,v| "#{k}=#{v}"}.join('&')
# => "a=100&b=200"
106
elektronaut

Légère variation de la version @ elektronaut:

Vous pouvez réellement y mettre juste un |e| Au lieu de |k, v| Auquel cas e est un tableau de deux éléments et vous pouvez appeler e.join('='). Au total, vous avez quelque chose comme

class Hash
  def join(keyvaldelim=$,, entrydelim=$,) # $, is the global default delimiter
    map {|e| e.join(keyvaldelim) }.join(entrydelim)
  end
end

{a: 100, b: 200}.join('=', '&') # I love showing off the new Ruby 1.9 Hash syntax
# => 'a=100&b=200'
22
Jörg W Mittag

Eh bien, vous pouvez le faire avec des méthodes et des tableaux standard:

class Hash
  def flatten(keyvaldelimiter, entrydelimiter)
    self.map { |k, v| "#{k}#{keyvaldelimiter}#{v}" }.join(entrydelimiter)
  end
end

Vous pourriez également être intéressé par les Rails to_query méthode .

De plus, évidemment, vous pouvez écrire "#{k}#{keyvaldelimiter}#{v}" comme k.to_s + keyvaldelimiter + v.to_s...

5
Amadan

Si vous essayez de générer une chaîne de requête d'URL, vous devez très certainement utiliser une méthode comme to_param d'actifsupport (alias to to_query). Imaginez les problèmes si vous aviez une esperluette ou un signe égal dans les données elles-mêmes.

to_query prend soin de s'échapper:

>> require 'active_support/core_ext/object'
>> {'a&' => 'b', 'c' => 'd'}.to_query
>> => "a%26=b&c=d"

MODIFIER

@fahadsadah fait un bon point sur le fait de ne pas vouloir charger Rails. Même active_support/core_ext/object chargera 71 fichiers. Il classe également les classes principales de patches de singe.

Une solution légère et plus propre:

require 'rack'  # only loads 3 files
{'a&' => 'b', 'c' => 'd'}.map{|pair|
  pair.map{|e| Rack::Utils.escape(e.to_s) }.join('=')
}.join('&')
# => "a%26=b&c=d"

Il est important de s'échapper, sinon l'opération n'est pas réversible.

4
Kelvin

Je ne sais pas s'il existe une méthode intégrée, mais voici un code plus court:

class Hash
  def flatten(kvdelim='', entrydelim='')
    self.inject([]) { |a, b| a << b.join(kvdelim) }.join(entrydelim)
  end
end

puts ({ :a => :b, :c => :d }).flatten('=', '&') # => a=b&c=d
1
sarahhodne

Qu'en est-il de l'obtenir au format JSON. De cette façon, le format est clair.

Il existe de nombreux outils JSON pour Ruby. Mais je ne les ai jamais essayés. Mais la sortie glissera alors bien sûr en javascript, etc. plus facilement. Probablement une ligne.

Les grosses économies, cependant, seraient dans la documentation, car vous en aurez besoin de beaucoup moins.

0
Tom Andersen