93

I've got two arrays of Tasks - created and assigned. I want to remove all assigned tasks from the array of created tasks. Here's my working, but messy, code:

    @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

I'm sure there's a better way. What is it? Thanks :-)

1
  • I bet under the hood the answer is doing just what you've coded there. Commented May 16, 2012 at 5:49

2 Answers 2

190

You can subtract arrays in Ruby:

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

ary - other_ary → new_ary Array Difference

Returns a new array that is a copy of the original array, removing any items that also appear in other_ary. The order is preserved from the original array.

It compares elements using their hash and eql? methods for efficiency.

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

If you need set-like behavior, see the library class Set.

See the Array documentation.

Sign up to request clarification or add additional context in comments.

9 Comments

Arg. Big face-palm moment for me. For some reason I thought that wouldn't work with objects. Worked just fine - thanks!
careful with this, test it in IRB first, for example: [5, 5, 5, 5] - [5, 5] = [] ... the subtraction removes the unique elements in the array.
Also note, this will not work: [1,2]-[1,2,3] => []. But [1,2,3]-[1,2] => [3]. Argh.
If you think in terms of subtraction then this last "gotchas" actually make sense. To subtract something you aren't asking for a diff... you are asking to subtract Y from X... if Y has something not even in X then the result is kind of 'undefined', hence the extra Y-element wouldn't be included in the X-result.
Specifically, Array#- is a set difference. It's more an inverse of Array#|, a set union, than it is of Array#+, concatenation (not a set operation at all!).
|
14

The above solution

a - b

deletes all instances of elements in array b from array a.

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

In some cases, you want the result to be [1, 2, 3, 3, 5]. That is, you don't want to delete all duplicates, but only the elements individually.

You could achieve this by

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

test

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]

The code works even when the two arrays are not sorted. In the example, the arrays are sorted, but this is not required.

2 Comments

delete_elements_in is not available in Ruby proper (ruby 2.6.3p62)
@qaisjp, because it is defined by the writer. You shall define it, too. Look at the code above the test code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.