Restar una matriz de otra en Ruby

82

Tengo dos matrices de tareas: creadas y asignadas. Quiero eliminar todas las tareas asignadas de la matriz de tareas creadas. Aquí está mi código funcional, pero desordenado:

    @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

Estoy seguro de que hay una forma mejor. ¿Qué es? Gracias :-)

doctor naranja
fuente
Apuesto a que bajo el capó la respuesta es hacer exactamente lo que ha codificado allí.
baash05

Respuestas:

175

Puede restar matrices en Ruby:

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

ary - other_ary → new_ary Array Diferencia

Devuelve una nueva matriz que es una copia de la matriz original, eliminando cualquier elemento que también aparezca en other_ary. El orden se conserva de la matriz original.

Compara elementos usando su hash y eql? métodos de eficiencia.

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

Si necesita un comportamiento similar a un conjunto, consulte la clase de biblioteca Conjunto.

Consulte la documentación de Array .

hobodave
fuente
2
Arg. Gran momento en la palma de la mano para mí. Por alguna razón pensé que no funcionaría con objetos. Funcionó muy bien, ¡gracias!
doctororange
28
cuidado con esto, primero pruébelo en IRB, por ejemplo: [5, 5, 5, 5] - [5, 5] = [] ... la resta elimina los elementos únicos en la matriz.
hagope
9
También tenga en cuenta, esto no funcionará: [1,2]-[1,2,3] => []. Pero [1,2,3]-[1,2] => [3]. Argh.
Zabba
17
Si piensa en términos de resta, entonces estos últimos "errores" tienen sentido. Para restar algo no está pidiendo una diferencia ... está pidiendo restar Y de X ... si Y tiene algo que ni siquiera está en X, entonces el resultado es algo 'indefinido', por lo tanto, el elemento Y adicional no sería no se incluirá en el resultado X.
Bane
2
Específicamente, Array#-es una diferencia establecida. Es más una inversa de Array#|una unión de conjuntos que de una Array#+concatenación (¡no es una operación de conjuntos en absoluto!).
ymbirtt
9

La solución anterior

a - b

elimina todas las instancias de elementos en la matriz bde la matriz a.

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

En algunos casos, desea que el resultado sea [1, 2, 3, 3, 5]. Es decir, no desea eliminar todos los duplicados, sino solo los elementos individualmente.

Podrías lograr esto por

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

prueba

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]

El código funciona incluso cuando las dos matrices no están ordenadas. En el ejemplo, las matrices están ordenadas, pero esto no es necesario.

Zack Xu
fuente
delete_elements_inno está disponible en Ruby propiamente dicho (ruby 2.6.3p62)
qaisjp
@qaisjp, porque lo define el escritor. Tú también lo definirás. Mira el código sobre el código de prueba.
ismailarilik
Aparentemente no puedo leer. Gracias, @ismailarilik.
qaisjp