In Progress
Unit 1, Lesson 1
In Progress

#one?

Video transcript & code

In the last episode, we used Ruby's exclusive-or operator to verify that exactly one of a pair of options was supplied.

def replace_var(text, var_name, value=nil)
  unless block_given? ^ value
    raise ArgumentError, 
          "Either value or block must be given, but not both"
  end
  text.gsub!(/\{#{var_name}\}/) { value || yield }
end

Some of you probably watched that episode and said "that's great Avdi, but what if I have more than two exclusive options?" You might have even experimented with stringing more than one XOR operator together, in which case you probably discovered that this doesn't work for asserting that only one operand is truthy.

true ^ true ^ false ^ false ^ true    # => true

So what do we do when we want to check that just one of an arbitrary set of values is truthy? For that case, Ruby gives us the #one? predicate method.

It returns false if there are no truthy elements in a collection:

[false, nil, nil].one?          # => false

…it also returns false if more than one member is truthy:

[42, nil, 'banana'].one?          # => false

But if exactly one element is truthy, it returns true:

[42, nil, false].one?          # => true

Of course, it's also true if there's just one element in the collection, and that element is truthy:

["just me"]                     # => ["just me"]

And it is always false for empty collections:

[].one?                         # => false

This makes it a succinct way to verify that a search returned one and only one result, so long as nil and false are not valid search results.

"waldo is at the waldorf".scan(/waldo/).one? # => false

#one? isn't just for counting truthy values. We can pass a block to it and have it look for any property of the collection elements.

[42, 85, 34, 6, 10].one?{|e| e.odd?} # => true

We can combine this with symbol-to-proc for some very concise code:

[42, 85, 34, 6, 10].one?(&:odd?) # => true

#one? is defined on Enumerable, so it's available on more than just arrays.

require 'set'
Set.new(["just me"]).one?         # => true

And with that we come to the end of our introduction to the #one? predicate. Happy hacking!

Responses