This is part 3 of a series on Weird Ruby. Don’t miss Weird Ruby Part 1: The Beginning of the End, Weird Ruby Part 2: Exceptional Ensurance, and Weird Ruby Part 4: Code Pods (Blocks, Procs, and Lambdas).
Welcome back to Weird Ruby! This time we’re going to talk about Ruby’s rarely seen flip-flop operator and how you can use it to confuse and annoy future versions of yourself.
Have you ever longed for a way to execute part of a loop part of the time? Do you also feel like if/else statements are too “clear” and “understandable” for your clever code? Then the flip-flop operator is perfect for you!
A flip-flop is a range operator that compares two conditions inside of a loop. It evaluates to
true when the first condition is met until the second condition is met, after which it returns
Yes that’s about the best I can do, there isn’t a really easy way to explain this without showing you, so here we go:
Let’s say you’re out shopping for a hoverboard, and you don’t want to plunk down your credit card until you find one that’s totally rad. You’ve also decided that after finding a rad hoverboard if you see an ugly one you’re just going to stop caring. This is an ideal opportunity to do some flip-flops on your hoverboards.
hoverboards = [:blue, :pink, :rad, :lasers, :peuce, :ugly, :double_decker] hoverboards_to_buy_maybe =  hoverboards.each do |hoverboard| if (hoverboard == :rad)..(hoverboard == :ugly) hoverboards_to_buy_maybe << hoverboard end end
So let’s start looping over our 7 hoverboards. On the first pass, hoverboard is
:blue is not rad so the first conditional fails and we skip our if block; nothing gets added to
(:blue == :rad)..(:blue == :ugly) => false # hoverboards_to_buy_maybe: 
Next up we find the
:pink hoverboard, and while arguably an improvement over that creepy
:blue board we’re still not in rad territory, so we skip our
if block again and move on.
(:pink == :rad)..(:pink == :ugly) => false # hoverboards_to_buy_maybe: 
On the third pass through our loop we finally find our
:rad hoverboard. The flip-flop evaluates to true and we shovel
:rad into our
hoverboards_to_buy_maybe list. Because we’ve now seen a
:rad hoverboard our flip-flop operator will continue to return true until we meet the second condition.
(:rad == :rad)..(:rad == :ugly) => true # hoverboards_to_buy_maybe: [:rad]
Next up is our fourth hoverboard,
:lasers, and the flip-flop state is still true because we just met our first condition. The second
:ugly condition is not met, because a hoverboard made of lasers is an unimaginably beautiful thing, so the flip-flop state doesn’t change. We haven’t met the second condition to turn off our flip-flop, so it stays true and we shovel
:lasers right into
(:lasers == :rad)..(:lasers == :ugly) => true # hoverboards_to_buy_maybe: [:rad, :lasers]
Next up we find a
:peuce hoverboard, a poorly defined color that is sometimes hideous and other times not, but the important thing to us now is that it’s not exactly
:ugly. We shovel the
:peuce board into our list and since we still haven’t found the
:ugly board, our flip-flop remains true as we head into our sixth iteration.
(:peuce == :rad)..(:peuce == :ugly) => true # hoverboards_to_buy_maybe: [:rad, :lasers, :peuce]
In our sixth pass we finally find our
:ugly hoverboard and reach the absolute bottom of our bucket of cares. Since we’ve tripped our second
‘hoverboard == :ugly’ conditional our flip-flop is now set to
false. We shovel this final monstrosity into our array and move on with our shopping.
(:ugly == :rad)..(:ugly == :ugly) => false # hoverboards_to_buy_maybe: [:rad, :lasers, :peuce, :ugly]
In our seventh and final iteration we completely ignore the glory that is the
:double_decker hoverboard, as we’ve now decided we don’t care. If this board happened to meet our first condition, our flip-flop would have gone back to true, we would have executed our
if block and shoveled the
hoverboards_to_buy_maybe. It’s probably for the best, double decker hoverboards sound incredibly dangerous.
When our loop is finished
hoverboards_to_buy_maybe looks like this:
[:rad, :lasers, :peuce, :ugly]
To recap, we iterated until the first condition in our flip-flop was met, skipping the
if block each time. Once we met the first condition, the flip-flop state became true, so we started adding things to our list. We continued iterating and executing our
if block each time until we met the second condition, after which the flip-flop state was set to
false and we transformed back into an apathetic lump.
Two-dot, three-dot, red-dot, blue-dot
The flip-flop operator itself is actually a range of conditionals, where the range only returns
false: true once the first condition is met and until the second condition is met, then false until the first condition is met again.
As you may know there are two types of ranges in Ruby: the two-dot and the three-dot. In our example we’re using the two-dot version, which evaluates both of the conditionals for a given iteration. So if we meet our first condition and our second condition in a single pass, the flip-flop will finish the loop set to
false, though it will execute the body of the
if block exactly once.
To demonstrate let’s change our flip-flop so both conditionals check for
hoverboards.each do |hoverboard| if (hoverboard == :rad)..(hoverboard == :rad) hoverboards_to_buy_maybe << hoverboard end end
When we make our third iteration and hoverboard is
:rad we will meet the first condition and set the flip-flop state to true. We’ll add the
:rad hoverboard to
hoverboards_to_buy_maybe because the state is now set to
true, and afterwards we’ll check the second condition. The second condition also evaluates to
true, so we change the flip-flop state back to
false and we move to our next iteration. Since we never find
:rad again the flip-flop state never returns to
true and our final
boards_to_buy_maybe looks like this:
We met the first condition, executed the block and met the second condition in a single iteration.
The three-dot version of this same flip-flop behaves quite differently, evaluating only one of the conditionals for each iteration:
(hoverboard == :rad)...(hoverboard == :ugly)
The flip-flop starts out set to
false, so until we find
:rad it will remain
false. We only check the first condition with the three-dot until we find the
:rad hoverboard and the flip-flop state changes to
true. If we don’t meet the first condition we never evaluate the second condition.
Once we’ve met the first condition our flip-flop is
true and we will stop evaluating the first condition until the second is met. So on each iteration we now check only for the
When we find the
:ugly hoverboard the second condition evaluates to
true and the flip-flop is set back to
false. We stop evaluating the second condition and continue through our loop evaluating only the first condition, which in our case never evaluates to
The three-dot change in behavior is especially obvious when both conditions are the same:
(hoverboard == :rad)...(hoverboard == :rad) # hoverboards_to_buy_maybe: [:rad]
When we find
:rad and the first condition evaluates to
true, we execute the body of our
if block, adding
boards_to_buy_maybe. Unlike the two-dot version we do not evaluate the second condition on this iteration, so our flip-flop state remains
On our next iteration hoverboard is now equal to
(:lasers == :rad)...(:lasers == :rad) # hoverboards_to_buy_maybe: [:rad, :lasers]
We don’t evaluate the first condition at all since the flip-flop state is
true. Instead we check the second condition, which evaluates to
false, and we go on our merry way. Since we don’t have another
:rad in our hoverboards we will never meet the second condition, and the flip-flop stays
true until the end of the loop.
We end up with this in our final
[:rad, :lasers, :peuce, :ugly, :double_decker]
:rad and flipped to
true but didn’t check the second condition, because three-dot flip-flops check only one condition on each iteration. By the time we came back again it was too late to find a
:rad hoverboard so we never tripped our second condition and the flip-flop remained
true all the way down.
Flipped and flopped
To review, the two-dot version of the flip-flop operator evaluates both conditions on each iteration. The three-dot version will evaluate only a single condition on each pass: the first condition if the flip-flop state is
true and the second condition if the flip-flop state is
false. You’ll need to decide which flip-flop is the right one for your particular hoverboard shopping use case.
I hope that this “sometimes I care” hoverboard example has made clear the value of Ruby’s flip-flop operator, because I have no idea why anyone would actually want to use this thing in the real world.
I assume that someone made up the flip-flop operator for a reason, so if you have a good application for flip-flop operators please leave a comment in the New Relic community forum. I am incredibly curious.
if (true)..(false) puts “<3 Jonan" end