해시에서 문자열을 기호로 변환하는 가장 좋은 방법


250

해시의 모든 키를 루비의 문자열에서 기호로 변환하는 가장 빠른 방법은 무엇입니까?

YAML을 구문 분석 할 때 유용합니다.

my_hash = YAML.load_file('yml')

사용할 수 있기를 원합니다 :

my_hash[:key] 

오히려 :

my_hash['key']


80
hash.symbolize_keys그리고 hash.deep_symbolize_keys당신이 레일을 사용하는 경우 작업을한다.
Zaz

조쉬 당신이 당신의 의견을 답에 넣었다면, 나는 당신을 투표했을 것입니다. 'rails'가 필요합니다 .hash.deep_symbolize_keys는 irb 또는 pry에서 잘 작동합니다. : D
Douglas G. Allen

답변:


239

에서 루비> = 2.5 ( 문서 ) 당신은 사용할 수 있습니다 :

my_hash.transform_keys(&:to_sym)

이전 Ruby 버전을 사용하고 있습니까? 다음은 키를 상징으로 해시를 새로운 것으로 복사하는 하나의 라이너입니다.

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 ({}) {| memo, (k, v) | 메모 [k.to_sym] = v; memo}
Sarah Mei

4
바로 내가 찾던 것입니다. 중첩 된 해시에 기호를 만들기 위해 약간 수정하고 몇 줄을 추가했습니다. 관심이 있으시면
Matt

37
Ruby 1.9에서는 다음 each_with_object과 같이 사용할 수 있습니다 .my_hash.each_with_object({}){|(k,v), h| h[k.to_sym] = v}
sgtFloyd 23.41의

8
이것은 재귀 해시를 처리하지 않습니다 ... 일회성이지만 DRY는 아닙니다.
baash05

8
@BryanM. 나는이 토론에 매우 늦게왔다 :-) 그러나 당신은 또한 마지막 .tap에 통과 할 필요성을 제거하는 방법을 사용할 수 있습니다 memo. 모든 솔루션 (재귀 적 솔루션)의 정리 된 버전을 만들었습니다. gist.github.com/Integralist/9503099
Integralist

307

Rails를 사용하는 경우 더 나은 방법이 있습니다.

매개 변수. symbolize_keys

끝.

그렇지 않은 경우 코드를 추출하십시오 (링크에도 있음).

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


45
중첩 해시를 상징하지 않습니다.
oma

3
링크를 symbolize_keys새로운 URL (Rails 3) 로 전환했습니다 . 원래의 URL을 방금 수정 to_options했지만 해당 링크에 문서가 없습니다. symbolize_keys실제로 설명이 있으므로 대신 사용했습니다.
Craig Walker

23
deep_symbolize_keys! . 레일 2+에서 작동
mastaBlasta

14
그 반대 방법을 궁금해하는 사람들을 위해 hash.stringify_keys작동합니다.
Nick

112

Ruby에서 특정 YAML의 경우, 키가 ' :'로 시작 하면 자동으로 기호로 삽입됩니다.

'yaml'이 필요합니다
'pp'필요
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를 넣습니다.
pp yaml_sym = YAML.load (yaml_sym)
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"}]}
끈
{: 연결 수 =>
  [{: port => 10000, : host => "host1.example.com"},
   {: port => 20000, : host => "host2.example.com"}]}
상징

15
단! YAML#load_file모든 키를 콜론으로 시작하지 않고 문자열 대신 모든 키를 기호로 기본 설정하는 방법이 있습니까?
ma11hew28

63

더 간결한 :

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

6
이것은 명백한 선택처럼 보입니다.
케이시 왓슨

12
중첩 해시를 상징하지 않습니다.
rgtk

15
더 읽기 쉬운 버전 :my_hash.map { |k, v| [k.to_sym, v] }.to_h
Dennis

60

Rails를 사용하는 경우 훨씬 간단합니다. HashWithIndifferentAccess를 사용하고 문자열 및 기호로 키에 액세스 할 수 있습니다.

my_hash.with_indifferent_access 

또한보십시오:

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


또는 Ruby Core 및 Standard Library 클래스에 대한 많은 확장이 포함 된 멋진 "Facets of Ruby"Gem을 사용할 수 있습니다.

  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
실제로는 그 반대입니다. symbol에서 문자열로 변환합니다. 기호로 변환하려면 my_hash.symbolize_keys
Espen

#symbolize_keys는 일반 Ruby / irb가 아닌 Rails에서만 작동합니다. #symbolize_keys는 깊이 중첩 된 해시에서 작동하지 않습니다.
Tilo

54

때문에 Ruby 2.5.0당신이 사용할 수있는 Hash#transform_keysHash#transform_keys!.

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

또한 transform_values apidock.com/rails/Hash/transform_values 와 함께 작동합니다 . 즉이 않기 때문에 함께이 같은 값을 수정하기위한 동등한 것으로 보인다 정말 좋다 stringify_keys또는 symbolize_keys.
Mike Vallano

키를 깊이 상징하는 방법이
있습니까

이것은 2018 년 이후에 선택된 솔루션이되어야합니다.
Ivan Wang


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

좋은 하나, 나는 그것을 deep_symbolize :) 이름을 바꾸더라도 이것과 함께 갈 것입니다 :
PierrOz

20

나는 매쉬 보석을 정말 좋아합니다 .

