49 votes

Aplatissement d'un hash en chaîne de caractères en Ruby

Existe-t-il un moyen d'aplatir un hachage en une chaîne de caractères, 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 s'imprimer a=b&c=d

J'ai écrit un code à cet effet, mais je me demandais s'il n'y avait pas un moyen plus efficace :

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

109voto

elektronaut Points 1249

Je ne passerais pas outre .flatten qui est déjà défini :

Retourne un nouveau tableau qui est un un aplatissement unidimensionnel de ce hachage. C'est-à-dire que pour chaque clé ou valeur qui est un tableau, on extrait ses éléments dans le nouveau tableau. Contrairement à Array#flatten, cette méthode ne permet pas de aplatit pas récursivement par défaut. Si l'argument l'argument facultatif level détermine le niveau de récursion à aplatir.

C'est la façon la plus simple de le faire, à ma connaissance :

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

22voto

Jörg W Mittag Points 153275

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

Vous pouvez en fait mettre juste un |e| à la place de |k, v| dans ce 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'

6voto

Kelvin Points 5810

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

to_query s'occupe de l'échappement :

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

EDIT

@fahadsadah a raison de ne pas vouloir charger Rails. Même active_support/core_ext/object chargera 71 fichiers. Il modifie également les classes de base.

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.

5voto

Amadan Points 41944

Vous pourriez le faire avec des méthodes standard et des tableaux :

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

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

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

1voto

henrikhodne Points 3569

Je ne sais pas s'il existe un moyen intégré, 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X