Вычитание одного массива из другого в Ruby


83

У меня есть два массива Задач - созданные и назначенные. Я хочу удалить все поставленные задачи из массива созданных задач. Вот мой рабочий, но беспорядочный код:

    @assigned_tasks = @user.assigned_tasks
    @created_tasks = @user.created_tasks

    #Do not show created tasks assigned to self
    @created_not_doing_tasks = Array.new
    @created_tasks.each do |task|
        unless @assigned_tasks.include?(task)
            @created_not_doing_tasks << task
        end
    end

Я уверен, что есть способ получше. Что это такое? Благодаря :-)


Держу пари, что под капотом ответ - это то, что вы там закодировали.
baash05

Ответы:


177

В Ruby можно вычитать массивы:

[1,2,3,4,5] - [1,3,4]  #=> [2,5]

ary - other_ary → new_ary Разница в массиве

Возвращает новый массив, который является копией исходного массива, удаляя все элементы, которые также присутствуют в other_ary. Порядок сохраняется из исходного массива.

Он сравнивает элементы, используя их хэш и eql? методы повышения эффективности.

[ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]

Если вам нужно поведение, подобное множеству, см. Библиотечный класс Set.

См. Документацию по массиву .


2
Арг. Большой момент для меня. Почему-то я подумал, что с объектами это не сработает. Сработало отлично - спасибо!
doctororange

28
осторожно с этим, сначала проверьте его в IRB, например: [5, 5, 5, 5] - [5, 5] = [] ... вычитание удаляет уникальные элементы в массиве.
hagope

9
Также обратите внимание, что это не будет работать: [1,2]-[1,2,3] => []. Но [1,2,3]-[1,2] => [3]... Ага.
Забба

17
Если мыслить в терминах вычитания, то эти последние «подводные камни» действительно имеют смысл. Чтобы вычесть то, о чем вы не просите разницу ... вы просите вычесть Y из X ... если Y не имеет чего-то даже в X, тогда результат будет вроде 'undefined', следовательно, дополнительный Y-элемент не будет не попадут в X-результат.
Bane

2
В частности, Array#-это разница в наборах. Это скорее обратное объединению Array#|множеств, чем Array#+конкатенация (а вовсе не операция над множествами!).
ymbirtt 02

9

Вышеупомянутое решение

a - b

удаляет все экземпляры элементов в массиве bиз массива a.

[ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ]  #=>  [ 3, 3, 5 ]

В некоторых случаях вы хотите, чтобы результат был [1, 2, 3, 3, 5] . То есть вы не хотите удалять все дубликаты, а только элементы по отдельности.

Вы можете добиться этого с помощью

class Array
  def delete_elements_in(ary)
    ary.each do |x|
      if index = index(x)
        delete_at(index)
      end
    end
  end
end

контрольная работа

irb(main):198:0> a = [ 1, 1, 2, 2, 3, 3, 4, 5 ]
=> [1, 1, 2, 2, 3, 3, 4, 5]
irb(main):199:0> b = [ 1, 2, 4 ]
=> [1, 2, 4]
irb(main):200:0> a.delete_elements_in(b)
=> [1, 2, 4]
irb(main):201:0> a
=> [1, 2, 3, 3, 5]

Код работает, даже если два массива не отсортированы. В примере массивы отсортированы, но это не обязательно.


delete_elements_inнедоступен в собственно Ruby (ruby 2.6.3p62)
qaisjp

@qaisjp, потому что он определен автором. Вы тоже должны это определить. Посмотрите на код над тестовым кодом.
ismailarilik
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.