1

I'm using Ruby 2.4. Let's say I have an array of MyData instances. Each object has an attribute attr1. Given my array

array = [myobj1, myobj2, myobj3]

How do I check that every object in the array contains a nil value for attr1?

1
  • Do you want to filter out the objects with nil att1's? Commented Jan 17, 2017 at 16:04

3 Answers 3

7

Try something like this

my_arr.all? { |my_obj| my_obj.attr1.nil? }
Sign up to request clarification or add additional context in comments.

1 Comment

I like this answer better because of the readability. It is reading like exactly what it is supposed to do.
1

TL;DR

How do I check that every object in the array contains a nil value for attr1?

array
  .map(&:attr1) # maps by attr1
  .compact      # removes nil values from collection
  .empty?       # checks, whether array is empty

Note, that the below does not differentiate between false and nil values.

Enumerable#none?:

The method returns true if the block never returns true for all elements. If the block is not given, none? will return true only if none of the collection members is true.

It can be shortened to just

array.map(&:attr1).none?

Or even shorter (I bet this is the shortest one possible :D (without crazy metaprogramming))

array.none?(&:attr1)

which basically checks, whether none of the object.attr1 is truthy.

8 Comments

What about array.map(&:attr1).all?(&:itself)?
@Ursus awesome idea, absolutely
Cool, even better the last one :)
@ndn actually added this note before seeing your comment :)
@ndn my first solution does differentiate between nil and false, though
|
-1

The simplest thing is to use none? when you want to know if all values are nil:

[nil, nil].none? # => true
[true, nil].none? # => false

Or you can use any? and toggle the boolean result:

![nil, nil].any? # => true
![true, nil].any? # => false

Extending that to fit your object:

class MyData
  def initialize(b)
    @attr1 = b
  end

  def attr1
    @attr1
  end
end

obj1 = MyData.new(nil)
obj2 = MyData.new(true)

[obj1, obj1].none?(&:attr1) # => true
[obj1, obj2].none?(&:attr1) # => false

This will break down if you can have false values and want to consider them different than nil because none? considers nil and false values the same:

[false].none? # => true

(any? and all? also get fooled by the truthiness of a value.)

To work around that I'd add a method to the class that tests whether that attribute is actually nil:

def nil?
  @attr1.nil?
end

Then something using all? works:

obj3 = MyData.new(false)
[obj1, obj1].all?(&:nil?) # => true
[obj1, obj2].all?(&:nil?) # => false
[obj1, obj2, obj3].all?(&:nil?) # => false

Comments

Your Answer

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