เพิ่มองค์ประกอบให้กับอาร์เรย์หากยังไม่มี


92

ฉันมีคลาส Ruby

class MyClass
  attr_writer :item1, :item2
end

my_array = get_array_of_my_class() #my_array is an array of MyClass
unique_array_of_item1 = []

ฉันต้องการส่งMyClass#item1ไปunique_array_of_item1แต่ถ้าunique_array_of_item1ยังไม่มีสิ่งitem1นั้น มีวิธีง่ายๆที่ฉันรู้: เพียงแค่ทำซ้ำmy_arrayและตรวจสอบว่าunique_array_of_item1มีกระแสอยู่แล้วitem1หรือไม่

มีโซลูชันที่มีประสิทธิภาพมากกว่านี้หรือไม่?

คำตอบ:


82

คุณสามารถใช้Setแทน Array


แม้ว่าจะเป็นความจริงที่เอกสารบอกว่าชุดไม่ใช่คำสั่ง แต่ในความเป็นจริง (ตามที่ Ruby 1.9) สั่ง ถ้าคุณดูที่รหัสวิธีการหลักที่คุณต้องการใช้ที่จะได้รับการสั่งซื้อ (เช่นSet#eachและSet#to_a) @hashตัวแทนไป และเมื่อมีการสั่งซื้อ Ruby 1.9 Hashes "แฮชแจกแจงค่าตามลำดับที่ใส่คีย์ที่เกี่ยวข้อง" ruby-doc.org/core-1.9.1/Hash.html
phylae

ไม่เคยมีสิ่งใหม่เป็นชุด พวกเขายอดเยี่ยมขอบคุณมาก
Brad

123

@Coorasseมีคำตอบที่ดีแม้ว่าควรจะเป็น:

my_array | [item]

และเพื่ออัปเดตmy_arrayในสถานที่:

my_array |= [item]

63
หรือmy_array |= [item]จะอัปเดตmy_arrayในสถานที่
andorov

2
บางทีฉันอาจจะพลาดอะไรบางอย่างที่นี่ แต่ตัวดำเนินการ | = ดูเหมือนจะไม่ได้ผลสำหรับฉัน ฉันใช้ Ruby 2.1.1
เวียด

@Viet ใช้|=งานได้ดีในการทดสอบของฉันด้วย 2.1.1 โปรดอธิบายกรณีทดสอบของคุณหรือเปิดคำถามใหม่
depquid

ทดสอบอีกครั้งและใช้งานได้ทันที ไม่รู้ว่าก่อนหน้านี้ฉันทำอะไรตั้งแต่มีการแสดงความคิดเห็นเมื่อหลายเดือนก่อน
เวียด

1
อะไรคือความซับซ้อนของสิ่งนี้?
โนบิตะ

42

คุณไม่จำเป็นต้องทำซ้ำmy_arrayด้วยมือ

my_array.push(item1) unless my_array.include?(item1)

แก้ไข:

ตามที่ Tombart ชี้ให้เห็นในความคิดเห็นของเขาการใช้งานArray#include?ไม่ได้มีประสิทธิภาพมากนัก ฉันจะบอกว่าผลกระทบด้านประสิทธิภาพนั้นไม่สำคัญสำหรับอาร์เรย์ขนาดเล็ก แต่คุณอาจต้องการใช้สำหรับอาร์เรย์ที่Setใหญ่กว่า


6
คุณไม่อยากทำอย่างนั้นแน่นอน! array.include?(item)มีความซับซ้อนO(n)- จึงเหมือนกับการวนซ้ำอาร์เรย์ทั้งหมด ดูเกณฑ์มาตรฐานนี้: gist.github.com/deric/4953652
Tombart

ทางออกที่สวยงาม :). น่าอ่าน! ในกรณีของฉันฉันไม่สามารถตั้งค่าได้ฉันจึงใช้วิธีนี้
วิกเตอร์

คุณยังสามารถใช้การค้นหาแบบไบนารีเพื่อเพิ่มประสิทธิภาพหากอาร์เรย์ได้รับคำสั่ง [1, 2, 3, 4, 5].bsearch { |e| e == 3 }
วิกเตอร์

32

คุณสามารถแปลง item1 เป็นอาร์เรย์และเข้าร่วมได้:

my_array | [item1]

1
นี้ควรจะเป็น|ไม่ได้||(ดูคำตอบของเจสัน)
เซท

1
ความผิดของฉัน. ขออภัย. แก้ไขคำตอบ
coorasse

3

สิ่งสำคัญโปรดทราบว่าคลาส Set และ | method (เรียกอีกอย่างว่า "Set Union") จะให้อาร์เรย์ขององค์ประกอบที่ไม่ซ้ำกันซึ่งดีมากหากคุณไม่ต้องการให้ซ้ำกัน แต่จะเป็นเรื่องที่น่าแปลกใจหากคุณมีองค์ประกอบที่ไม่ซ้ำกันในอาร์เรย์เดิมโดยการออกแบบ

หากคุณมีองค์ประกอบที่ซ้ำกันอย่างน้อยหนึ่งรายการในอาร์เรย์เดิมของคุณที่คุณไม่ต้องการสูญเสียการทำซ้ำในอาร์เรย์ด้วยผลตอบแทนก่อนกำหนดเป็นกรณีที่เลวร้ายที่สุด O (n) ซึ่งไม่เลวร้ายเกินไปในรูปแบบที่ยิ่งใหญ่ของสิ่งต่างๆ .

class Array
  def add_if_unique element
    return self if include? element
    push element
  end
end

0

ฉันไม่แน่ใจว่ามันเป็นทางออกที่สมบูรณ์แบบหรือเปล่า แต่ใช้ได้กับฉัน:

    host_group = Array.new if not host_group.kind_of?(Array)
    host_group.push(host)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.