당신이 할 수있는 mash['key'], 또는 mash[:key], 또는mash.key


2
그것은 매우 멋진 보석입니다! 해시 작업을 매우 편안하게 만듭니다. 감사합니다!
asaaki

매우 아름답습니다. 이 프로젝트에 대한 새로운 개발 Hashie (에서 계속되고있다 github.com/intridea/hashie )하지만 여전히 거의 같은 방식으로 작동합니다 github.com/intridea/hashie#keyconversion
jpalmieri

19

json을 사용하고 있고 해시로 사용하려는 경우 핵심 Ruby에서 할 수 있습니다.

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

symbolize_names : true로 설정하면 JSON 객체의 이름 (키)에 대한 기호를 반환합니다. 그렇지 않으면 문자열이 반환됩니다. 문자열이 기본값입니다.

문서 : Json # parse symbolize_names


symbol_hash = JSON.parse(JSON.generate(YAML.safe_load(FILENAME)), symbolize_names: true)YAML 파일에서 오는 경우 중첩 키를 기호로 사용하여 해시를 빠르게 얻는 매우 건조한 방법이지만 비효율적입니다.
Cameron Gagnon

12

params.symbolize_keys작동합니다. 이 메소드는 해시 키를 기호로 바꾸고 새 해시를 리턴합니다.


26
이 방법은 핵심 루비가 아닙니다. Rails 방식입니다.
Ricardo Acras

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

Rails에서는 다음을 사용할 수 있습니다.

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

로 변환 :

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

3
deep_symbolize_keysRails의 Hash 확장에 추가 되었지만 Ruby 코어의 일부가 아닙니다.
Ryan Crispin Heneise


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

참고로, Rails에서만 작동합니다. 이 경우 HashWithIndifferentAccess가 더 나은 대안 일 수 있습니다.
Adam Grant

6

경우에 따라서 는이 작업을 수행 할 필요가 데이터가 원래 JSON에서 왔기 때문에, 당신은 단지 전달하여이 구문 분석 중 하나를 건너 뛸 수 있습니다 :symbolize_namesJSON 섭취에 따라 옵션을 선택합니다.

레일이 필요 없으며 Ruby> 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'))

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}

따라서 이것들에주의해야 할 것은 두 가지 주요 형태 사이를 전환하는 것입니다. 하나를 고수하십시오.


3

다음과 같은 것이 효과가 있습니까?

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

해시를 복사하지만 대부분은 신경 쓰지 않습니다. 모든 데이터를 복사하지 않고 할 수있는 방법이있을 것입니다.


3

짧은 한 줄짜리 술 :

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

2

이건 어때요:

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

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

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
  • 문자열을 상징하는 것이 일부 정보를 잃는 것을 의미한다면 (해시의 일부를 덮어 씁니다) RuntimeError
  • 재귀 적으로 포함 된 해시도 상징
  • 상징화 된 해시를 돌려줍니다
  • 그 자리에서 작동합니다!

1
당신의 방법에 오타가있어, 당신은 잊었 !에를 symbolize_keys. 그렇지 않으면 잘 작동합니다.
Ka Mok

1

변경하려는 배열입니다.

문자열 = [ "HTML", "CSS", "JavaScript", "Python", "Ruby"]

새 변수를 빈 배열로 만들어 심볼을 ".push"할 수 있도록합니다.

기호 = []

여기서 블록으로 메소드를 정의합니다.

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

코드 끝

따라서 이것은 아마도 Ruby의 배열에서 문자열을 기호로 변환하는 가장 간단한 방법 일 것입니다. 문자열 배열을 만든 다음 새 변수를 만들고 변수를 빈 배열로 설정하십시오. 그런 다음 ".each"메서드로 만든 첫 번째 배열에서 각 요소를 선택하십시오. 그런 다음 블록 코드를 사용하여 새 배열의 모든 요소를 ​​".push"하고 ".intern 또는 .to_sym"을 사용하여 모든 요소를 ​​기호로 변환하십시오.

기호는 코드 내에서 더 많은 메모리를 절약하고 한 번만 사용할 수 있기 때문에 더 빠릅니다. 기호는 해시의 키에 가장 일반적으로 사용됩니다. 나는 최고의 루비 프로그래머는 아니지만이 코드 형식은 많은 도움이되었습니다. 누군가 더 나은 방법을 알고 있다면 공유 하고이 방법을 해시에도 사용할 수 있습니다!


2
문제는 배열이 아닌 해시에 관한 것이 었습니다.
Richard_G

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

Psych 3.0 부터 symbolize_names : 옵션을 추가 할 수 있습니다

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

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

참고 : Psych 버전이 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이 훨씬 더 간결을 할 수있는 블록 인수를 분해 괄호. 예를 들어 내 대답을 참조하십시오.
Michael Barton

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

나는 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}

해시 #delete는 삭제 된 키의 값을 반환합니다.


0

패싯의 해시 #deep_rekey 도 특히 좋은 옵션입니다.

  • 프로젝트의 패싯에서 다른 설탕을 사용하는 경우,
  • 암호 한 줄짜리보다 코드 가독성을 선호하는 경우.

견본:

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)}

해시의 각 키에 대해 해시에서 키를 제거하는 delete를 호출하고 delete 는 삭제 된 키와 관련된 값을 반환합니다. 이를 즉시 심볼 화 된 키와 동일하게 설정합니다.


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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.