0

In Rails, I want to check if an array is empty(arr = []).

!arr&.any? 
arr&.empty?

Which is more correct?

Remy Wang
  • 666
  • 6
  • 26
  • Your first sentence is clear but you have confused some readers about what you want to do by your use of the Safe Navigation operator. Perhaps you could add an example (including the desired result) to clarify your question. More generally, why did you choose to use the Safe Navigation operator? – Cary Swoveland Dec 30 '19 at 18:04
  • I think arr.empty? is correct to check if the array is empty and arr.any? is correct to check if the array is not empty. – Remy Wang Dec 30 '19 at 18:12

5 Answers5

7

Take a look at the following Ruby:

[1, 2].any? => true
[nil, false].any? => false
[nil, nil].any? => false

[1, 2].empty? => false
[nil, nil].empty? => false

Enumerable#any? is a method that asks "is there anything in this collection that’s true?" In Ruby, the existence of an object is considered true; so calling #any? without a block is saying is there something that’s not nil or false in this collection?.

For more information see "any? != ! empty?"

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
code_aks
  • 1,972
  • 1
  • 12
  • 28
  • 1
    "...the existence of an object is considered true..." requires clarification. You might tweak your example by writing `[nil, false].any? => false`. – Cary Swoveland Dec 30 '19 at 19:10
3

In Ruby on Rails I would use blank?. From the docs:

An object is blank if it's false, empty, or a whitespace string. For example, nil, '', ' ', [], {}, and false are all blank.

array = []
array.blank? 
#=> true

Note that blank? only works in Ruby on Rails, not in plain Ruby and that it has a slightly different behavior then empty?. Have a look at this comparision about the responses or errors you might get from the different methods.

spickermann
  • 100,941
  • 9
  • 101
  • 131
3

If you want to check that arr contains no elements then arr.empty? is correct.

If you want to also accept nil or false as empty then !arr || arr.empty? is correct. In Ruby on Rails you can use arr.blank? instead.

arr&.empty? does the opposite of what you intend. nil&.empty? is false when you want the result from nil to be true. If arr is nil the safe navigation operator &. will return nil. Rubocop will catch this with Lint/SafeNavigationWithEmpty.

Schwern
  • 153,029
  • 25
  • 195
  • 336
  • The OP’s first sentence makes clear what is desired (especially “`arr = []`”). You and others giving answers refer to `nil`, `false`, `blank`, but none of that is relevant. I upvoted for your first sentence but suggest that you remove the “if clause” in that sentence, explain why you think ‘empty?` is preferred to alternatives and ditch the rest. – Cary Swoveland Dec 30 '19 at 09:16
  • 1
    @CarySwoveland Sometimes what people ask for and what they need are not quite the same. They say they're checking for `[]` but they're using safe navigation, so it seems they want `nil` to be empty. Rails offers `blank?` to handle that. `arr&.empty?` is clearly incorrect and that needed to be addressed (I got bit by this myself just recently). Now they have options and they can decide which is appropriate, we don't have enough context to decide for them. – Schwern Dec 30 '19 at 15:32
1

You wish to determine if an array arr is empty; that is, if arr equals the empty array []. Here are some ways of doing that1:

arr == []
arr.size == 0
arr.empty?

Of these, arr.empty? is normally the method of choice, if for no other reason than it reads best.

When choosing one method among several that work you should consider what would happen if there were a bug in your code resulting in arr holding an object that is not an array. In particular, many bugs could result in arr holding nil. Should that occur arr == [] is problematic because it would return false and merrily carry on, masking the bug from detection at that point.

nil.size == 0 and nil.empty? both raise exceptions (because nil does not have a method size or empty?), which is what you want.

You certainly do not want to use the Safe Navigation operator as it would serve only to mask errors when arr is nil:

arr = nil
arr&.size
  #=> nil
arr&.empty?
  #=> nil

Lastly arr.size == 0 has the weakness that the coder may inadvertently write arr.size = 0, particularly if playing Call of Duty while coding (though arr.size.zero? would be good practice as it overcomes that potential problem).

To sum up, it is difficult to find an argument for using something other than arr.empty? to determine if an array is empty.

1 The question suggests the use of the method Array#any?. That doc references Enumerable#any?, which makes clear that, for example, [nil].any? returns false, yet [nil] is not empty. Hence, any? cannot be used to determine if an array is empty.

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
0

Since you've tagged the question with , I'm adding an answer based upon an active record collection.

Since everything other than nil and false is considered truthy. For active record collections this means that the behaviour would be the same for records.any? and !records.empty?. This is also visible when looking at the source for ActiveRecord::Relation:

# Returns true if there are any records.
def any?
  return super if block_given?
  !empty?
end

So assuming there is no block given and you're working with an ActiveRecord::Relation:

User.active.any?
# results in the same result as
!User.active.empty?
3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
  • 2
    While this is true, the subtle difference in behavior between `ActiveRecord::Relation#any?` and `Enumerable#any?` invites confusion. It's very common to swap Relations for Arrays and other Enumerables. I would recommend steering away from using `any?` to mean `!empty?` and perhaps use `present?`. – Schwern Dec 30 '19 at 15:48
  • 1
    I agree, that there is a subtle difference. However, there is also a subtle difference between `any?` and `present?` for `ActiveRecord::Relation`. `any?` will use a count query if the records aren't loaded yet, but doesn't if the collection is already loaded. `present?` will load the full collection if not already loaded instead of using a count query. I do understand that this answer doesn't fully fit, since OP specifically asked about arrays. However I feel the need to still mention it, since it's related to the [tag:ruby-on-rails] tag. – 3limin4t0r Jan 01 '20 at 17:19
  • For those interested in the differences between `ActiveRecord::Relation` query methods, I've listed the differences in a [comment under an Rails style guide issue](https://github.com/rubocop-hq/rails-style-guide/issues/232#issuecomment-425848425). – 3limin4t0r Jan 01 '20 at 17:25