What general tips can you give for golfing in Ruby?
I'm looking for ideas that can be applied to code golf problems in general that are specific to Ruby. (For example, "Remove comments" would not be an answer.)
Please post one tip per answer.
Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. It only takes a minute to sign up.
Sign up to join this communityWhat general tips can you give for golfing in Ruby?
I'm looking for ideas that can be applied to code golf problems in general that are specific to Ruby. (For example, "Remove comments" would not be an answer.)
Please post one tip per answer.
You may be able to save 2 chars and use
[*(...)]
instead of
(...).to_a
For example, suppose we have a range that we want as an array:
(1..2000).to_a
Just do it like this:
[*1..2000] # Parentheses around the (ran..ge) is not needed!
And now you have your range as an array.
There is a whole bunch of predefined global variables that you can use instead of initialising new variables.
A very common example is that you have some golfed one-liner that loops, but you also need to keep a counter for later:
i=0
s.gsub!(/../){i+=1;"foo"}while s=~/.../
p i
So frustrating. Luckily, $.
comes to the rescue! It is initially 0
, and is incremented each time you read a line from input. This is of course incredibly useful if you actually need to keep track of the amount of lines you have read, but otherwise you can just manually update it:
s.gsub!(/../){$.+=1;"foo"}while s=~/.../
p$.
That's 3 bytes saved.
If you for example need to append your counter to a string s
, you may save some more bytes though string interpolation because the variable has a sigil:
i=0;i+=1;s+i.to_s
i=0;i+=1;s+"#{i}"
$.+=1;s+"#$."
Another interesting global is $:
. It's an alias for $LOAD_PATH
and is an array full of strings. I haven't actually used this in a golf yet, but I imagine it could come in handy if you need a cache for checking previously visited values or something, and don't care if it's completely empty. Your values are unlikely to crash with any of its initial contents anyways.
$.
as a counter like this in function submissions because of this consensus regarding function reuse. (I was guilty of doing so a couple of times before I became aware of that rule.)
\$\endgroup\$
while...end
If you need to break
out of the loop, while condition;code;end
will probably be shorter than loop{code;condition||break}
.
The ;
before end
is not always required, eg. while condition;p("text")end
until c;...;end
is equivalent to while !c;...;end
and 1 byte shorter.
Note that in most cases code while condition
and code until condition
are significantly shorter as they don't require the end
keyword and can often drop semicolons. Also, i+=1 while true
is equivalent to i+=1while true
and 1 byte shorter.
redo
When run, the redo
command jumps back to the beginning of the block it's in.
When using redo
in a lambda, you will have to move any setup variables to the arguments to avoid them being reset at every iteration (see examples).
Recursion can be shorter is some cases. For instance, if you're working on an array element by element, something like f=->s,*t{p s;t[0]&&f[*t]}
can be shorter than the alternatives depending on the stuff
.
Note that per the current consensus, if you're calling your function by name, you need to include the assignment (f=
) in the byte count making all recursive lambdas 2 bytes longer by default.
eval
If you need to run some code n
times, you can use eval"code;"*n
.
This will concatenate code;
n
times and run the whole thing.
Note that in most cases you need to include a ;
after your code.
1
to a
inclusive:->n{i=0;loop{p i+=1;i<n||break}} # 32 bytes
f=->n,i=1{i>n||p(i)&&f[n,i+1]} # 30 bytes
->n,i=0{p(i+=1)<n&&redo} # 24 bytes
->n{i=0;p i+=1while i<n} # 24 bytes
->n{i=0;eval"p i+=1;"*n} # 24 bytes
->n{n.times{|i|p i+1}} # 22 bytes # thanks to @benj2240
->n{n.times{p _1+1}} # 20 bytes # thanks to @AgentIvan
In this case, since the end-point is defined (n
), the n.times
loop is the shortest.
The redo
loop works because i+=1
modifies i and returns its new value and p(x)
returns x
(this is not true of print
and puts
).
g
and a number n
, find the first number strictly larger than n
for which g[n]
is truthy->g,n{loop{g[n+=1]&&break};n} # 29 bytes
f=->g,n{g[n+=1]?n:f[g,n]} # 25 bytes
->g,n{1until g[n+=1];n} # 23 bytes
->g,n{(n+1..).find &g} # 22 bytes
->g,n{g[n+=1]?n:redo} # 21 bytes
In this case, with an unknown end-point, redo
is the best option.
The (n+1..Inf)
loop is equivalent to simply loop
ing indefinitely but more verbose.
A 1
(or anything else) is required before the until
keyword to complete the syntax, using a number allows you to drop a space.
The eval
method is not viable in this case because there is neither a defined end-point nor an upper bound.
Update: with the new open ranges (n+1..Inf)
can be written simply as (n+1..)
, also .find{|x|g[x]}
is equivalent to .find &g
where g
is converted to a block.
TL;DR check out redo
, it can very often shave off a couple of bytes
.times
can often be even shorter for fixed loops, eg ->n{n.times{|i|p i+1}}
for 22 bytes
\$\endgroup\$
If the first line starts with #!
and contains ruby
, then Ruby will take command-line switches from it.
For example, if you require two libraries, you can golf
require'benchmark'
require'prime'
into
#!ruby -rbenchmark -rprime
which saves 7 bytes.
-rbenchmark -rprime
to the character/byte count. That's usual in choice golf.
\$\endgroup\$
a.push x
can be shortened to:
a<<x
for -4 bytes.
To subtract one (-1), instead of doing this
(i - 1) * 2
do the following
~-i * 2
which will save you 2 bytes in cases where you otherwise have to use brackets (e.g. arithmetics).
Array#assoc
/rassoc
When you have an array of arrays and want to find the sub-array that starts with a particular value, don't use Enumerable#find
, use Array#assoc
:
a = [[0,"foo"],[0,"bar"],[1,"baz"],[0,"qux"]]
a.find{|x,|x==1} # => [1,"baz"]
a.assoc(1) # => [1,"baz"]
This is also a good replacement for Enumerable#any?
in some situations.
Array#rassoc
does the same thing, but checks the sub-arrays' last element:
a = [[123,"good"],[456,"good"]]
a.any?{|*,x|x=="bad"} # => false
a.rassoc("bad") # => nil
a.any?
line in the rassoc
example, what does |x,|
do? How is it different from |x|
?
\$\endgroup\$
x=[1,2]
vs x,=[1,2]
. Using my example above, with |x|
, in the first iteration x
will be [0,"foo"]
. With |x,y|
, x
will be 0
and y
will be "foo"
. Likewise, with |x,|
, x
will be 0
. In other words, it says "put the first element in x
and throw the rest away.
\$\endgroup\$
|,y|
is a SyntaxError, ergo |_,y|
. But I've just now realized that |*,y|
works, which is cleaner than using a variable named _
(but no shorter).
\$\endgroup\$
There are many ways to do the same thing in ruby, and often times the default methods are inefficient - as a case study, a new arrays with set size and initialized to all 10
:
Array.new(20,10) # idiomatic
[10]*20
You may already be informed not to use this syntax, however, when you are initializing elements in an array that needs to not be shallow-cloned, for example 2D arrays:
[[]]*20 # beware, all 20 elements points to the one same array
Array.new(20){[]} # idiomatic
20.times.map{[]}
([0]*20).map{[]} # free ticket, you can move any line into `0` to save newline
([a,b,c,d]*5).map{[]} # saves a char when you move 4 1-time use expressions
(1..20).map{[]}
Many operations in ruby will create new objects. Sometimes this is to our advantage. For example, to create a new array with the same size as another array, we can get by without even knowing its .size
at all:
[0]*a.size # looks kinda nice
a.map{0} # -2 chars
(Note: the []*
version may be preferable in cases where you also want to reuse the value, so [0]*b=a.size
might end up helping you cut a char elsewhere)
To shallow clone an array:
b=a[0..-1] # makes sense
b=a.clone
b=a*1 # this returns new_ary
b=*a # this version for assignments - use [*a] in expressions
.clone
is an alias of .dup
([*a]
is still better for arrays)
\$\endgroup\$
Mar 1, 2018 at 12:13
eval
instead of reduce
While array.sum
is great and shorter than array.reduce(:+)
, there are no similar methods for the other operators *
, -
, /
...
Joining arrays and eval
ing them saves some space:
a = [1,2,3,4]
a.reduce(:*) # => 24
eval(a*?*) # => 24
^^ two bytes saved.
It also removes the need to map string input to numbers first:
b = "1 2 3 4".split
b.map(&:to_i).reduce(:*)
eval(b*?*)
^^^^^^^^^^^^^^
.tr' ',?*
is way more concise than .split*?*
...
\$\endgroup\$
Jan 29, 2018 at 6:33
a.reduce:*
is valid if brackets aren't required though I think it can always be replaced with eval a*?*
\$\endgroup\$
Mar 1, 2018 at 12:10
eval(a*?*)
is horrible. I like it!
\$\endgroup\$
Oct 10, 2020 at 16:31
Ruby keeps getting new features that look like they could be handy for golfing. Here are some that caught my eye. If there's any you think should be added feel free to edit or mention them in the comments.
(See also New features in Ruby 2.3 and 2.4.)
Here's an easy-to-read reference with all of the new features in Ruby 2.5: https://rubyreferences.github.io/rubychanges/2.5.html
(?~foo)
This matches any string that doesn't end with foo
. I don't have a specific use for this one off the top of my head, but here's a good article about it.
Enumerable#all?
, any?
, none?
, and one?
accept objects that respond to ===
Similar to Enumerable#grep
:
%w[Foo bar].one?(/[A-Z]/) # => true
%w[Foo bar].any?("bar") # => true
[1, 2, 3, 5, 7, 11].all?(0..10) # => false
Hash#slice
gets a subset of a HashI don't see a lot of Hashes in Ruby golf, but it might come in handy:
{foo: 1, bar: 2, baz: 3}.slice(:bar, :baz) # => {foo: 1, bar: 2}
All of the changes in Ruby 2.6: https://rubyreferences.github.io/rubychanges/2.6.html
1..
Lots of potential uses for this.
%w[a b c].zip("X"..).to_a
# => [["a", "X"], ["b", "Y"], ["c", "Z"]]
[1, 2, 3, 5, 7, 11].any?(10..)
# => true
String#split
takes a blockSaves four bytes when iterating over the result of String#split
, e.g.:
"foo;bar;baz".split(?;).map {|s| x << s.upcase if ... }
You can do this:
"foo;bar;baz".split(?;) {|s| x << s.upcase if ... }
Note that it still returns the original string, so it won't always be fewer bytes than map
.
>>
and <<
Procs now have >>
and <<
methods that let them be combined with other procs, or any object that responds to call
:
double = ->n{ n * 2 }
square = ->n{ n ** 2 }
(double >> square)[5] # => 100
(double << square)[5] # => 50
%
operator for RangeAn alias for Range#step
:
((1..10) % 2).to_a
# => [1, 3, 5, 7, 9]
Random.bytes
Could be handy.
Random.bytes(10)
# => "'\xB2\xD7\ny"
String#sub!
Instead of
a.sub!b,c
save a byte with
a[b]&&=c
Both return nil
if no substitution occurs. If a substitution does occur, be aware that the return values differ: a.sub!b,c
returns the modified string a
whereas a[b]&&=c
returns c
. However, given that you're using sub!
(as opposed to sub
) chances are that either:
a
in place and will be discarding the return value anyway; orThe shorter form meets your needs in both cases.
When working with arrays, .compact
can be replaced with -[nil]
to save 2 chars.
When getting input of fixed length the optional integer parameter to gets
may come in handy.
Getting a substring:
gets 10
vs gets[0,10]
-- saves 3 bytes
Cutting trailing newline:
gets 10
vs gets.chop
-- saves 3 - log10(required_length) bytes (= 2 in this case)
Strings are mutable in Ruby so if you have two variables holding the same string (a=b="string"
) and you modify one (a[0]="S"
), the other will also change (b=="String"
)
To clone a string:
a="string"
b=a.clone # 9 bytes
b=a.dup # 7 bytes
b=+-a # 5 bytes
b=-a # 4 bytes (b will be frozen)
Suppose, you've to choose only Integer
from
a = [1, 2, "3", 4.0, [5], "6".to_sym, 7r]
then you can do,
a.keep_if{|x|x.kind_of? Integer} # 32 bytes
a.keep_if{|x|x.is_a? Integer} # 29 bytes
a.select{|x|x.is_a? Integer} # 28 bytes
a.select{|x|Integer===x} # 24 bytes
a.grep(Integer) # 15 bytes
You can use Numeric
class in case you want numbers only.
Originally from histocrat.
Rather than writing
a.class==Array # 14 bytes
you could write
a.is_a? Array # 13 bytes
or even
a!=[*a] # 7 bytes
String split function uses $;
or space by default; if input is space-seperated, you can omit the argument.
# Reads 3 integers from line 1 of input file
x,y,z=gets.split.map &:to_i
Or, you can configure the behaviour of ARGF with $/
(if there is only one line):
# Reads 3 integers from input file
# Not seperated by newlines, so in case of:
# 1 2
# 3 4
# You will get: 1 2 4 ("2\n3".to_i -> 2)
$/=' '
x,y,z=$<.map &:to_i
Due to the predictable behaviour of .to_i
/ .to_f
, if you only have two numbers, you can use string.to_i
(/.to_f) to get the first number and substring after the non-digit (even simpler for space) for the two values. There are tricks if you already know the constraints on the length of the numbers and especially so if the first one is length 1-2:
v=gets # 1 2 / 11 22 / 11 22222222222222222222 / 123 4567890123456
v.to_i # 1* 11* 11* 123*
v[2,9].to_i # 2*#$ 22*#$ 22222222 3
v[2,99].to_i # 2#$ 22#$ 22222222222222222222*# 3
v[2..-1].to_i # 2# 22# 22222222222222222222*# 3
v[/\d+$/].to_i # 2 22 22222222222222222222 4567890123456
v[~/ /,9].to_i # 2$ 22$ 22222222 45678901
v[~/ /,99].to_i # 2 22 22222222 4567890123456*
v[~/ /..-1].to_i # 2 22 22222222 4567890123456
*: recommend to use in cases as specific than this
#: 1st int in string length is expected to be <=2 (-10 < x < 100)
$: 2nd int in string length is expected to be <=8 (if 1st int is length 1: <=9)
Note: [x,99]
will take 99
characters, while ..-1
goes all the way to end of string. Because ~/ /
matches the index of the space, this should be accounted for: [~/ /,9]
will get length 8 int at most, as first char is the space
Note: If there are three+ numbers, please just use split
or map
The above trick can be combined with $_
:
p *gets.to_i..$_[2,9].to_i
Handy ways to repeat strings:
# aaaaabcbcbcbcaaaaa,aaaaa,aaaaa
$><<?a*5+'bc'*4+[?a*5]*3*?,
Shorthand for size of input line:
p (eval gets.chars*?+)/~/$/
# is equivalent to:
p (eval gets.chars*?+)/$_.size
~//
and !//
to match against $_
(last line read)These can be used with the -n
or -p
flags.
~/regex/
is equivalent to $_=~/regex/
returning the index of the first match or nil
.
!/regex/
equivalent to $_!~/regex/
returning true
or false
.
!//
is weird as it doesn't call the !
method on the regex but only if you use a literal.!/regex/
matches the expression.
r=/regex/;!r
calls the !
(not
) method and always returns false
.
Also, !r=/regex/
gives an assignment in conditional
warning so there's some syntactic sugar going on.
To generate symmetric output, you can use a single range like (-x..x) and then map a function on the absolute value:
(-8..8).map{|x|x.abs}
Use short-circuit evaluation of boolean expressions to avoid if...else: x>1&&s+=x
To check if an integer array a
contains a number x
, you can use a-[x]!=a
, especially useful if x
is an expression.
Defining a variable as an accumulator is often shorter than using map and then reduce.
Use string interpolation with %
for alignment: "x%12s"%?x
is shorter than ?x+11*" "+?x
or for base conversions: "%b"%65
vs 65.to_s(2)
Use complex number for trigonometric functions: -1.arg
is PI, 1i.arg
is PI/2, (a+b*1i).arg
is Math.atan(b/a)
and works even when a and b are integers.
(-8..8).map &:abs
saves four characters (three if you need to surround the argument in parentheses for additional method chaining afterwards).
\$\endgroup\$
Mar 2 at 13:31
To check if a
is an Array
, instead of doing:
a.kind_of?(Array)
you can do
a.to_s['[']
Won't work if a
can be a string that contains [
.
Array===a
;)
\$\endgroup\$
Pre-defining your variables within a function can save you from calling particular arguments if you already know what they are and are calling the function multiple times.
def f(a,b,c)
return a+b+c
end
puts f(f(3,3,3),f(3,3,3),f(3,3,3))
# => 27
One can shorten this quite easily:
def f(a,b=3,c=3)
return a+b+c
end
puts f(f(3),f(3),f(3))
# => 27
If you do not assign any second or third arguments, then b=3
and c=3
automatically. One can shorten this further:
def f(a,b=a,c=a)
return a+b+c
end
puts f(f(3))
# => 27
Note however that you'll need to give it at least one variable, the first one, and I'm not quite sure how you can avoid assigning a value to b
while assigning a value to c
.
See here for more argument methods.
The <=>
(spaceship) operator compares two expressions and returns -1, 0, or 1. Applies to anything that can be compared with <
, >
, and ==
.
Some example uses:
x<=>0
is equivalent to the sign function.
x.*x<=>0
is the absolute value of x
.
Quick way to check if all items in a Ruby array are unique lists various ways to check whether everything in an array is unique. Unfortunately, none of them are methods that I can send to a receiver, I need to have my array on both sides of an operator. Lame!
Fortunately, uniq!
returns nil
if no changes are made, and returns the modified array if changes are made, meaning that it can be placed at the end of a pipeline to check for uniqueness;
x.method1.method2.method3.uniq! ? "not unique" : "everything is unique!"
Various other shouty methods have similar behaviour - returning the modified array if changes are made and nil
if no changes are made.