루비 해시 배열


192

좋아, 여기에 거래가 있습니다. 나는 이것에 대한 해결책을 찾기 위해 여러 해 동안 인터넷 검색을 해왔으며 거기에 많은 사람들이 있지만 그들은 내가 찾고있는 일을하지 않는 것 같습니다.

기본적으로 나는 이런 식으로 배열 된 배열을 가지고 있습니다.

["item 1", "item 2", "item 3", "item 4"] 

이것을 해시로 변환하고 싶습니다.

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

즉, '짝수'인덱스에있는 항목은 키이고 'odd'인덱스에있는 항목은 값입니다.

이것을 깨끗하게하는 방법에 대한 아이디어가 있습니까? 무차별 강제 방법은 모든 짝수 색인을 별도의 배열로 가져온 다음 주위를 반복하여 값을 추가하는 것입니다.

답변:


358
a = ["item 1", "item 2", "item 3", "item 4"]
h = Hash[*a] # => { "item 1" => "item 2", "item 3" => "item 4" }

그게 다야. 이것을 splat 연산자 *라고합니다 .

@Mike Lewis 당 한 가지주의 사항 (의견에서) : "조심해야합니다. 루비는 스택에서 표시를 확장합니다. 대규모 데이터 세트로이 작업을 수행하는 경우 스택을 제거 할 것으로 예상됩니다."

따라서 대부분의 일반적인 사용 사례에서이 방법은 훌륭하지만 많은 데이터를 변환하려면 다른 방법을 사용하십시오. 예를 들어, @ Łukasz Niemier (또한 주석에서)는 큰 데이터 세트에이 방법을 제공합니다.

h = Hash[a.each_slice(2).to_a]

10
@tester *splat 연산자 라고합니다 . 배열을 받아서 항목의 리터럴 목록으로 변환합니다. 따라서 *[1,2,3,4]=> 1, 2, 3, 4입니다. 이 예에서 위의 작업은 것과 같습니다 Hash["item 1", "item 2", "item 3", "item 4"]. 그리고 Hash있다 [](심지어 인덱스 키와 홀수 인덱스 값을) 인수 목록을 받아들이는 방법을하지만, Hash[]우리가 사용하는 배열을 플랫 있도록 배열을 허용하지 않습니다 *.
Ben Lee

15
이것에 매우 조심하십시오. Ruby는 스택에서 표시를 확장합니다. 큰 데이터 세트로이 작업을 수행하면 스택이 터질 것입니다.
Mike Lewis

9
빅 데이터 테이블에서는을 사용할 수 있습니다 Hash[a.each_slice(2).to_a].
Hauleth

4
"스택을 날려 버린다"는 무슨 뜻입니까?
Kevin

6
@Kevin, 스택 은 프로그램이 특정 작업을 위해 할당하고 예약하는 작은 메모리 영역을 사용합니다. 가장 일반적으로 지금까지 호출 된 메소드 스택을 유지하는 데 사용됩니다. 이것이 바로 스택 트레이스 라는 용어의 원점이며, 무한 재귀 메서드가 스택 오버플로를 일으킬 수있는 이유이기도합니다 . 이 답변의 방법은 스택을 사용하지만 스택은 메모리의 작은 영역이므로 큰 배열 로이 방법을 시도하면 스택을 채우고 오류 (같은 줄을 따라 오류가 발생합니다) 스택 오버플로).
Ben Lee

103

Ruby 2.1.0은 to_h원래 배열이 키-값 쌍의 배열로 구성된 경우 필요한 것을 수행 하는 메소드를 Array에 도입했습니다 . http://www.ruby-doc.org/core-2.1.0/Array.html#method -i-to_h .

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

1
아름다운! 여기에있는 다른 솔루션보다 훨씬 낫습니다.
Dennis

3
2.1.0 이전의 루비 버전의 경우 중첩 배열 쌍이 있으면 Hash :: [] 메서드를 사용하여 유사한 결과를 얻을 수 있습니다. 그래서 a = [[: foo, : 1], [bar, 2]] --- 해시 [a] => {: foo => 1, : bar => 2}
AfDev

@AfDev, 정말로 감사합니다. 당신은 맞습니다 ( 단어 오타를 무시할 때 : bar기호 :2가 필요 하고 기호 는 정수 여야합니다. 따라서 수정 된 식은입니다 a = [[:foo, 1], [:bar, 2]]).
Jochem Schulenklopper 2014

28

Hash.[]배열의 값과 함께 사용 하십시오. 예를 들면 다음과 같습니다.

arr = [1,2,3,4]
Hash[*arr] #=> gives {1 => 2, 3 => 4}

1
[* arr]은 무슨 뜻인가요?
Alan Coromano

1
@Marius : 인수 목록으로 *arr변환 arr하므로 []arr의 내용을 인수로 사용하여 Hash 메소드를 호출합니다 .

26

또는 배열 배열이있는 경우 다음을 [key, value]수행 할 수 있습니다.

[[1, 2], [3, 4]].inject({}) do |r, s|
  r.merge!({s[0] => s[1]})
end # => { 1 => 2, 3 => 4 }

2
당신은 대답이 질문과 관련이 없으며 귀하의 경우에도 여전히 같은 것을 사용하는 것이 훨씬 쉽습니다Hash[*arr]
Yossi

2
아니. 반환 { [1, 2] => [3, 4] }합니다. 그리고 질문의 제목에 "Array to Hash"가 표시되고 내장 된 "Hash to Array"메서드는 다음과 같이 수행합니다. 내장 된 "Hash to Array"메서드 { 1 => 2, 3 => 4}.to_a # => [[1, 2], [3, 4]]의 역수를 얻으려고 여러 사람이 여기서 끝낼 수 있다고 생각했습니다. 사실, 내가 어쨌든 여기서 끝났어.
Erik Escobedo

