2010-06-15 7 views
40

Existe-t-il un moyen d'aplatir un hachage dans une chaîne, avec des délimiteurs optionnels entre les clés et les valeurs, et des paires clé/valeur?Aplatissement de la hachage dans une chaîne dans Ruby

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

j'ai écrit un code pour le faire, mais je me demandais s'il y avait une façon plus propre:

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

Répondre

92

Je ne priment pas sur .flatten qui est déjà défini:

renvoie une nouvelle matrice qui est un aplatissement à une dimension de cette hachage. C'est-à-dire, pour chaque clé ou valeur qui est un tableau, extraire ses éléments dans le nouveau tableau. Contrairement à Array # aplati, cette méthode n'affecte pas de manière récursive par défaut. Si l'argument de niveau optionnel détermine le niveau de récursivité à aplatir.

C'est une façon de le faire que le plus simple, je suis au courant:

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

Vous pouvez encore simplifier cela. Voir ma réponse pour plus de détails. L'essentiel est de remplacer '| k, v |' par '| e |', ce qui fait de 'e' un tableau que vous pouvez appeler' e.join ('=') '. –

+1

comment faire l'inverse pour obtenir un hachage – jenuine

4

Eh bien, vous pourriez 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 le Rails to_query method.

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

+2

Ou '[k, v] .join (keyvaldelimiter)'. – henrikhodne

1

Je ne sais pas s'il y a une façon 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 
21

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

Vous pouvez réellement mettre juste un là |e| au lieu de |k, v| en quel 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' 
+5

Je voudrais sérieusement remettre en question la façon dont il est intelligent de patch de singe 'Hash', en particulier avec un nom de méthode non nommé comme' join'. Alors que je m'attends à ce que cela soit le cas, je suis très surpris de voir une telle suggestion de la part d'un développeur très expérimenté. – akostadinov

0

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. Je ne les ai jamais essayés, cependant. Mais la sortie va bien sûr glisser en javascript, etc. Probable une ligne.

Les grandes économies, bien que ce soit dans la documentation, en ce que vous aurez besoin de beaucoup moins.

4

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

to_query prend soin de Escaping:

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

EDIT

@fahadsadah fait un bon point de ne pas vouloir charger Rails. Même active_support/core_ext/object chargera 71 fichiers. C'est aussi des classes de base de singes-correctifs.

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 d'échapper, sinon l'opération est irréversible.

+0

Tout cela est très bien, et c'est un bon point, mais je préfère ne pas avoir besoin d'installer Rails quand j'ai créé le hash source –

+0

Rails pour la victoire !! –

Questions connexes