أفضل طريقة لتحويل السلاسل إلى رموز في التجزئة

StackOverflow https://stackoverflow.com/questions/800122

  •  03-07-2019
  •  | 
  •  

سؤال

ما هي الطريقة (الأسرع/الأنظف/المباشرة) لتحويل جميع المفاتيح في التجزئة من السلاسل إلى الرموز في روبي؟

سيكون هذا مفيدًا عند تحليل YAML.

my_hash = YAML.load_file('yml')

أود أن أكون قادرًا على استخدام:

my_hash[:key] 

بدلا من:

my_hash['key']
هل كانت مفيدة؟

المحلول

إذا كنت تريد بطانة واحدة،

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

سيتم نسخ التجزئة إلى واحدة جديدة مع المفاتيح المرمزة.

نصائح أخرى

إليك طريقة أفضل، إذا كنت تستخدم Rails:

المعلمات.رمز_المفاتيح

النهاية.

إذا لم تكن كذلك، فما عليك سوى نسخ الكود الخاص بهم (وهو موجود أيضًا في الرابط):

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

بالنسبة لحالة YAML المحددة في Ruby، إذا كانت المفاتيح تبدأ بـ ":"، سيتم احتجازهم تلقائيًا كرموز.

require 'yaml'
require 'pp'
yaml_str = "
connections:
  - host: host1.example.com
    port: 10000
  - host: host2.example.com
    port: 20000
"
yaml_sym = "
:connections:
  - :host: host1.example.com
    :port: 10000
  - :host: host2.example.com
    :port: 20000
"
pp yaml_str = YAML.load(yaml_str)
puts yaml_str.keys.first.class
pp yaml_sym = YAML.load(yaml_sym)
puts yaml_sym.keys.first.class

انتاج:

#  /opt/ruby-1.8.6-p287/bin/ruby ~/test.rb
{"connections"=>
  [{"port"=>10000, "host"=>"host1.example.com"},
   {"port"=>20000, "host"=>"host2.example.com"}]}
String
{:connections=>
  [{:port=>10000, :host=>"host1.example.com"},
   {:port=>20000, :host=>"host2.example.com"}]}
Symbol

بل وأكثر إيجازًا:

Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]

إذا كنت تستخدم Rails، فالأمر أبسط بكثير - يمكنك استخدام HashWithIndifferentAccess والوصول إلى المفاتيح كسلسلة وكرموز:

my_hash.with_indifferent_access 

أنظر أيضا:

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


أو يمكنك استخدام جوهرة "Facets of Ruby" الرائعة، والتي تحتوي على الكثير من الامتدادات لفئات Ruby Core وStandard Library.

  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

منذ Ruby 2.5.0 يمكنك استخدام Hash#transform_keys أو Hash#transform_keys!.

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

http://api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys

hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }

إليك طريقة للترميز العميق لكائن ما

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

أنا حقا أحب الهريس جوهرة.

يمكنك ان تفعل mash['key'], ، أو mash[:key], ، أو mash.key

إذا كنت تستخدم json، وتريد استخدامه كتجزئة، فيمكنك القيام بذلك في Ruby الأساسي:

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

رمز_الأسماء:إذا تم التعيين على صواب، فسيتم إرجاع رموز للأسماء (المفاتيح) في كائن JSON.وإلا يتم إرجاع السلاسل.السلاسل هي الافتراضية.

الوثيقة: Json#parse يرمز للأسماء

params.symbolize_keys سوف تعمل أيضا.تعمل هذه الطريقة على تحويل مفاتيح التجزئة إلى رموز وإرجاع تجزئة جديدة.

تعديل علىإجابة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
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

يتحول إلى:

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

هناك الكثير من الإجابات هنا، ولكن وظيفة القضبان هي الطريقة الوحيدة hash.symbolize_keys

هذه هي الخطوط الملاحية المنتظمة الخاصة بي للتجزئة المتداخلة

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

في حالة سبب ما عليك القيام به هو أن بياناتك جاءت في الأصل من JSON، ويمكنك تخطي أي من هذا التحليل بمجرد تمرير :symbolize_names الخيار عند تناول JSON.

لا توجد حاجة إلى Rails وتعمل مع Ruby >1.9

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

يمكن أن تكون كسولًا، وتلفه في ملف 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'))

ستقوم كتلة init بتحويل المفاتيح مرة واحدة عند الطلب، على الرغم من أنه إذا قمت بتحديث قيمة إصدار السلسلة من المفتاح بعد الوصول إلى إصدار الرمز، فلن يتم تحديث إصدار الرمز.

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}