1
미안, 여분의 별표를 추가했습니다. Hash[arr]당신을 위해 일을 할 것입니다.
요시

9
IMHO 더 나은 솔루션 : Hash [* array.flatten (1)]
손님

2
요시 : 죽은 사람을 키워서 미안하지만, 그의 대답에는 한 가지 더 큰 문제가 있습니다 #inject. 그것이 방법을 사용하는 것입니다. 와 #merge!, #each_with_object사용되어 있어야합니다. #inject주장 했다면 , #merge오히려 #merge!사용해야합니다.
Boris Stitnicky 2016 년

12

이것은 내가 인터넷 검색 할 때 찾고 있던 것입니다.

[{a: 1}, {b: 2}].reduce({}) { |h, v| h.merge v } => {:a=>1, :b=>2}


당신은 사용하고 싶지 않으며 merge, 루프마다 새로운 해시를 생성하고 버리고 매우 느립니다. 해시 배열이있는 경우 [{a:1},{b:2}].reduce({}, :merge!)대신 시도하십시오 -모든 것을 동일한 (새로운) 해시로 병합합니다.

고마워, 이것도 내가 원하는 것입니다! :)
Thanasis Petsas 1

당신은 또한 할 수 있습니다.reduce(&:merge!)
벤 리

1
[{a: 1}, {b: 2}].reduce(&:merge!)다음으로 평가{:a=>1, :b=>2}
Ben Lee

이 작품 때문에 주사한다 / 당신이에 작동하는 배열의 첫 번째 인수를 사용하는 경우에 인수 생략 할 수있는 기능이 감소 입력 인수 및 배열과 배열의 나머지 부분을. 이것을 심볼 대 프로세스와 결합하면이 간결한 구성을 얻을 수 있습니다. 환언 [{a: 1}, {b: 2}].reduce(&:merge!)과 동일 [{a: 1}, {b: 2}].reduce { |m, x| m.merge(x) }동일하다 [{b: 2}].reduce({a: 1}) { |m, x| m.merge(x) }.
벤 리

10

Enumerator포함 Enumerable합니다. 이후 2.1, Enumerable같은 방법이있다 #to_h. 그래서 우리는 다음과 같이 쓸 수 있습니다.

a = ["item 1", "item 2", "item 3", "item 4"]
a.each_slice(2).to_h
# => {"item 1"=>"item 2", "item 3"=>"item 4"}

#each_slice블록이 없으면 Enumerator위의 설명 #to_h에 따라 Enumerator객체를 제공 할 수 있기 때문에 객체 에서 메소드를 호출 할 수 있습니다 .


7

단일 배열의 경우 이와 같이 시도 할 수 있습니다

irb(main):019:0> a = ["item 1", "item 2", "item 3", "item 4"]
  => ["item 1", "item 2", "item 3", "item 4"]
irb(main):020:0> Hash[*a]
  => {"item 1"=>"item 2", "item 3"=>"item 4"}

배열의 배열

irb(main):022:0> a = [[1, 2], [3, 4]]
  => [[1, 2], [3, 4]]
irb(main):023:0> Hash[*a.flatten]
  => {1=>2, 3=>4}

6
a = ["item 1", "item 2", "item 3", "item 4"]
Hash[ a.each_slice( 2 ).map { |e| e } ]

또는 당신이 싫어하는 경우 Hash[ ... ]:

a.each_slice( 2 ).each_with_object Hash.new do |(k, v), h| h[k] = v end

또는 기능 프로그래밍이 깨진 게으른 팬인 경우 :

h = a.lazy.each_slice( 2 ).tap { |a|
  break Hash.new { |h, k| h[k] = a.find { |e, _| e == k }[1] }
}
#=> {}
h["item 1"] #=> "item 2"
h["item 3"] #=> "item 4"

당신이 완전히 미워 Hash[ ... ]하지는 않지만 그것을 연결 된 방법으로 사용하고 싶다면 (와 같이 할 수있는 것처럼 to_h) Boris 제안을 결합하고 다음과 같이 쓸 수 있습니다.arr.each_slice( 2 ).map { |e| e }.tap { |a| break Hash[a] }
b-studios

위 코드의 의미를보다 명확하게하기 위해 : "lazy hash"h 가 만들어 지며 처음에는 비어 있으며 필요할 때 원래 배열에서 요소를 가져옵니다. 그래야만 실제로 h에 저장됩니다!
Daniel Werner

1

모든 답변은 시작 배열이 고유하다고 가정합니다. OP는 중복 항목이있는 배열을 처리하는 방법을 지정하지 않아 중복 키가 발생했습니다.

봅시다 :

a = ["item 1", "item 2", "item 3", "item 4", "item 1", "item 5"]

item 1 => item 2bij가 재정의 되면 쌍을 잃게됩니다 item 1 => item 5.

Hash[*a]
=> {"item 1"=>"item 5", "item 3"=>"item 4"}

reduce(&:merge!)동일한 제거 결과를 포함한 모든 방법 .

그러나 이것이 정확히 당신이 기대하는 것일 수 있습니다. 그러나 다른 경우에는 Array대신 for 값 으로 결과를 얻고 싶을 것입니다 .

{"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}

순진한 방법은 도우미 변수, 기본값을 가진 해시를 만들고 루프에 채우는 것입니다.

result = Hash.new {|hash, k| hash[k] = [] } # Hash.new with block defines unique defaults.
a.each_slice(2) {|k,v| result[k] << v }
a
=> {"item 1"=>["item 2", "item 5"], "item 3"=>["item 4"]}

한 줄에서 위의 내용 을 사용 assoc하고 reduce수행 하는 것이 가능할 수 있지만, 추론하고 읽기가 훨씬 어려워집니다.

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