คุณเคยมีข้อมูลจำนวนมากในอาร์เรย์ แต่จำเป็นต้องทำการค้นหาคีย์/ค่าเหมือนกับที่คุณทำกับแฮชหรือไม่? โชคดีที่ 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"]
หากไม่พบการจับคู่ เมธอดจะคืนค่าเป็นศูนย์:
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"]
ดังนั้นเราจึงได้ประโยชน์จากค่าคีย์-ค่าของแฮช พร้อมกับหลักฐานการตรวจสอบฟรี
มากกว่าสองคอลัมน์
อีกอย่างที่เรียบร้อยเกี่ยวกับ 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) อย่างไรก็ตาม ความแตกต่างอาจไม่มากพอที่จะทำให้คุณกังวลได้ในบางกรณี ทั้งนี้ขึ้นอยู่กับรายละเอียด
เพื่อความสนุก ฉันเขียนเกณฑ์เปรียบเทียบง่ายๆ เปรียบเทียบการค้นหาแฮชกับ assoc สำหรับชุดข้อมูล 10 แถวและสำหรับชุดข้อมูล 100,000 แถว ตามที่คาดไว้ แฮชและอาร์เรย์ทำงานคล้ายกับชุดข้อมูลขนาดเล็ก ด้วยชุดข้อมูลขนาดใหญ่ แฮชจึงครอบงำอาร์เรย์
...แม้ว่าจะพูดตามตรง ฉันกำลังค้นหาองค์ประกอบสุดท้ายในอาร์เรย์ ซึ่งเป็นสถานการณ์กรณีที่เลวร้ายที่สุดสำหรับการค้นหาอาร์เรย์
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