해시 값을 변경하는 방법은 무엇입니까?


126

나는 각을 교체하고 싶습니다 value과 함께 해시 value.some_method.

예를 들어 간단한 해시가 주어진 경우 :

{"a" => "b", "c" => "d"}` 

모든 값은 .upcased 여야 하므로 다음과 같습니다.

{"a" => "B", "c" => "D"}

나는 시도 #collect하고 #map있지만, 항상 그냥 배열을 얻는다. 이를 수행하는 우아한 방법이 있습니까?

최신 정보

젠장, 잊어 버렸습니다. 해시가 변경되어서는 안되는 인스턴스 변수에 있습니다. 변경된 값이있는 새 해시가 필요하지만 해당 변수를 명시 적으로 정의한 다음이를 채우는 해시를 반복하는 것을 선호하지 않습니다. 다음과 같은 것 :

new_hash = hash.magic{ ... }

내 대답의 두 번째 예는 새 해시를 반환합니다. #inject가 수행하는 마법에 대해 자세히 설명하고 싶다면 댓글을 달아주세요.
kch

답변:


218
my_hash.each { |k, v| my_hash[k] = v.upcase } 

또는 비파괴 적으로 수행하고 싶은 경우 수정하는 대신 새 해시를 반환합니다 my_hash.

a_new_hash = my_hash.inject({}) { |h, (k, v)| h[k] = v.upcase; h } 

이 마지막 버전에는 키도 변환 할 수있는 추가 이점이 있습니다.


26
첫 번째 제안은 적절하지 않습니다. 반복하는 동안 열거 가능한 요소를 수정하면 정의되지 않은 동작이 발생합니다. 이것은 다른 언어에서도 유효합니다. 다음은 Matz의 답변입니다 (배열을 사용하여 예제에 답변했지만 일반적인 답변입니다). j.mp/tPOh2U
Marcus

4
@Marcus 나는 일반적으로 그러한 수정을 피해야한다는 데 동의합니다. 그러나이 경우 수정이 향후 반복을 변경하지 않기 때문에 안전 할 수 있습니다. 이제 다른 키 (블록에 전달 된 키가 아님)가 할당되면 문제가 발생합니다.
Kelvin

36
@Adam @kch each_with_objectinject해시로 블록을 끝내고 싶지 않을 때보다 편리한 버전입니다 .my_hash.each_with_object({}) {|(k, v), h| h[k] = v.upcase }
Kelvin

6
이 해시에 쌍의 목록을 변환하는 매우 깔끔한 구문은 또한, 그래서 당신은 쓸 수 있습니다a_new_hash = Hash[ my_hash.map { |k, v| [k, v.upcase] } ]

2
Ruby 2를 사용하면 다음과 같이 쓸 수 있습니다.to_h
roman-roman


35

값을 수집하고이를 Array에서 Hash로 다시 변환 할 수 있습니다.

이렇게 :

config = Hash[ config.collect {|k,v| [k, v.upcase] } ]

23

이렇게하면됩니다.

my_hash.each_with_object({}) { |(key, value), hash| hash[key] = value.upcase }

inject장점과는 반대로 블록 내에서 해시를 다시 반환 할 필요가 없다는 것입니다.


이 방법은 원래 해시를 수정하지 않는 것이 좋습니다.
Christian Di Lorenzo

10

이 기능을 시도하십시오 :

h = {"a" => "b", "c" => "d"}
h.each{|i,j| j.upcase!} # now contains {"a" => "B", "c" => "D"}.

5
그것은 좋지만 j가 그 자체로 작용하는 파괴적인 방법을 가지고있을 때만 작동한다는 점에 유의해야합니다. 따라서 일반적인 value.some_method 케이스를 처리 할 수 ​​없습니다.
kch

좋은 점입니다. 그의 업데이트를 통해 두 번째 솔루션 (비파괴)이 최고입니다.
Chris Doggett

10

ActiveSupport v4.2.0 에는 방법이 있습니다 . 호출 transform_values되고 기본적으로 각 키-값 쌍에 대해 블록을 실행합니다.

그들은 그것을 사용하고 있기 때문에 each반복하는 것보다 더 좋은 방법은 없다고 생각합니다.

hash = {sample: 'gach'}

result = {}
hash.each do |key, value|
  result[key] = do_stuff(value)
end

최신 정보:

Ruby 2.4.0부터 기본적으로 #transform_values#transform_values!.


2

한 단계 더 나아가 중첩 된 해시에서이 작업을 수행 할 수 있습니다. 확실히 이것은 Rails 프로젝트에서 상당한 양의 일이 발생합니다.

다음은 params 해시가 UTF-8인지 확인하는 몇 가지 코드입니다.

  def convert_hash hash
    hash.inject({}) do |h,(k,v)|
      if v.kind_of? String
        h[k] = to_utf8(v) 
      else
        h[k] = convert_hash(v)
      end
      h
    end      
  end    

  # Iconv UTF-8 helper
  # Converts strings into valid UTF-8
  #
  # @param [String] untrusted_string the string to convert to UTF-8
  # @return [String] your string in UTF-8
  def to_utf8 untrusted_string=""
    ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
    ic.iconv(untrusted_string + ' ')[0..-2]
  end  

1

나는 다음과 같이한다.

new_hash = 해시 [* original_hash.collect {| key, value | [키, 값 + 1]}. flatten]

이는 모든 표현식을 통해 키 또는 값을 변환 할 수있는 기능을 제공합니다 (물론 비파괴 적입니다).


1

Ruby에는 이와 같은 작업에 매우 유용한 tap메서드 ( 1.8.7 , 1.9.32.1.0 )가 있습니다.

original_hash = { :a => 'a', :b => 'b' }
original_hash.clone.tap{ |h| h.each{ |k,v| h[k] = v.upcase } }
# => {:a=>"A", :b=>"B"}
original_hash # => {:a=>"a", :b=>"b"}

1

레일 별

누군가 to_s가 각 값에 대해 메소드 를 호출해야하고 Rails 4.2 ( transform_values메소드 링크 포함 )를 사용하지 않는 경우 다음을 수행 할 수 있습니다.

original_hash = { :a => 'a', :b => BigDecimal('23.4') }
#=> {:a=>"a", :b=>#<BigDecimal:5c03a00,'0.234E2',18(18)>}
JSON(original_hash.to_json)
#=> {"a"=>"a", "b"=>"23.4"}

참고 : 'json'라이브러리를 사용해야합니다.

참고 2 : 키도 문자열로 바뀝니다.


1

값이 문자열이라는 것을 알고 있는 경우주기 중에 해당 값에 대해 replace 메서드를 호출 할 수 있습니다 . 이렇게하면 값이 변경됩니다.

Altohugh 이것은 값이 문자열이기 때문에 질문에 완전히 대답하지 않는 경우로 제한되며 누군가에게 유용 할 수 있다고 생각했습니다.


1
new_hash = old_hash.merge(old_hash) do |_key, value, _value|
             value.upcase
           end

# old_hash = {"a" => "b", "c" => "d"}
# new_hash = {"a" => "B", "c" => "D"}

이것이 효율적입니까?
ioquatix jul.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.