Programing

Ruby에서 배열에서 해시를 만들려면 어떻게해야합니까?

lottogame 2020. 10. 31. 09:11
반응형

Ruby에서 배열에서 해시를 만들려면 어떻게해야합니까?


간단한 배열이 있습니다.

arr = ["apples", "bananas", "coconuts", "watermelons"]

또한 f단일 문자열 입력에 대해 작업을 수행하고 값을 반환하는 함수 가 있습니다. 이 작업은 비용이 많이 들기 때문에 결과를 해시로 기억하고 싶습니다.

다음과 같이 원하는 해시를 만들 수 있다는 것을 알고 있습니다.

h = {}
arr.each { |a| h[a] = f(a) }

내가하고 싶은 것은 h를 초기화 할 필요가 없기 때문에 다음과 같이 작성할 수 있습니다.

h = arr.(???) { |a| a => f(a) }

할 수 있습니까?


재미있는 이름을 가진 함수가 있다고 가정 해 보겠습니다. "f"

def f(fruit)
   fruit + "!"
end

arr = ["apples", "bananas", "coconuts", "watermelons"]
h = Hash[ *arr.collect { |v| [ v, f(v) ] }.flatten ]

당신에게 줄 것입니다 :

{"watermelons"=>"watermelons!", "bananas"=>"bananas!", "apples"=>"apples!", "coconuts"=>"coconuts!"}

업데이트 :

주석에서 언급했듯이 Ruby 1.8.7에는 이에 대한 더 좋은 구문이 도입되었습니다.

h = Hash[arr.collect { |v| [v, f(v)] }]

주어진 답변 중 일부에 대해 빠르고 더러운 벤치 마크를 수행했습니다. (이러한 결과는 Ruby 버전, 이상한 캐싱 등을 기반으로 귀하의 결과와 정확히 일치하지 않을 수 있지만 일반적인 결과는 비슷합니다.)

arr ActiveRecord 개체의 모음입니다.

Benchmark.measure {
    100000.times {
        Hash[arr.map{ |a| [a.id, a] }]
    }
}

벤치 마크 @ real = 0.860651, @ cstime = 0.0, @ cutime = 0.0, @ stime = 0.0, @ utime = 0.8500000000000005, @ total = 0.8500000000000005

Benchmark.measure { 
    100000.times {
        h = Hash[arr.collect { |v| [v.id, v] }]
    }
}

벤치 마크 @ real = 0.74612, @ cstime = 0.0, @ cutime = 0.0, @ stime = 0.010000000000000009, @ utime = 0.740000000000002, @ total = 0.750000000000002

Benchmark.measure {
    100000.times {
        hash = {}
        arr.each { |a| hash[a.id] = a }
    }
}

벤치 마크 @ real = 0.627355, @ cstime = 0.0, @ cutime = 0.0, @ stime = 0.010000000000000009, @ utime = 0.6199999999999974, @ total = 0.6299999999999975

Benchmark.measure {
    100000.times {
        arr.each_with_object({}) { |v, h| h[v.id] = v }
    }
}

벤치 마크 @ real = 1.650568, @ cstime = 0.0, @ cutime = 0.0, @ stime = 0.12999999999999998, @ utime = 1.51, @ total = 1.64

결론적으로

Ruby가 표현력이 풍부하고 역동적이라고해서 항상 가장 예쁜 솔루션을 찾아야한다는 의미는 아닙니다. 기본 각 루프는 해시를 만드는 데 가장 빠릅니다.


h = arr.each_with_object({}) { |v,h| h[v] = f(v) }

이것은 아마도 내가 쓸 것입니다.

h = Hash[arr.zip(arr.map(&method(:f)))]

간단하고 명확하고 분명하며 선언적입니다. 무엇을 더 원할 수 있습니까?


Ruby 2.6.0 enables a shorter syntax by passing a block to the to_h method:

arr.to_h { |a| [a, f(a)] }

I'm doing it like described in this great article http://robots.thoughtbot.com/iteration-as-an-anti-pattern#build-a-hash-from-an-array

array = ["apples", "bananas", "coconuts", "watermelons"]
hash = array.inject({}) { |h,fruit| h.merge(fruit => f(fruit)) }

More info about inject method: http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-inject


Another one, slightly clearer IMHO -

Hash[*array.reduce([]) { |memo, fruit| memo << fruit << f(fruit) }]

Using length as f() -

2.1.5 :026 > array = ["apples", "bananas", "coconuts", "watermelons"]
 => ["apples", "bananas", "coconuts", "watermelons"] 
2.1.5 :027 > Hash[*array.reduce([]) { |memo, fruit| memo << fruit << fruit.length }]
 => {"apples"=>6, "bananas"=>7, "coconuts"=>8, "watermelons"=>11} 
2.1.5 :028 >

in addition to the answer of Vlado Cingel (I cannot add a comment yet, so I added an answer).

Inject can also be used in this way: the block has to return the accumulator. Only the assignment in the block returns the value of the assignment, and an error is reported.

array = ["apples", "bananas", "coconuts", "watermelons"]
hash = array.inject({}) { |h,fruit| h[fruit]= f(fruit); h }

참고URL : https://stackoverflow.com/questions/2943422/in-ruby-how-do-i-make-a-hash-from-an-array

반응형