New features in Ruby 2.3 and 2.4
It's good to stay abreast of new language features that will help your golf game. There are a few great ones in the latest Rubies.
Ruby 2.3
The safe navigation operator: &.
When you call a method that might return nil
but you want to chain additional method calls if it's not, you waste bytes handling the nil
case:
arr = ["zero", "one", "two"]
x = arr[5].size
# => NoMethodError: undefined method `size' for nil:NilClass
x = arr[5].size rescue 0
# => 0
The "safe navigation operator" stops the chain of method calls if one returns nil
and returns nil
for the whole expression:
x = arr[5]&.size || 0
# => 0
Array#dig
& Hash#dig
Deep access to nested elements, with a nice short name:
o = { foo: [{ bar: ["baz", "qux"] }] }
o.dig(:foo, 0, :bar, 1) # => "qux"
Returns nil
if it hits a dead end:
o.dig(:foo, 99, :bar, 1) # => nil
Enumerable#grep_v
The inverse of Enumerable#grep
—returns all elements that don't match the given argument (compared with ===
). Like grep
, if a block is given its result is returned instead.
(1..10).grep_v 2..5 # => [1, 6, 7, 8, 9, 10]
(1..10).grep_v(2..5){|v|v*2} # => [2, 12, 14, 16, 18, 20]
Hash#to_proc
Returns a Proc that yields the value for the given key, which can be pretty handy:
h = { N: 0, E: 1, S: 2, W: 3 }
%i[N N E S E S W].map(&h)
# => [0, 0, 1, 2, 1, 2, 3]
Ruby 2.4
Ruby 2.4 isn't out yet, but it will be soon and has some great little features. (When it's released I'll update this post with some links to the docs.) I learned about most of these in this great blog post.
Enumerable#sum
No more arr.reduce(:+)
. You can now just do arr.sum
. It takes an optional initial value argument, which defaults to 0 for Numeric elements ([].sum == 0
). For other types you'll need to provide an initial value. It also accepts a block that will be applied to each element before addition:
[[1, 10], [2, 20], [3, 30]].sum {|a,b| a + b }
# => 66
Integer#digits
This returns an array of a number's digits in least-to-greatest significance order:
123.digits # => [3, 2, 1]
Compared to, say, 123.to_s.chars.map(&:to_i).reverse
, this is pretty nice.
As a bonus, it takes an optional radix argument:
a = 0x7b.digits(16) # => [11, 7]
a.map{|d|"%x"%d} # => ["b", "7"]
Comparable#clamp
Does what it says on the tin:
v = 15
v.clamp(10, 20) # => 15
v.clamp(0, 10) # => 10
v.clamp(20, 30) # => 20
Since it's in Comparable you can use it with any class that includes Comparable, e.g.:
?~.clamp(?A, ?Z) # => "Z"
String#unpack1
A 2-byte savings over .unpack(...)[0]
:
"".unpack(?U) # => [128123]
"".unpack(?U)[0] # => 128123
"".unpack1(?U) # => 128123
Precision argument for Numeric#ceil
, floor
, and truncate
Math::E.ceil(1) # => 2.8
Math::E.floor(1) # => 2.7
(-Math::E).truncate(1) # => -2.7
Multiple assignment in conditionals
This raises an error in earlier versions of Ruby, but is allowed in 2.4.
(a,b=1,2) ? "yes" : "no" # => "yes"
(a,b=nil) ? "yes" : "no" # => "no"
Someone needs to write a language called Rub, which uses a single Unicode character for every Ruby token, kinda like Jelly and Pyth :) – Mark Thomas – 2017-08-30T12:28:57.550