Додайте елемент до масиву, якщо його там ще немає


92

У мене є клас Рубі

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 заказано хеш. "Хеші перелічують свої значення в тому порядку, в який були вставлені відповідні ключі." ruby-doc.org/core-1.9.1/Hash.html
філи

Ніколи нового не було такого поняття, як набір. Вони чудові, велике спасибі
Бред,

123

@Coorasse має гарну відповідь , хоча вона повинна бути:

my_array | [item]

І оновити my_arrayна місці:

my_array |= [item]

63
або my_array |= [item]який оновиться my_arrayна місці
andorov

2
Можливо, мені чогось тут не вистачає, але оператор | =, здається, не працює для мене? Я запускаю Ruby 2.1.1
Viet

@Viet чудово |=працює в моїх тестах з 2.1.1. Опишіть свій тест або відкрийте нове запитання.
Depquid

Перевіряємо ще раз, і це працює зараз. Не знаю, чим я займався раніше, оскільки мій коментар був зроблений багато місяців тому.
В'єт

1
У чому складність цього?
Nobita

40

Вам не потрібно перебирати my_arrayвручну.

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

Редагувати:

Як зазначає Томбарт у своєму коментарі, використання Array#include?не надто ефективно. Я б сказав, що вплив продуктивності є незначним для малих масивів, але ви можете піти Setна більші.


6
ви точно не хочете цього робити! array.include?(item)має складність O(n)- отже, це як перегляд цілого масиву. подивіться на цей орієнтир: gist.github.com/deric/4953652
Tombart

32

Ви можете перетворити item1 в масив і приєднати їх:

my_array | [item1]

1
Це повинно бути |НЕ ||(див відповіді Джейсона)
Сет

1
Моя провина. Вибачте. Відповідь
відредаговано

3

Важливо мати на увазі, що клас Set та | метод (також званий "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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.