لذا فإن الشيء الذي يجب توخي الحذر بشأنه هو التبديل بين النموذجين الرئيسيين.العصا مع واحد.

هل هناك شيء مثل العمل التالي؟

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

سيقوم بنسخ التجزئة، لكنك لن تهتم بذلك في معظم الأوقات.ربما تكون هناك طريقة للقيام بذلك دون نسخ جميع البيانات.

خط واحد أقصر:

my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }

وماذا عن هذا:

my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))

# my_hash['key'] => "val"
# my_hash[:key]  => "val"

هذا بالنسبة للأشخاص الذين يستخدمون 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
  • إذا كان رمز سلسلة يعني فقدان بعض المعلومات (الكتابة فوق جزء من التجزئة) قم برفع أ RuntimeError
  • يرمز أيضًا إلى التجزئة الواردة بشكل متكرر
  • إرجاع التجزئة الرمزية
  • يعمل في مكانه!

المصفوفة التي نريد تغييرها.

سلاسل = ["HTML"، "CSS"، "JavaScript"، "Python"، "Ruby"]

قم بإنشاء متغير جديد كمصفوفة فارغة حتى نتمكن من ".دفع" الرموز إلى الداخل.

الرموز = [ ]

هنا نحدد طريقة مع كتلة.

سلاسل. Symbors.push (x.intern)}

نهاية الكود.

ربما تكون هذه هي الطريقة الأكثر مباشرة لتحويل السلاسل إلى رموز في المصفوفة (المصفوفات) الخاصة بك في روبي.أنشئ مصفوفة من السلاسل ثم أنشئ متغيرًا جديدًا واضبط المتغير على مصفوفة فارغة.ثم حدد كل عنصر في المصفوفة الأولى التي قمت بإنشائها باستخدام الطريقة ".each".ثم استخدم رمز الكتلة ".push" لجميع العناصر في المصفوفة الجديدة واستخدم ".intern أو .to_sym" لتحويل جميع العناصر إلى رموز.

تكون الرموز أسرع لأنها توفر المزيد من الذاكرة داخل التعليمات البرمجية الخاصة بك ويمكنك استخدامها مرة واحدة فقط.يتم استخدام الرموز بشكل شائع للمفاتيح في التجزئة وهو أمر رائع.أنا لست أفضل مبرمج روبي ولكن هذا النوع من التعليمات البرمجية ساعدني كثيرًا. إذا كان أي شخص يعرف طريقة أفضل، فيرجى مشاركتها ويمكنك استخدام هذه الطريقة للتجزئة أيضًا!

إذا كنت ترغب في حل روبي الفانيليا وأنا لا أستطيع الوصول إليه 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

ابتداء من نفسية 3.0 يمكنك إضافة أسماء_الرموز: خيار

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

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

ملحوظة:إذا كان لديك إصدار Psych أقل من 3.0 symbolize_names: سيتم تجاهلها بصمت.

يتضمن نظام Ubuntu 18.04 الخاص بي هذا المنتج خارج الصندوق مع Ruby 2.5.1p57

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}

هذا ليس سطرًا واحدًا تمامًا، ولكنه يحول جميع مفاتيح السلسلة إلى رموز، وكذلك الرموز المتداخلة:

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

تعجبني هذه الجملة، عندما لا أستخدم Rails، لأنني لن أضطر إلى إجراء تجزئة ثانية والاحتفاظ بمجموعتين من البيانات أثناء معالجتها:

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}

يقوم Hash#delete بإرجاع قيمة المفتاح المحذوف

تجزئة الأوجه#deep_rekey يعد أيضًا خيارًا جيدًا، خاصة:

  • إذا وجدت استخدامًا لسكر آخر من جوانب في مشروعك،
  • إذا كنت تفضل سهولة قراءة التعليمات البرمجية على الخطوط الفردية الغامضة.

عينة:

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

في روبي، أجد أن هذه هي الطريقة الأكثر بساطة وسهولة في الفهم لتحويل مفاتيح السلسلة في التجزئة إلى رموز:

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

لكل مفتاح في التجزئة نسميه حذفًا مما يؤدي إلى إزالته من التجزئة (حذف أيضًا إرجاع القيمة المرتبطة بالمفتاح تم حذفه) وقمنا على الفور بضبط هذا على أنه يساوي المفتاح المرمز.

مشابهة للحلول السابقة ولكنها مكتوبة بشكل مختلف قليلاً.

  • يسمح هذا بتجزئة متداخلة و/أو تحتوي على صفائف.
  • احصل على تحويل المفاتيح إلى سلسلة كمكافأة.
  • لا يؤدي الرمز إلى تغيير التجزئة التي تم تمريرها.

    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
    
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top