40

Being new to Ruby, I have a question about the difference between the .reject! and .delete_if methods when dealing with hashes and arrays. If just wanting to get rid of certain objects, is there functionally any difference between the methods? And reason to use one over the other?

Thanks!

EDIT I have read the documentation...I guess I should have been more clear in my original question. I was wondering more about differences in efficiency. Do they operate differently in how they delete items? (Again, ignoring return value. I understand that is a difference. Thanks!)

loganhasson
  • 1,150
  • 2
  • 10
  • 16
  • @rid so does `delete_if` – John Dvorak Sep 20 '13 at 16:41
  • 3
    Haven't you tried reading the documentation? It is quite explicit about this: [`Hash#reject!`](http://www.ruby-doc.org/core-2.0.0/Hash.html#method-i-reject-21) and [`Array#reject!`](http://www.ruby-doc.org/core-2.0.0/Array.html#method-i-reject-21). – toro2k Sep 20 '13 at 16:46

3 Answers3

69
  • reject - create a new array without elements that match and return the new array
  • delete_if - delete elements that match from current array and return the array
  • reject! - delete elements that match from current array. Return the array if something was rejected, or nil when none.
Jan Zerebecki
  • 825
  • 7
  • 18
Nowaker
  • 12,154
  • 4
  • 56
  • 62
  • 2
    Thank you for the organized answer. Makes it so much easier than parsing what other people write inline. – Joshua Pinter May 03 '15 at 03:33
  • `reject` always returns an *Array*, while `delete_if` and `reject!` returns the original object (which could have been a *Set*, for example). – go2null Jan 16 '16 at 00:59
21

The documentation is pretty clear about this.

The difference is that if reject! does not change the array, it returns nil. delete_if will return the unchanged array.

Explosion Pills
  • 188,624
  • 52
  • 326
  • 405
  • @JanDvorak "...but returns nil if no changes were made." Looks like the documentation includes the return value to me. – Explosion Pills Sep 20 '13 at 16:51
  • 2
    Oops, misread. Deleting my answer and upvoting yours. Also, tested in jRuby 1.7.3 (`["a"].delete_if{false}` returns the array while `["a"].reject!{false}` returns `nil`) – John Dvorak Sep 20 '13 at 16:53
  • @ExplosionPills Thanks. I had seen that in the documentation, but I guess I was asking more in terms of efficiency. Ignoring return value, I'm wondering if one is more efficient at deleting items? – loganhasson Sep 20 '13 at 20:36
  • I would recommend the http://ruby-doc.org as a link to documentation instead of the http://apidock.com you linked. http://ruby-doc.org/core-2.3.0/Hash.html for the hashes, http://ruby-doc.org/core-2.3.0/Array.html for the arrays. – Jean Bob Feb 22 '16 at 15:12
  • @ExplosionPills : The explanation is not correct! `reject!` **does** change the array, as you can see by doing `a=[1,2,3,4]; a.reject! {|i| i==2 }; puts a`. – user1934428 Feb 23 '21 at 12:19
  • 2
    @user1934428 you might have misread; I said _if_ `reject!` does not change the array... Try `a=[1,2,3,4]; a.reject! {|i| i == 5 };` – Explosion Pills Feb 23 '21 at 14:35
  • Right! I now see it. – user1934428 Feb 24 '21 at 07:28
7

tl;dr: delete_if seems slightly faster. However, the main consideration for choosing a method is the difference in the return value, as pointed out in the other answers.

Since you clarified that your question is concerning efficiency, I did a few tests:

> time { (1..100000).to_a.reject!{ |n| n % 5 == 0 } }
  0.390000   0.000000   0.390000 (  0.394596)
> time { (1..100000).to_a.delete_if{ |n| n % 5 == 0 } }
  0.400000   0.000000   0.400000 (  0.399122)

> time { (1..200000).to_a.reject!{ |n| n % 5 == 0 } }
  1.650000   0.000000   1.650000 (  1.657927)
> time { (1..200000).to_a.delete_if{ |n| n % 5 == 0 } }
  1.630000   0.010000   1.640000 (  1.637719)

> time { (1..300000).to_a.reject!{ |n| n % 5 == 0 } }
  3.700000   0.000000   3.700000 (  3.717206)
> time { (1..300000).to_a.delete_if{ |n| n % 5 == 0 } }
  3.660000   0.000000   3.660000 (  3.686213)

> time { (1..400000).to_a.reject!{ |n| n % 5 == 0 } }
  7.320000   0.020000   7.340000 (  7.361767)
> time { (1..400000).to_a.delete_if{ |n| n % 5 == 0 } }
  7.190000   0.020000   7.210000 (  7.239549)

So, looks like that beyond a certain size delete_if is slightly faster. time is defined as:

def time(&block)
  puts Benchmark.measure(&block)
end

The numbers represent user CPU time, system CPU time, the sum of the user and system CPU times, and the elapsed real time, respectively. You can find here an explanation of their meanings. Note that as in every benchmark, YMMV, and you should test on your specific workflows rather than my contrived example of removing numbers that are multiples of 5.

Community
  • 1
  • 1
dimid
  • 7,285
  • 1
  • 46
  • 85
  • The concern about efficiency of two similar built-ins is a red herring. And you didn't really prove anything with this test. – Mark Thomas Feb 21 '16 at 16:34
  • Why is it a "red herring"? The OP "was wondering more about differences in efficiency" – dimid Feb 21 '16 at 16:36
  • It's a red herring because nobody should choose between the two methods based on fractions of a second of execution speed. I would really hate to see the code of someone who made this the primary determination of their coding style. – Mark Thomas Feb 21 '16 at 16:42
  • Right, but it's irrelevant, since the OP has read the docs and is fully aware of the semantic difference. Nevertheless, s/he asked specifically about **efficiency** rather than general guidelines when to use what. – dimid Feb 21 '16 at 16:46
  • Maybe we agree to disagree? I believe if an OP has a misplaced concern then it is our duty to point it out. – Mark Thomas Feb 21 '16 at 16:51
  • 1
    And I've removed my downvote. I still feel that the real answer is no, there is no significant difference and certainly no performance-related reason to choose between the two. I challenge anyone to find a real-world example where it makes any real difference. – Mark Thomas Feb 21 '16 at 17:00