[ruby] Ruby에서 배열을 해시로 변환하는 가장 좋은 방법은 무엇입니까?

Ruby에서는 다음 형식 중 하나의 배열이 제공됩니다.

[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]

…이를 해시로 변환하는 가장 좋은 방법은 무엇입니까?

{apple => 1, banana => 2}



답변

참고 : 간결하고 효율적인 솔루션은 아래 Marc-André Lafortune의 답변을 참조하십시오.

이 답변은 원래 글을 쓰는 시점에서 가장 많이 찬성 된 flatten을 사용하는 접근 방식의 대안으로 제공되었습니다. 이 예제를 모범 사례 나 효율적인 접근 방식으로 제시 할 의도가 아님을 분명히해야했습니다. 원래 대답은 다음과 같습니다.


경고! 평면화 를 사용 하는 솔루션 은 배열 키 또는 값을 보존하지 않습니다!

@John Topley의 인기있는 답변을 바탕으로 시도해 보겠습니다.

a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]

이로 인해 오류가 발생합니다.

ArgumentError: odd number of arguments for Hash
        from (irb):10:in `[]'
        from (irb):10

생성자는 짝수 길이의 배열 (예 : [ ‘k1’, ‘v1,’k2 ‘,’v2 ‘])을 예상했습니다. 더 나쁜 것은 짝수 길이로 평평한 다른 배열이 잘못된 값을 가진 해시를 조용히 제공한다는 것입니다.

배열 키 또는 값을 사용하려면 map 을 사용할 수 있습니다 .

h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"

이렇게하면 배열 키가 유지됩니다.

h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}


답변

간단히 사용 Hash[*array_variable.flatten]

예를 들면 :

a1 = ['apple', 1, 'banana', 2]
h1 = Hash[*a1.flatten(1)]
puts "h1: #{h1.inspect}"

a2 = [['apple', 1], ['banana', 2]]
h2 = Hash[*a2.flatten(1)]
puts "h2: #{h2.inspect}"

사용 Array#flatten(1)하면 재귀가 제한되므로 Array키와 값이 예상대로 작동합니다.


답변

가장 좋은 방법은 Array#to_h .

[ [:apple,1],[:banana,2] ].to_h  #=> {apple: 1, banana: 2}

참고 to_h또한 블록을 허용합니다

[:apple, :banana].to_h { |fruit| [fruit, "I like #{fruit}s"] }
  # => {apple: "I like apples", banana: "I like bananas"}

참고 : to_hRuby 2.6.0 이상에서는 블록을 허용합니다. 초기 루비의 경우 내backports 보석을 하고require 'backports/2.6.0/enumerable/to_h'

to_h 블록없이 루비 2.1.0에서 도입되었습니다.

Ruby 2.1 이전에는 덜 읽기 쉬운 Hash[]다음을 사용할 수있었습니다 .

array = [ [:apple,1],[:banana,2] ]
Hash[ array ]  #= > {:apple => 1, :banana => 2}

마지막으로를 사용하는 솔루션에주의하십시오 flatten. 이것은 배열 자체 인 값에 문제를 일으킬 수 있습니다.


답변

최신 정보

Ruby 2.1.0이 오늘 출시되었습니다 . 그리고 나는 Array#to_h( 릴리스 노트ruby-doc ) 와 함께 제공되며 , 이는 an Array을 a 로 변환하는 문제를 해결합니다 .Hash .

Ruby 문서 예 :

[[:foo, :bar], [1, 2]].to_h    # => {:foo => :bar, 1 => 2}


답변

편집 : 내가 글을 쓰는 동안 게시 된 응답을 보았습니다. Hash [a.flatten]가 갈 길인 것 같습니다. 응답을 통해 생각할 때 문서에서 그 부분을 놓쳤을 것입니다. 필요한 경우 내가 작성한 솔루션을 대안으로 사용할 수 있다고 생각했습니다.

두 번째 형식은 더 간단합니다.

a = [[:apple, 1], [:banana, 2]]
h = a.inject({}) { |r, i| r[i.first] = i.last; r }

a = 배열, h = 해시, r = 반환 값 해시 (우리가 축적 한 것), i = 배열의 항목

첫 번째 형식을 수행하는 가장 좋은 방법은 다음과 같습니다.

a = [:apple, 1, :banana, 2]
h = {}
a.each_slice(2) { |i| h[i.first] = i.last }


답변

다음을 사용하여 2D 배열을 해시로 간단히 변환 할 수도 있습니다.

1.9.3p362 :005 > a= [[1,2],[3,4]]

 => [[1, 2], [3, 4]]

1.9.3p362 :006 > h = Hash[a]

 => {1=>2, 3=>4} 


답변

요약 및 요약 :

이 답변은 다른 답변의 정보에 대한 포괄적 인 요약이되기를 바랍니다.

질문의 데이터와 몇 가지 추가 사항을 고려할 때 매우 짧은 버전 :

flat_array   = [  apple, 1,   banana, 2  ] # count=4
nested_array = [ [apple, 1], [banana, 2] ] # count=2 of count=2 k,v arrays
incomplete_f = [  apple, 1,   banana     ] # count=3 - missing last value
incomplete_n = [ [apple, 1], [banana   ] ] # count=2 of either k or k,v arrays


# there's one option for flat_array:
h1  = Hash[*flat_array]                     # => {apple=>1, banana=>2}

# two options for nested_array:
h2a = nested_array.to_h # since ruby 2.1.0    => {apple=>1, banana=>2}
h2b = Hash[nested_array]                    # => {apple=>1, banana=>2}

# ok if *only* the last value is missing:
h3  = Hash[incomplete_f.each_slice(2).to_a] # => {apple=>1, banana=>nil}
# always ok for k without v in nested array:
h4  = Hash[incomplete_n] # or .to_h           => {apple=>1, banana=>nil}

# as one might expect:
h1 == h2a # => true
h1 == h2b # => true
h1 == h3  # => false
h3 == h4  # => true

토론 및 세부 사항은 다음과 같습니다.


설정 : 변수

우리가 사용할 데이터를 보여주기 위해 데이터에 대한 다양한 가능성을 나타내는 몇 가지 변수를 만들겠습니다. 다음 범주에 해당합니다.

질문에 직접 포함 된 내용을 기반으로 다음 a1a2같습니다.

(참고 : 나는 그것을 가정 applebanana변수를 나타내는 것을 의미하고 다른 사람이했던 것처럼, 나는 그 입력과 결과가 일치 할 수 있도록 여기에서 문자열을 사용됩니다.).

a1 = [  'apple', 1 ,  'banana', 2  ] # flat input
a2 = [ ['apple', 1], ['banana', 2] ] # key/value paired input

다중 값 키 및 / 또는 값 a3:

다른 답변에서 또 다른 가능성이 제시되었습니다 (여기에서 확장합니다). 키 및 / 또는 값은 자체적으로 배열 일 수 있습니다.

a3 = [ [ 'apple',                   1   ],
       [ 'banana',                  2   ],
       [ ['orange','seedless'],     3   ],
       [ 'pear',                 [4, 5] ],
     ]

불균형 어레이, a4 :

좋은 측정을 위해 불완전한 입력이있을 수있는 경우 하나를 추가 할 것이라고 생각했습니다.

a4 = [ [ 'apple',                   1],
       [ 'banana',                  2],
       [ ['orange','seedless'],     3],
       [ 'durian'                    ], # a spiky fruit pricks us: no value!
     ]

이제 작동합니다.

처음에는 평평한 배열로 시작하여 a1 .

일부는 #to_h(Ruby 2.1.0에 나타 났으며 이전 버전 으로 백 포트 할 수있는) 사용을 제안했습니다 . 초기 플랫 배열의 경우 작동하지 않습니다.

a1.to_h   # => TypeError: wrong element type String at 0 (expected array)

splat 연산자Hash::[] 와 함께 사용하면 다음이 수행됩니다.

Hash[*a1] # => {"apple"=>1, "banana"=>2}

이것이 다음으로 표현되는 간단한 사례에 대한 해결책입니다. a1 .

키 / 값 쌍 배열의 배열을 사용하면 a2 다음을 수행합니다.

배열로 [key,value] 형 배열, 이동하는 방법은 두 가지가 있습니다.

첫째, Hash::[]여전히 작동합니다 (에서와 같이 *a1).

Hash[a2] # => {"apple"=>1, "banana"=>2}

그리고 #to_h지금도 작동합니다.

a2.to_h  # => {"apple"=>1, "banana"=>2}

따라서 간단한 중첩 배열 사례에 대한 두 가지 쉬운 대답입니다.

이는 다음과 같이 하위 배열을 키 또는 값으로 사용하더라도 마찬가지입니다 a3.

Hash[a3] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]} 
a3.to_h  # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "pear"=>[4, 5]}

그러나 두리안에는 스파이크가 있습니다 (변칙적 인 구조로 인해 문제가 발생 함).

균형이 맞지 않는 입력 데이터가 있으면 다음과 #to_h같은 문제가 발생합니다 .

a4.to_h  # => ArgumentError: wrong array length at 3 (expected 2, was 1)

그러나 Hash::[]여전히 작동 nil합니다. durian(및 a4의 다른 모든 배열 요소 (단지 1- 값 배열))로 설정하면됩니다.

Hash[a4] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}

평면화-새 변수 사용 a5a6

인수의 flatten유무에 관계없이 몇 가지 다른 답변이 언급 1되었으므로 새로운 변수를 만들어 보겠습니다.

a5 = a4.flatten
# => ["apple", 1, "banana", 2,  "orange", "seedless" , 3, "durian"] 
a6 = a4.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian"] 

나는 a4우리가 가진 균형 문제 때문에 기본 데이터 로 사용하기 로 결정했습니다 a4.to_h. 나는 전화를flatten 는 누군가가 그것을 해결하기 위해 사용할 수있는 한 가지 접근 방식 일 수 있다고 생각합니다. 다음과 같이 보일 수 있습니다.

flatten인수 없음 ( a5) :

Hash[*a5]       # => {"apple"=>1, "banana"=>2, "orange"=>"seedless", 3=>"durian"}
# (This is the same as calling `Hash[*a4.flatten]`.)

순진한 눈에 작업이 나타납니다이 -하지만 이렇게도 만드는 씨없는 오렌지 잘못된 도보로 우리를 얻었다 값을 .3durian

그리고 이것은에서와 a1마찬가지로 작동하지 않습니다.

a5.to_h # => TypeError: wrong element type String at 0 (expected array)

그래서 a4.flatten우리에게는 유용하지 않습니다.Hash[a4]

flatten(1)경우 ( a6)

그러나 부분적으로 만 평평하게하는 것은 어떻습니까? 부분적으로 평면화 된 배열 ( ) 에서 Hash::[]using 을 호출 하는 것은 다음을 호출하는 것과 동일 하지 않다는 점에 주목할 가치가 있습니다 .splata6Hash[a4]

Hash[*a6] # => ArgumentError: odd number of arguments for Hash

미리 병합 된 배열, 여전히 중첩 됨 (다른 방법으로 a6 ) :

그러나 이것이 우리가 처음에 배열을 얻은 방법이라면 어떨까요? (즉,에 비해 a1입력 데이터였습니다. 이번에는 일부 데이터가 배열이나 다른 객체가 될 수 있습니다.) Hash[*a6]작동하지 않는 것을 보았습니다. 하지만 여전히 작동하지 않는 동작을 얻으려면 어떻게해야합니까? 마지막 요소 (중요! 아래 참조)는nil 값 했습니까?

이러한 상황에서도 Enumerable#each_slice키 / 값 을 외부 배열의 요소로 되 돌리는 데 사용하는 방법이 여전히 있습니다 .

a7 = a6.each_slice(2).to_a
# => [["apple", 1], ["banana", 2], [["orange", "seedless"], 3], ["durian"]] 

이것은 ” 동일a4하지 않지만 동일한 값을 갖는 새로운 배열을 얻게 됩니다 .

a4.equal?(a7) # => false
a4 == a7      # => true

따라서 다시 사용할 수 있습니다 Hash::[].

Hash[a7] # => {"apple"=>1, "banana"=>2, ["orange", "seedless"]=>3, "durian"=>nil}
# or Hash[a6.each_slice(2).to_a]

하지만 문제가 있습니다!

each_slice(2)솔루션은 마지막 키가 값이 누락 된 경우에만 모든 것을 정상 상태로 되 돌린다는 점에 유의하는 것이 중요 합니다. 나중에 추가 키 / 값 쌍을 추가 한 경우 :

a4_plus = a4.dup # just to have a new-but-related variable name
a4_plus.push(['lychee', 4])
# => [["apple",                1],
#     ["banana",               2],
#     [["orange", "seedless"], 3], # multi-value key
#     ["durian"],              # missing value
#     ["lychee", 4]]           # new well-formed item

a6_plus = a4_plus.flatten(1)
# => ["apple", 1, "banana", 2, ["orange", "seedless"], 3, "durian", "lychee", 4]

a7_plus = a6_plus.each_slice(2).to_a
# => [["apple",                1],
#     ["banana",               2],
#     [["orange", "seedless"], 3], # so far so good
#     ["durian",               "lychee"], # oops! key became value!
#     [4]]                     # and we still have a key without a value

a4_plus == a7_plus # => false, unlike a4 == a7

그리고 여기서 얻을 수있는 두 개의 해시는 중요한면에서 다릅니다.

ap Hash[a4_plus] # prints:
{
                     "apple" => 1,
                    "banana" => 2,
    [ "orange", "seedless" ] => 3,
                    "durian" => nil, # correct
                    "lychee" => 4    # correct
}

ap Hash[a7_plus] # prints:
{
                     "apple" => 1,
                    "banana" => 2,
    [ "orange", "seedless" ] => 3,
                    "durian" => "lychee", # incorrect
                           4 => nil       # incorrect
}

(참고 : 여기서 구조를 더 쉽게 표시하기 위해 awesome_print‘s ap를 사용하고 있습니다. 이에 대한 개념적 요구 사항은 없습니다.)

따라서 each_slice불평형 플랫 입력에 대한 솔루션은 불평형 비트가 맨 끝에있는 경우에만 작동합니다.


요약 :

  1. 가능하면 이러한 것에 대한 입력을 [key, value]쌍으로 설정하십시오 (외부 배열의 각 항목에 대한 하위 배열).
  2. 실제로 그렇게 할 수 있으면 둘 중 하나 #to_h또는 Hash::[]둘 다 작동합니다.
  3. 할 수없는 경우 입력이 균형을 이루는Hash::[] 한 splat ( *) 과 함께 사용할 수 있습니다.
  4. 으로 불균형평면 경우 입력으로 배열, 전혀 유일한 방법이 의지의 작품은 합리적이고 마지막 value 항목이 실종있는 유일한 사람입니다.

참고 사항 : 추가 할 가치가 있다고 생각하기 때문에이 답변을 게시하고 있습니다. 기존 답변 중 일부에는 잘못된 정보가 있으며 여기에서 수행하려는 것만 큼 완전한 답변을 제공 한 사람은 없습니다. 도움이 되었기를 바랍니다. 그럼에도 불구하고 나는이 답변의 일부에 영감을 주었던 제 앞에 오신 분들께 감사드립니다.