हैश में प्रतीकों को तार बदलने का सबसे अच्छा तरीका है


250

रूबी में तार से प्रतीकों में हैश में सभी कुंजियों को परिवर्तित करने का सबसे तेज़ (सबसे साफ / सरल / सीधा) तरीका क्या है?

YAML को पार्स करते समय यह आसान होगा।

my_hash = YAML.load_file('yml')

मैं उपयोग करने में सक्षम होना चाहता हूं:

my_hash[:key] 

बजाय:

my_hash['key']


80
hash.symbolize_keysऔर hash.deep_symbolize_keysयदि आप रेल का उपयोग कर रहे हैं तो काम करें।
ज़ाज़

जोश अगर आप अपनी टिप्पणी एक जवाब में डालते, तो मैं आपको वोट देता। आवश्यकता है 'रेल'; hash.deep_symbolize_keys irb या pry में बहुत अच्छी तरह से काम करता है। : डी
डगलस जी। एलन

जवाबों:


239

में रूबी> = 2.5 ( डॉक्स ) आप का उपयोग कर सकते हैं:

my_hash.transform_keys(&:to_sym)

पुराने रूबी संस्करण का उपयोग करना? यहाँ एक-लाइनर है जो हैश को प्रतीक के रूप में एक नए में कॉपी करेगा:

my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}

रेल के साथ आप उपयोग कर सकते हैं:

my_hash.symbolize_keys
my_hash.deep_symbolize_keys 

5
आह, अस्पष्ट होने के लिए खेद है - इंजेक्टर कॉलर को संशोधित नहीं करता है। आपको my_hash = my_hash.inject ({}) {| ज्ञापन, (k, v) करने की आवश्यकता है ज्ञापन [k.to_sym] = v; ज्ञापन}
सारा मेई

4
ठीक वैसा ही मैं देख रहा था। मैंने इसे थोड़ा संशोधित किया और कुछ पंक्तियों को जोड़ा जो कि घोंसले की राख में प्रतीक बनाने के लिए था। यहां देखें: यदि आप रुचि रखते हैं: any-de.de/blog/ruby-hash-convert-string-keys-to-symbols
मैट

37
रूबी 1.9 में आप ऐसा इस्तेमाल कर सकते हैं each_with_object:my_hash.each_with_object({}){|(k,v), h| h[k.to_sym] = v}
sgtFloyd

8
यह पुनरावर्ती हैश को संभाल नहीं करता है ... एक-बंद के लिए खोजें, लेकिन DRY के लिए नहीं।
baash05

8
@BryanM। मैं इस चर्चा में बहुत देर से आया हूं :-) लेकिन आप अंत में .tapपारित memoहोने की आवश्यकता को दूर करने के लिए विधि का उपयोग भी कर सकते हैं । मैंने सभी समाधानों का एक साफ किया हुआ संस्करण बनाया है (साथ ही पुनरावर्ती) gist.github.com/Integralist/9503099
इंटीग्रलिस्ट

307

यदि आप रेल का उपयोग कर रहे हैं, तो यह एक बेहतर तरीका है:

पैरामीटर। symbolize_keys

समाप्त।

यदि आप नहीं हैं, तो बस उनके कोड को चीर दें (यह लिंक में भी है):

myhash.keys.each do |key|
  myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end

6
to_options के लिए एक उपनाम है sybolize_keys
ma11hew28

45
नेस्टेड हैश का प्रतीक नहीं होगा।
oma

3
मैंने लिंक को symbolize_keysनए और काम कर रहे (नियम 3) URL के साथ बदल दिया । मैंने मूल रूप से इसके लिए केवल URL तय किया था to_options, लेकिन उस लिंक पर शून्य प्रलेखन है। symbolize_keysवास्तव में इसका वर्णन है, इसलिए मैंने इसके बजाय इसका इस्तेमाल किया।
क्रेग वॉकर

23
deep_symbolize_keys! । पटरियों पर काम करता है 2+
mastaBlasta

14
उन लोगों के लिए जो उल्टा hash.stringify_keysकाम करते हैं।
निक

112

रूबी में YAML के विशिष्ट मामले के लिए, यदि कुंजी ' :' से शुरू होती है , तो वे स्वचालित रूप से प्रतीक के रूप में नजरबंद हो जाएंगे।

'यामल' की आवश्यकता
'पीपी' की आवश्यकता
yaml_str = "
सम्बन्ध:
  - मेजबान: host1.example.com
    पोर्ट: 10000
  - मेजबान: host2.example.com
    पोर्ट: 20000
