Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

แอสโซซิเอทีฟอาเรย์ใน Ruby...อะไรนะ?

คุณเคยมีข้อมูลจำนวนมากในอาร์เรย์ แต่จำเป็นต้องทำการค้นหาคีย์/ค่าเหมือนกับที่คุณทำกับแฮชหรือไม่? โชคดีที่ 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