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


123

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

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

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

{apple => 1, banana => 2}

답변:


91

참고 : 간결하고 효율적인 솔루션은 아래 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}

15
a3 == a3.map {| k, v |이므로 Hash [a3]와 동일합니다. [k, v]}는 사실이며 실제로는 a3.dup과 동일합니다.
Cluster

2
맵을 사용하는 대신 평면화의 깊이를 지정하지 않는 이유는 무엇입니까? 예를 들어 : h3 = Hash[*a3.flatten(1)]대신 h3 = Hash[*a3.flatten]오류가 발생합니다.
Jeff McCune

3
이 대답은 효율적이지 않습니다. 또한 구식입니다. 내 대답을 참조하십시오.
Marc-André Lafortune

1
네, 저는 Marc-André to_h가 더 낫다고 생각 합니다.
B Seven

1
@ Marc-André Lafortune 감사합니다. 사용자를 귀하의 답변으로 안내하기 위해 답변을 업데이트했습니다.
Stew

145

간단히 사용 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키와 값이 예상대로 작동합니다.


4
오, 웅변! 내가 루비 좋아하는 이유입니다
iGbanam

11
경고 : 배열 키 또는 값을 원하는 경우 평면화를 사용하여 답변하면 문제가 발생합니다.
Stew

배열 키 또는 값 문제를 방지 할 수있는 대체 솔루션을 아래에 게시했습니다.
Stew

5
이에 대한 포괄적 인 해결책을 시도하지 않는 것이 좋습니다. 키와 값이 [[key1, value1], [key2, value2]]에서와 같이 쌍을 이루는 경우 비 육질없이 Hash []에 전달하면됩니다. 해시 [a2] == 해시 [* a2.flatten]. 배열이 [key1, value1, key2, value2]와 같이 이미 평면화 된 경우 var 앞에 *, Hash [* a1]
Cluster

8
FWIW, 만약 당신이 정말로 (더 많은) 하나의 크기에 맞는 버전을 원한다면 Hash[*ary.flatten(1)], 배열 키와 값을 보존 할을 사용할 수도 있습니다 . flatten그것들을 파괴하는 것은 재귀 적 이며 피할 수있을만큼 쉽습니다.
brymck 2013 년

80

가장 좋은 방법은 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. 이것은 배열 자체 인 값에 문제를 일으킬 수 있습니다.


4
새로운 .to_h 메서드의 단순성에 감사드립니다!
코딩 중독 됨

3
등 내가 to_h더 이상 답변보다는 방법은이 변환의 의도를 표현하기 때문에 배열에서 작동합니다.
B Seven

1
@BSeven 어느 Array#to_h도는 Enumerable#to_h코어 루비 1.9이다.
Iron Savior

배열 [[apple, 1], [banana, 2], [apple, 3], [banana, 4]]이 있고 출력을 원하면 {"apple" =>[1,3], "banana"=>[2,4]}어떻게합니까?
nishant

@NishantKumar는 다른 질문입니다.
Marc-André Lafortune 17. 9.


9

편집 : 내가 글을 쓰는 동안 게시 된 응답을 보았습니다. 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 }

2
a.inject({})보다 유연한 값 할당을 허용 하는 한 줄의 경우 +1 .
Chris Bloom

그것은 드롭 수도 h = {}로 끝나는 분사의 사용을 통해 두 번째 예제에서a.each_slice(2).inject({}) { |h,i| h[i.first] = i.last; h }
lindes

할 수 있습니다a.each_slice(2).to_h
Conor O'Brien

6

다음을 사용하여 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} 

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 항목이 실종있는 유일한 사람입니다.

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


3

답변에 추가하지만 익명 배열을 사용하고 주석을 추가합니다.

Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]

내부에서 시작하여 그 대답을 분리하십시오.

  • "a,b,c,d" 실제로 문자열입니다.
  • split 쉼표로 배열에.
  • zip 다음 배열과 함께.
  • [1,2,3,4] 실제 배열입니다.

중간 결과는 다음과 같습니다.

[[a,1],[b,2],[c,3],[d,4]]

flatten은 다음과 같이 변환합니다.

["a",1,"b",2,"c",3,"d",4]

그리고:

*["a",1,"b",2,"c",3,"d",4] 그것을 풀어 "a",1,"b",2,"c",3,"d",4

Hash[]메서드에 대한 인수로 사용할 수 있습니다 .

Hash[*("a,b,c,d".split(',').zip([1,2,3,4]).flatten)]

결과 :

{"a"=>1, "b"=>2, "c"=>3, "d"=>4}

이것은 splat ( *) 없이도 작동 하고 flatten : Hash[("a,b,c,d".split(',').zip([1,2,3,4]))]=> {"a"=>1, "b"=>2, "c"=>3, "d"=>4}. 내가 추가 한 답변에 더 자세히 설명되어 있습니다.
lindes

0

다음과 같은 배열이 있다면-

data = [["foo",1,2,3,4],["bar",1,2],["foobar",1,"*",3,5,:foo]]

각 배열의 첫 번째 요소가 해시의 키가되고 나머지 요소는 값 배열이되기를 원합니다. 그러면 다음과 같이 할 수 있습니다.

data_hash = Hash[data.map { |key| [key.shift, key] }]

#=>{"foo"=>[1, 2, 3, 4], "bar"=>[1, 2], "foobar"=>[1, "*", 3, 5, :foo]}

0

이것이 최선의 방법인지 확실하지 않지만 작동합니다.

a = ["apple", 1, "banana", 2]
m1 = {}
for x in (a.length / 2).times
  m1[a[x*2]] = a[x*2 + 1]
end

b = [["apple", 1], ["banana", 2]]
m2 = {}
for x,y in b
  m2[x] = y
end

-1

숫자 값이 seq 인덱스이면 더 간단한 방법을 사용할 수 있습니다. 여기에 제 코드 제출이 있습니다. My Ruby는 약간 녹슬 었습니다.

   input = ["cat", 1, "dog", 2, "wombat", 3]
   hash = Hash.new
   input.each_with_index {|item, index|
     if (index%2 == 0) hash[item] = input[index+1]
   }
   hash   #=> {"cat"=>1, "wombat"=>3, "dog"=>2}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.