"
yaml_sym = "
:सम्बन्ध:
  - - मेजबान: host1.example.com
    : पोर्ट: 10000
  - - मेजबान: host2.example.com
    : पोर्ट: 20000
"
pp yaml_str = YAML.load (yaml_str)
yaml_str.keys.first.class डालता है
पीपी yaml_sym = YAML.load (yaml_sym)
yaml_sym.keys.first.class डालता है

आउटपुट:

# # //ruby-1.8.6-p287/bin/ruby ~ / test.rb
{ "कनेक्शन" =>
  [{"पोर्ट" => 10000, "होस्ट" => "host1.example.com"},
   {"पोर्ट" => 20000, "होस्ट" => "host2.example.com"}]}
तार
{: कनेक्शन =>
  [{: port => 10000,: host => "host1.example.com"},
   {= port => 20000,: host => "host2.example.com"}]}
प्रतीक

15
मिठाई! वहाँ एक YAML#load_fileतार के बजाय प्रतीकों के लिए सभी कुंजी को डिफ़ॉल्ट करने के लिए सेट करने के लिए एक रास्ता है / ओ एक बृहदान्त्र के साथ हर कुंजी शुरू करने के लिए?
ma11hew28


60

यदि आप रेल का उपयोग कर रहे हैं, तो यह बहुत सरल है - आप एक HashWithIndifferentAccess का उपयोग कर सकते हैं और स्ट्रिंग और प्रतीक के रूप में दोनों कुंजियों का उपयोग कर सकते हैं:

my_hash.with_indifferent_access 

यह सभी देखें:

http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html


