배열에 많은 데이터가 있지만 해시를 사용하는 것처럼 키/값 조회를 수행해야 하는 경우가 있습니까? 다행히 Ruby는 배열을 키-값 구조로 처리하는 메커니즘을 제공합니다. 확인해 봅시다!
Array#assoc
소개 및 Array#rassoc
마법의 주식 선별 기계를 받았다고 상상해보십시오. 몇 분마다 주식을 사거나 팔도록 권장합니다. 컴퓨터에 연결하고 다음과 같은 데이터 스트림을 수신했습니다.
picks = [
["AAPL", "buy"],
["GOOG", "sell"],
["MSFT", "sell"]
]
Google에 대한 최신 지침을 찾으려면 Array#assoc
방법. 다음과 같습니다.
# Returns the first row of data where row[0] == "GOOG"
picks.assoc("GOOG") # => ["GOOG", "sell"]
가장 최근의 "판매" 권장 사항을 찾으려면 Array#rassoc
를 사용할 수 있습니다. 방법.
# Returns the first row of data where row[1] == "sell"
picks.rassoc("sell") # => ["GOOG", "sell"]
일치하는 항목이 없으면 메서드는 nil을 반환합니다.
picks.assoc("CSCO") # => nil
picks.rassoc("hold") # => nil
이력 데이터
해시는 단일 키에 대해 둘 이상의 값을 가질 수 없습니다. 그러나 배열에는 원하는 만큼 중복이 있을 수 있습니다. assoc 및 rassoc 메서드는 이 경우에 합리적인 작업을 수행하고 찾은 첫 번째 일치하는 행을 반환합니다. 이렇게 하면 꽤 흥미로운 일을 할 수 있습니다.
가상의 주식 피킹 머신은 데이터 스트림을 제공합니다. 결국, 그것은 특정 회사에 대해 마음을 바꾸고 이전에 나에게 팔라고 했던 것을 사라고 나에게 말할 것입니다. 이 경우 데이터는 다음과 같습니다.
picks = [
["GOOG", "buy"],
["AAPL", "sell"],
["AAPL", "buy"],
["GOOG", "sell"],
["MSFT", "sell"]
]
이 모든 데이터를 해시에 넣는 경우 특정 주식에 대한 추천을 업데이트하면 해당 주식에 대한 이전 추천이 손실될 수 있습니다. 어레이에서는 그렇지 않습니다. Array#assoc이 항상 가장 최근의 권장 사항을 제공한다는 것을 알고 있으므로 어레이에 권장 사항을 계속 추가할 수 있습니다.
# Returns the first row of data where row[0] == "GOOG"
picks.assoc("GOOG") # => ["GOOG", "buy"]
따라서 우리는 무료 감사 추적과 함께 해시의 키-값 장점을 얻습니다.
열이 2개 이상
assoc의 또 다른 멋진 점은 배열당 두 개의 열로 제한되지 않는다는 것입니다. 원하는 만큼 열을 가질 수 있습니다. 각 매수/매도 추천에 타임스탬프를 추가했다고 가정합니다.
picks = [
["AAPL", "buy", "2015-08-17 12:11:55 -0700"],
["GOOG", "sell", "2015-08-17 12:10:00 -0700"],
["MSFT", "sell", "2015-08-17 12:09:00 -0700"]
]
이제 assoc
를 사용할 때 또는 rassoc
, 타임스탬프도 가져옵니다.
# The entire row is returned
picks.assoc("GOOG") # => ["GOOG", "sell", "2015-08-17 12:10:00 -0700"]
열이 많을 수 있는 CSV 및 기타 파일 형식의 데이터를 처리할 때 이것이 얼마나 유용한지 알 수 있기를 바랍니다.
속도
Ruby의 해시는 확실히 Array#assoc
보다 성능이 뛰어납니다. 대부분의 벤치마크에서 데이터 세트가 커질수록 차이점이 더 분명해집니다. 결국 해시 테이블 검색은 O(1)이고 배열 검색은 O(n)입니다. 그러나 경우에 따라 차이가 걱정할 만큼 크지 않을 수 있습니다. 세부정보에 따라 다릅니다.
그냥 재미로 10행 데이터세트와 100,000행 데이터세트에 대해 해시 조회와 assoc을 비교하는 간단한 벤치마크를 작성했습니다. 예상대로 해시와 배열은 작은 데이터 세트와 유사하게 수행되었습니다. 대규모 데이터 세트에서는 해시가 어레이를 지배했습니다.
...공정하게 말해서 배열의 마지막 요소를 찾고 있는데, 이는 배열 검색의 최악의 시나리오입니다.
require 'benchmark/ips'
require 'securerandom'
Benchmark.ips do |x|
x.time = 5
x.warmup = 2
short_array = (0..10).map { |i| [SecureRandom.hex(), i] }
short_hash = Hash[short_array]
short_key = short_array.last.first
long_array = (0..100_000).map { |i| [SecureRandom.hex(), i] }
long_hash = Hash[long_array]
long_key = short_array.last.first
x.report("short_array") { short_array.assoc(short_key) }
x.report("short_hash") { short_hash[short_key] }
x.report("long_array") { long_array.assoc(long_key) }
x.report("long_hash") { long_hash[long_key] }
x.compare!
end
# Calculating -------------------------------------
# short_array 91.882k i/100ms
# short_hash 149.430k i/100ms
# long_array 19.000 i/100ms
# long_hash 152.086k i/100ms
# -------------------------------------------------
# short_array 1.828M (± 3.4%) i/s - 9.188M
# short_hash 6.500M (± 4.8%) i/s - 32.426M
# long_array 205.416 (± 3.9%) i/s - 1.026k
# long_hash 6.974M (± 4.2%) i/s - 34.828M
# Comparison:
# long_hash: 6974073.6 i/s
# short_hash: 6500207.2 i/s - 1.07x slower
# short_array: 1827628.6 i/s - 3.82x slower
# long_array: 205.4 i/s - 33950.98x slower