source

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

lovecheck 2023. 6. 1. 22:50
반응형

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

루비에서, 다음 형태 중 하나의 배열이 주어지면...

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

...이것을 ...의 형태로 해시로 변환하는 가장 좋은 방법은 무엇입니까?

{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키와 값이 예상대로 작동합니다.

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

이 답변은 원래 글을 쓸 당시 가장 많이 지지를 받았던 플랫을 사용한 접근 방식의 대안으로 제시되었습니다.이 사례를 모범 사례나 효율적인 접근 방식으로 제시할 의도가 없었다는 점을 분명히 했어야 합니다.원답은 다음과 같습니다.


경고!플랫을 사용하는 솔루션은 어레이 키 또는 값을 보존하지 않습니다!

@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')을 예상했습니다.더 나쁜 것은 짝수 길이로 평평해진 다른 배열이 잘못된 값을 가진 해시를 조용히 제공한다는 것입니다.

배열 키 또는 값을 사용하려면 맵:

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

이렇게 하면 어레이 키가 유지됩니다.

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

가장 좋은 방법은 다음을 사용하는 것입니다.

[ [: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_h 2할 수 의 경우 my Ruby 2.6.0+를 할 수 . 초기 루비의 경우 my를 사용할 수 있습니다.backports보석과

to_hRuby 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(릴리스 노트 및 루비 문서), 변환 문제를 해결합니다.ArrayHash.

Ruby 문서 예제:

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

편집: 제가 글을 쓰는 동안 올린 답변을 보니 해시[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} 

요약 및 TL;DR:

이 답변은 다른 답변의 정보를 종합적으로 정리하는 것이 되기를 바랍니다.

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

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

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


설정: 변수

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

질문에 직접적으로 포함된 내용에 따르면,a1그리고.a2:

(참고: 아마도apple그리고.banana변수를 표현하기 위한 것이었습니다.다른 사람들이 그랬던 것처럼, 저는 앞으로 입력과 결과가 일치할 수 있도록 문자열을 사용할 것입니다.)

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:

일부에서는 (Ruby 2.1.0에 표시되었으며 이전 버전으로 역포팅할 수 있음)을 사용할 것을 제안했습니다.초기 플랫 배열의 경우에는 다음과 같이 작동하지 않습니다.

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

스플랫 연산자와 함께 사용하면 다음 작업이 수행됩니다.

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::[]작동합니다, 계속작동, 그설정냥을 설정합니다.nildurian의 다른 중 배열에 a4" 있 1-배 열 한 른 다 배 열 요 소 불 과 에 및 값 는 에 : 요 ) 소 배 열 ▁a ▁( 른 ) ▁element 다 4 및

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

- 변수 하기 - 평화변사 - 새용수탄a5그리고.a6

몇 가지 다른 답변이 언급되었습니다. 포함 여부와 관계없이.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]`.)

언뜻 보기에, 이것은 효과가 있는 것처럼 보이지만, 그것은 씨 없는 오렌지로 우리를 잘못된 길로 이끌었고, 따라서 또한 만들었습니다.3열쇠와durian

리고이은것과 로 치마그.a1작동하지 않습니다.

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

그렇게a4.flatten우리에게 유용하지 않아요, 우리는 그냥 사용하고 싶어요.Hash[a4]

flatten(1)대소문자(a6):

하지만 부분적으로 평평해지는 것은 어떨까요?주목할 만한 것은 전화가Hash::[]용사를 splat으로 결합된a6)는 호출하는 것과 같지 않습니다.Hash[a4]:

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

사전 배열,으로 미리플된배열다, 히중첩됨있수음여전얻로을으른방법a6):

하지만 애초에 이것이 우리가 배열을 얻은 방법이라면 어떨까요?(즉, 와 비교할 수 있습니다.a1입력 데이터였습니다. 이번에는 일부 데이터가 어레이 또는 다른 개체가 될 수 있습니다.)우리는 그것을 보았습니다.Hash[*a6]작동하지는 않지만, 만약 우리가 마지막 요소(중요! 아래 참조)가 핵심으로 작용한 행동을 여전히 얻고 싶다면 어떨까요?nil 가치?

이러한 상황에서 키/ 쌍을 외부 배열의 요소로 되돌리는 방법은 다음과 같습니다.

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
}

(참고: 의 를 사용합니다.ap여기서 구조를 더 쉽게 보여주기 위해, 개념적 요구 사항은 없습니다.)

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


테이크아웃:

  1. 가능할 때마다 다음과 같이 입력을 설정합니다.[key, value]쌍(외부 배열의 각 항목에 대한 하위 배열).
  2. 수 는 당이정그할수있면다렇게말로신,▁either▁when면있다수▁you▁do,▁that▁indeed▁can할당정.#to_h또는Hash::[]둘 다 작동할 것입니다.
  3. 당이할수없면다신,▁if.Hash::[]스플랫과 결합(*)는 입력이 균형을 이루는 한 작동합니다.
  4. 불균형하고 평평한 배열을 입력으로 사용할 경우, 이것이 합리적으로 작동하는 유일한 방법은 다음과 같은 경우입니다. value항목이 유일하게 누락되었습니다.

참고 사항:제가 이 답변을 올린 이유는 추가적인 가치가 있다고 생각하기 때문입니다. 기존 답변 중 일부는 잘못된 정보를 가지고 있으며, 제가 여기서 하려고 노력하는 것만큼 완벽한 답변을 한 답변은 없었습니다.도움이 되길 바랍니다.그럼에도 불구하고 저는 제 앞에 온 사람들에게 감사를 드리며, 그들 중 몇몇은 이 대답의 일부에 영감을 주었습니다.

답변에 추가하지만 익명 배열을 사용하고 주석 달기:

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]]

플랫한 다음 다음으로 변환합니다.

["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}

만약 당신이 이렇게 생긴 배열을 가지고 있다면 -

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

이것이 최선의 방법인지는 모르겠지만, 이것은 효과가 있습니다.

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

성능 및 메모리 할당 관련 문제는 여러 솔루션을 벤치마크한 단일 해시에 해시 배열을 매핑하는 Rails에 대한 답변을 확인하십시오.

reduce/inject어떤 방법을 사용하느냐에 따라 가장 빠른 솔루션 또는 가장 느린 솔루션이 될 수 있습니다.

숫자 값이 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}

언급URL : https://stackoverflow.com/questions/39567/what-is-the-best-way-to-convert-an-array-to-a-hash-in-ruby

반응형