या आप भयानक "रूबी के पहलुओं" रत्न का उपयोग कर सकते हैं, जिसमें रूबी कोर और मानक लाइब्रेरी कक्षाओं के बहुत सारे एक्सटेंशन हैं।

  require 'facets'
  > {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys
    =>  {:some=>"thing", :foo=>"bar}

इसे भी देखें: http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core/json#api-class-Hash


2
वास्तव में यह विपरीत है। यह प्रतीक से एक स्ट्रिंग में परिवर्तित होता है। प्रतीक में परिवर्तित होने के लिए my_hash.symbolize_keys
Espen

#symbolize_keys केवल रेल में काम करता है - सादे रूबी / irb में नहीं। यह भी ध्यान दें कि #symbolize_keys गहरी नेस्टेड हैश पर काम नहीं करता है।
तिलो

54

चूंकि Ruby 2.5.0आप उपयोग कर सकते हैं Hash#transform_keysया Hash#transform_keys!

{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}

इसके अलावा ट्रांसफॉर्म_वेल्यूज के साथ काम करता है apidock.com/rails/Hash/transform_values । यह वास्तव में अच्छा है क्योंकि वहाँ के साथ stringify_keysया जैसे मूल्यों को संशोधित करने के लिए एक समान प्रतीत नहीं होता है symbolize_keys
माइक वालानो

क्या चाबी का गहरा प्रतीक है
अभय कुमार

2018 के बाद यह चुना हुआ समाधान होना चाहिए।
इवान वांग


26

यहाँ एक वस्तु को गहरा प्रतीक करने का एक तरीका है

def symbolize(obj)
    return obj.inject({}){|memo,(k,v)| memo[k.to_sym] =  symbolize(v); memo} if obj.is_a? Hash
    return obj.inject([]){|memo,v    | memo           << symbolize(v); memo} if obj.is_a? Array
    return obj
end

अच्छा एक, मैं इस के साथ जाऊंगा, भले ही मैं इसका नाम बदलकर डीप_सिमबोल कर दूं :)
पियरोज़

20

मुझे वास्तव में मैश रत्न पसंद है ।

आप कर सकते हैं mash['key'], या mash[:key], याmash.key


2
यह एक बहुत अच्छा रत्न है! हैश के साथ काम करना बहुत आरामदायक है। इसके लिए धन्यवाद!
आसाकी

इतनी सुंदर सरल। इस परियोजना पर नया विकास हाशि ( github.com/intridea/hashie ) में जारी रखा जा रहा है, लेकिन यह अभी भी उसी तरह से बहुत काम करता है: github.com/intridea/hashie#keyconversion
jpalmierea

19

यदि आप json का उपयोग कर रहे हैं, और इसे हैश के रूप में उपयोग करना चाहते हैं, तो कोर रूबी में आप इसे कर सकते हैं:

json_obj = JSON.parse(json_str, symbolize_names: true)

symbolize_names : यदि यह सही पर सेट है, तो JSON ऑब्जेक्ट में नामों (कुंजियों) के लिए प्रतीक लौटाता है। अन्यथा तार वापस कर दिए जाते हैं। स्ट्रिंग्स डिफ़ॉल्ट हैं।

Doc: Json # parse symbolize_names


symbol_hash = JSON.parse(JSON.generate(YAML.safe_load(FILENAME)), symbolize_names: true)एक सुंदर DRY (लेकिन अकुशल) तरीका है कि अगर वह YAML फ़ाइल से आ रहा है तो प्रतीक के रूप में नेस्टेड कुंजियों के साथ एक हैश प्राप्त करें।
कैमरून गगनोन

12

params.symbolize_keysकाम भी करेगा। यह विधि हैश कीज़ को प्रतीकों में बदल देती है और एक नया हैश लौटाती है।


26
वह तरीका कोर रूबी नहीं है। यह एक रेल विधि है।
रिकार्डो अक्रास

10

@Igorsales उत्तर के लिए एक संशोधन

class Object
  def deep_symbolize_keys
    return self.inject({}){|memo,(k,v)| memo[k.to_sym] = v.deep_symbolize_keys; memo} if self.is_a? Hash
    return self.inject([]){|memo,v    | memo           << v.deep_symbolize_keys; memo} if self.is_a? Array
    return self
  end
end

यह उपयोगी होगा यदि आपने शामिल किया कि आप उत्तर के माध्यम से स्कैन करने वाले लोगों के लिए ऑब्जेक्ट को संशोधित क्यों कर रहे हैं।
Dbz

9

रेल में आप उपयोग कर सकते हैं:

{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

धर्मान्तरित:

{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}

3
deep_symbolize_keysरेल के हैश एक्सटेंशन में जोड़ा जाता है , लेकिन यह रूबी कोर का हिस्सा नहीं है।
रयान क्रिस्पिन हेनेसी

7

यहाँ बहुत सारे उत्तर हैं, लेकिन एक विधि कार्य फ़ंक्शन है hash.symbolize_keys


1
आप केवल रेल के साथ इसका उपयोग कर सकते हैं: api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys में डिफ़ॉल्ट हैश नहीं है: docs.ruby-lub.org/en/2.0.0/Hash। html
एडुआर्डो सैन्टाना

1
केवल समाधान रेल
rodvlopes

7

नेस्टेड हैश के लिए यह मेरा एक लाइनर है

def symbolize_keys(hash)
  hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end

FYI करें, केवल रेल के लिए काम करता है। किस मामले में, HashWithIndifferentAccess एक बेहतर विकल्प हो सकता है।
एडम ग्रांट

6

मामले में कारण आप ऐसा करने की जरूरत है क्योंकि आपके डेटा को मूल रूप JSON से आया है, तो आप सिर्फ में पास करके इस पार्स के किसी भी छोड़ सकता है :symbolize_namesJSON अंतर्ग्रहण पर विकल्प।

रूबी की आवश्यकता नहीं है और रूबी> 1.9 के साथ काम करता है

JSON.parse(my_json, :symbolize_names => true)

4

आप आलसी हो सकते हैं, और इसे इसमें लपेट सकते हैं lambda:

my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }

my_lamb[:a] == my_hash['a'] #=> true

लेकिन यह केवल हैश से पढ़ने के लिए काम करेगा - लेखन नहीं।

ऐसा करने के लिए, आप उपयोग कर सकते हैं Hash#merge

my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))

इनिट ब्लॉक मांग पर एक बार चाबियाँ बदल देगा, हालांकि यदि आप प्रतीक संस्करण तक पहुंचने के बाद कुंजी के स्ट्रिंग संस्करण के लिए मान अपडेट करते हैं, तो प्रतीक संस्करण अपडेट नहीं किया जाएगा।

irb> x = { 'a' => 1, 'b' => 2 }
#=> {"a"=>1, "b"=>2}
irb> y = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(x)
#=> {"a"=>1, "b"=>2}
irb> y[:a]  # the key :a doesn't exist for y, so the init block is called
#=> 1
irb> y
#=> {"a"=>1, :a=>1, "b"=>2}
irb> y[:a]  # the key :a now exists for y, so the init block is isn't called
#=> 1
irb> y['a'] = 3
#=> 3
irb> y
#=> {"a"=>3, :a=>1, "b"=>2}

आपके पास init ब्लॉक भी हैश को अपडेट नहीं कर सकता है, जो आपको उस तरह की त्रुटि से बचाएगा, लेकिन आप अभी भी इसके विपरीत होंगे - प्रतीक संस्करण को अपडेट करने से स्ट्रिंग संस्करण अपडेट नहीं होगा:

irb> q = { 'c' => 4, 'd' => 5 }
#=> {"c"=>4, "d"=>5}
irb> r = Hash.new { |h,k| h[k.to_s] }.merge(q)
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called
#=> 4
irb> r
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called again, since this key still isn't in r
#=> 4
irb> r[:c] = 7
#=> 7
irb> r
#=> {:c=>7, "c"=>4, "d"=>5}

इसलिए इनसे सावधान रहने की बात दो प्रमुख रूपों के बीच बदल रही है। एक के साथ रहना।


3

निम्नलिखित कार्य के लिए कुछ करना चाहेंगे?

new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }

यह हैश की नकल करेगा, लेकिन आप उस समय के बारे में परवाह नहीं करेंगे। संभवत: सभी डेटा को कॉपी किए बिना ऐसा करने का एक तरीका है।




2

यह उन लोगों के लिए है जो उपयोग mrubyकरते हैं और symbolize_keysपरिभाषित कोई विधि नहीं है :

class Hash
  def symbolize_keys!
    self.keys.each do |k|
      if self[k].is_a? Hash
        self[k].symbolize_keys!
      end
      if k.is_a? String
        raise RuntimeError, "Symbolizing key '#{k}' means overwrite some data (key :#{k} exists)" if self[k.to_sym]
        self[k.to_sym] = self[k]
        self.delete(k)
      end
    end
    return self
  end
end

प्रक्रिया:

  • केवल कुंजियों का प्रतीक है जो हैं String
  • अगर एक स्ट्रिंग का प्रतीक है, तो कुछ informations खोना (हैश का ओवरराइट हिस्सा) एक उठाना RuntimeError
  • प्रतीकात्मक रूप से समाहित हैश का भी प्रतीक है
  • प्रतीक्षित हैश लौटें
  • जगह में काम करता है!

1
आपके विधि में कोई गलती है, तो आप भूल !में symbolize_keys। अन्यथा ठीक काम करता है।
का

1

जिस एरे को हम बदलना चाहते हैं।

तार = ["HTML", "सीएसएस", "जावास्क्रिप्ट", "पायथन", "रूबी"]

एक खाली सरणी के रूप में एक नया चर बनाएं ताकि हम प्रतीकों को ".push" कर सकें।

प्रतीक = []

यहां हम एक ब्लॉक के साथ एक विधि परिभाषित करते हैं।

strings.each {| x | symbols.push (x.intern)}

कोड का अंत।

तो यह शायद रूबी में अपने सरणी (ओं) में प्रतीकों को बदलने के लिए सबसे सरल तरीका है। तार की एक सरणी बनाएं फिर एक नया चर बनाएं और चर को एक खाली सरणी में सेट करें। फिर आपके द्वारा बनाए गए पहले सरणी में ".each" विधि के साथ प्रत्येक तत्व का चयन करें। फिर अपने नए एरे में सभी तत्वों के ".push" के लिए एक ब्लॉक कोड का उपयोग करें और सभी तत्वों को प्रतीकों में बदलने के लिए ".intern or .to_sym" का उपयोग करें।

प्रतीक तेज़ होते हैं क्योंकि वे आपके कोड के भीतर अधिक मेमोरी सेव करते हैं और आप केवल एक बार उनका उपयोग कर सकते हैं। हैश में कुंजियों के लिए प्रतीकों का सबसे अधिक उपयोग किया जाता है जो बहुत अच्छा है। मैं सबसे अच्छा रूबी प्रोग्रामर नहीं हूँ, लेकिन कोड के इस रूप ने मुझे बहुत मदद की है। अगर किसी को एक बेहतर तरीका पता है तो कृपया साझा करें और आप इस विधि का उपयोग हैश के लिए भी कर सकते हैं!


2
सवाल हैश के बारे में था, न कि सरणियों के बारे में।
रिचर्ड_ जी 12

1

यदि आप वेनिला रूबी समाधान चाहते हैं और जैसा कि मेरे पास ActiveSupportयहां तक पहुंच नहीं है, तो यह गहरे प्रतीक समाधान है (पिछले वाले के समान)

    def deep_convert(element)
      return element.collect { |e| deep_convert(e) } if element.is_a?(Array)
      return element.inject({}) { |sh,(k,v)| sh[k.to_sym] = deep_convert(v); sh } if element.is_a?(Hash)
      element
    end

1

साइक 3.0 पर शुरू होने पर आप प्रतीक_न : विकल्प जोड़ सकते हैं

Psych.load("---\n foo: bar") # => {"foo"=>"bar"}

Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}

नोट: यदि आपके पास 3.0 से कम साइक संस्करण है, तो symbolize_names:चुपचाप नजरअंदाज कर दिया जाएगा।

मेरे उबंटू 18.04 में इसे माणिक 2.5.1p57 के साथ बॉक्स से बाहर किया गया है


0
ruby-1.9.2-p180 :001 > h = {'aaa' => 1, 'bbb' => 2}
 => {"aaa"=>1, "bbb"=>2} 
ruby-1.9.2-p180 :002 > Hash[h.map{|a| [a.first.to_sym, a.last]}]
 => {:aaa=>1, :bbb=>2}

आप इसे aऔर भी अधिक जटिल बनाने के लिए ब्लॉक तर्क को विघटित करने के लिए कोष्ठक में लपेट सकते हैं । उदाहरण के लिए मेरा उत्तर देखें।
माइकल बार्टन

0

यह वास्तव में वन-लाइनर नहीं है, लेकिन यह सभी स्ट्रिंग कुंजियों को प्रतीकों में बदल देता है, नेस्टेड वाले भी:

def recursive_symbolize_keys(my_hash)
  case my_hash
  when Hash
    Hash[
      my_hash.map do |key, value|
        [ key.respond_to?(:to_sym) ? key.to_sym : key, recursive_symbolize_keys(value) ]
      end
    ]
  when Enumerable
    my_hash.map { |value| recursive_symbolize_keys(value) }
  else
    my_hash
  end
end

0

मुझे यह एक-लाइनर पसंद है, जब मैं रेल का उपयोग नहीं कर रहा हूं, क्योंकि तब मुझे दूसरा हैश नहीं करना है और जब मैं इसे संसाधित कर रहा हूं, तो डेटा के दो सेट रखें:

my_hash = { "a" => 1, "b" => "string", "c" => true }

my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key) }

my_hash
=> {:a=>1, :b=>"string", :c=>true}

हटाएं # हटाएं हटाए गए कुंजी का मान लौटाता है


0

विशेष रूप से 'हैश # डीप_रेकी' भी एक अच्छा विकल्प है:

  • यदि आपको अपनी परियोजना में अन्य चीनी के लिए उपयोग मिल जाए,
  • यदि आप क्रिप्टिकल वन-लाइनर्स पर कोड पठनीयता पसंद करते हैं।

नमूना:

require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey

0

रूबी में मैं सबसे सरल और आसान समझने के लिए हैश में स्ट्रिंग कुंजियों को प्रतीकों में बदलना चाहता हूं:

my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}

हैश में प्रत्येक कुंजी के लिए हम उस पर डिलीट कहते हैं जो उसे हैश से हटाती है (डिलीट की गई कुंजी के साथ जुड़े मूल्य को भी हटाती है) और हमने तुरंत इसे सिंबल की के बराबर सेट कर दिया।


0

पिछले समाधानों के समान लेकिन थोड़ा अलग लिखा गया।

  • यह हैश के लिए अनुमति देता है जो नेस्टेड है और / या सरणियाँ है।
  • एक बोनस के रूप में एक स्ट्रिंग के लिए कुंजी का रूपांतरण प्राप्त करें।
  • कोड हैश को म्यूट नहीं करता है।

    module HashUtils
      def symbolize_keys(hash)
        transformer_function = ->(key) { key.to_sym }
        transform_keys(hash, transformer_function)
      end
    
      def stringify_keys(hash)
        transformer_function = ->(key) { key.to_s }
        transform_keys(hash, transformer_function)
      end
    
      def transform_keys(obj, transformer_function)
        case obj
        when Array
          obj.map{|value| transform_keys(value, transformer_function)}
        when Hash
          obj.each_with_object({}) do |(key, value), hash|
            hash[transformer_function.call(key)] = transform_keys(value, transformer_function)
          end
        else
          obj
        end
      end
    end
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.