72
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ Someone needs to write a language called Rub, which uses a single Unicode character for every Ruby token, kinda like Jelly and Pyth :) \$\endgroup\$ Aug 30 '17 at 12:28

53 Answers 53

53
\$\begingroup\$
  • The numbers 100 to 126 can be written as ?d to ?~ in 1.8.
  • On a similar note if you need a single-character string in 1.9 ?x is shorter than "x".
  • If you need to print a string without appending a newline, $><<"string" is shorter than print"string".
  • If you need to read multiple lines of input $<.map{|l|...} is shorter than while l=gets;...;end. Also you can use $<.read to read it all at once.
  • If you're supposed to read from a file, $< and gets will read from a file instead of stdin if the filename is in ARGV. So the golfiest way to reimplement cat would be: $><<$<.read.
\$\endgroup\$
7
  • 1
    \$\begingroup\$ ?x yields the ascii code in general, so you can realistically get all the printables to digits in two characters. 1.9 is different, 'a'.ord yields the ascii number, but is four bytes longer than the decimal version. \$\endgroup\$
    – Hiato
    Feb 3 '11 at 8:03
  • 8
    \$\begingroup\$ An even golfier way to implement cat is to leave the ruby file completely empty (0 bytes) and insist that it should be run from the command line with the -p flag. \$\endgroup\$
    – daniero
    Jul 17 '13 at 20:01
  • 1
    \$\begingroup\$ or, from @daniero's own answer, puts *$< \$\endgroup\$ Jul 2 '14 at 19:15
  • 1
    \$\begingroup\$ So in 1.8, all I have to do is go ?~ and it will return 126? \$\endgroup\$ May 13 '17 at 13:09
  • 6
    \$\begingroup\$ You can go beyond 126 using thinks like or , or if you are crazy enough: ?﷽.ord=65021 \$\endgroup\$ May 13 '17 at 15:45
36
\$\begingroup\$

Use the splat operator to get the tail and head of an array:

head, *tail = [1,2,3]
head => 1
tail => [2,3]

This also works the other way:

*head, tail = [1,2,3]
head => [1,2]
tail => 3

Use the * method with a string on an array to join elements:

[1,2,3]*?,
=> "1,2,3"
\$\endgroup\$
31
\$\begingroup\$
  • Use abort to terminate the program and print a string to STDERR - shorter than puts followed by exit
  • If you read a line with gets, you can then use ~/$/ to find its length (this doesn't count a trailing newline if it exists)
  • Use [] to check if a string contains another: 'foo'['f'] #=> 'f'
  • Use tr instead of gsub for character-wise substitutions: '01011'.tr('01','AB') #=> 'ABABB'
  • If you need to remove trailing newlines, use chop instead of chomp
\$\endgroup\$
3
  • 2
    \$\begingroup\$ +1 for abort and ~/$/ \$\endgroup\$
    – J-_-L
    Jun 26 '11 at 22:13
  • \$\begingroup\$ Please explain how to use ~/$/ \$\endgroup\$ Nov 25 '18 at 6:47
  • \$\begingroup\$ @MathieuCAROFF every time you call gets, its result is stored in the $_ variable. /regex/ ~= string returns the index of the first match. Calling ~ on a regex is equivalent to /regex/ ~= $_. So it would be something like s=gets;l= ~/$/ \$\endgroup\$
    – Cyoce
    Feb 8 '19 at 3:18
23
\$\begingroup\$

End your end.

Try to remove end from your code.

Don't use def...end to define functions. Make a lambda with the new -> operator in Ruby 1.9. (The -> operator is a "stabby lambda", or "dash rocket".) This saves 5 characters per function.

# 28 characters
def c n
/(\d)\1/=~n.to_s
end

# 23 characters, saves 5
c=->n{/(\d)\1/=~n.to_s}

Method calls are c n or c(n). Lambda calls are c[n]. Changing each c n to c[n] costs 1 character, so if you can use c n more than 5 times, then keep the method.

All methods that take do...end blocks can take {...} blocks instead. This saves 3 to 5 characters. If the precedence of {...} is too high, then use parentheses to fix it.

# 48 characters
(?a..?m).zip (1..5).cycle do|a|puts a.join','end

# WRONG: passes block to cycle, not zip
(?a..?m).zip (1..5).cycle{|a|puts a.join','}

# 45 characters, saves 3
(?a..?m).zip((1..5).cycle){|a|puts a.join','}

Replace if...else...end with the ternary operator ?:. If a branch has two or more statements, wrap them in parentheses.

# 67 characters
if a<b
puts'statement 1'
puts'statement 2'else
puts'statement 3'end

# 62 characters, saves 5
a<b ?(puts'statement 1'
puts'statement 2'):(puts'statement 3')

You probably don't have while or until loops, but if you do, then write them in modifier form.

(a+=1
b-=1)while a<b
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Are the parentheses around puts'statement 3' necessary? \$\endgroup\$
    – Cyoce
    Dec 2 '16 at 2:38
20
\$\begingroup\$

Addition to w0lf

When working with arrays, .compact can be replaced with -[nil] to save 2 chars.

Combined with above -> you can make it even shorter with -[p] to save another 2 chars.

\$\endgroup\$
16
\$\begingroup\$

Don't use the true and false keywords.

Use:

  • !p for true (thanks, histocrat!)
  • !0 for false. If all you need is a falsy value, then you can simply use p (which returns nil).

to save some chars.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Unless you actually need true (i.e. if a truthy value is enough, like in an if condition), you don't even need !!. \$\endgroup\$ Aug 6 '14 at 6:31
  • 6
    \$\begingroup\$ And similarly, p (which evaluates to nil) is a shorter falsey value. Which means the shortest way to get true is !p. \$\endgroup\$
    – histocrat
    Aug 6 '14 at 22:17
  • \$\begingroup\$ @histocrat good point! I've edited my answer. \$\endgroup\$ Aug 7 '14 at 6:26
16
\$\begingroup\$

Use the short predefined variables wherever possible, e.g. $* instead of ARGV. There's a good list of them here, along with a lot of other useful information.

\$\endgroup\$
15
\$\begingroup\$

Use operator methods instead of parentheses

Let's say you want to express a*(b+c). Because of precedence, a*b+c won't work (obviously). Ruby's cool way of having operators as methods comes to the rescue! You can use a.*b+c to make the precedence of * lower than that of +.

a*(b+c) # too long
a*b+c   # wrong
a.*b+c  # 1 byte saved!

This can also work with the ! and ~ operators (things like unary + or unary - don't work because their methods are -@ and +@, saving () but adding .@)

(~x).to_s # too long
~x.to_s   # error
x.~.to_s  # 1 byte saved!
\$\endgroup\$
14
\$\begingroup\$

When you are using string interpolation, (as you should pr Martin Büttner's post), you don't need the curly brackets if your object has a sigil ($, @) in front of it. Useful for magical variables like $_, $&, $1 etc:

puts "this program has read #$. lines of input"

So also if you need to print a variable more than you use it otherwise, you may save some bytes.

a=42; puts "here is a: #{a}"; puts "here is a again: #{a}"
$b=43; puts "here is b: #$b"; puts "here is b again: #$b"
\$\endgroup\$
2
  • \$\begingroup\$ Isn't puts "here is b: "+b more shorter? \$\endgroup\$
    – vrintle
    Dec 16 '20 at 7:53
  • 1
    \$\begingroup\$ @vrintle In this particular example, yes. But for instance "here is b:#{b}, here is a:#{a}!!" is shorter than "here is b:"+b+", here is a:"+a+"!!". The linked post also provides more info \$\endgroup\$
    – daniero
    Dec 16 '20 at 22:14
13
\$\begingroup\$

Build arrays using a=i,*a to get them in reverse order. You don't even need to initialize a, and if you do it doesn't have to be an array.

\$\endgroup\$
13
\$\begingroup\$

$_ is last read line.

  • print - if no argument given print content of $_
  • ~/regexp/ - short for $_=~/regexp/

In Ruby 1.8, you have four methods in Kernel that operate on $_:

  • chop
  • chomp
  • sub
  • gsub

In Ruby 1.9, these four methods exist only if your script uses -n or -p.

If you want to print some variable often then use trace_var(:var_name){|a|p a}

\$\endgroup\$
4
  • 2
    \$\begingroup\$ These are only available when you run Ruby with the -p or -n option. Reference. \$\endgroup\$ Dec 27 '13 at 19:12
  • 1
    \$\begingroup\$ It seems that trace_var only works with global $variables \$\endgroup\$
    – daniero
    Mar 23 '16 at 8:56
  • 1
    \$\begingroup\$ Another trick that works with -n or -p is using a regexp literal as a boolean: the regexp is implicitly matched against $_. See this answer of mine for an example. \$\endgroup\$
    – Dingus
    Sep 4 '20 at 0:30
  • \$\begingroup\$ Unlike the String methods with the same names, the listed Kernel methods modify $_ in place. Generally this behaviour is what is wanted, but worth noting the difference. \$\endgroup\$
    – Dingus
    Nov 30 '20 at 11:50
12
\$\begingroup\$

If you need to find if a particular element e is inside a range r, you can use

r===e

instead of the longer:

r.cover?(e) # only works if `r.exclude_end?` is false

or

r.member?(e)

or

r.include?(e)
\$\endgroup\$
3
  • 3
    \$\begingroup\$ Isn’t r===e even shorter? \$\endgroup\$
    – akuhn
    Jun 1 '12 at 21:10
  • \$\begingroup\$ @akuhn Yes, it is. Much Shorter. Thanks for pointing that out, it helped me shorten my code by 10 chars, which is huge: codegolf.stackexchange.com/a/6125/3527 \$\endgroup\$ Jun 1 '12 at 21:20
  • 1
    \$\begingroup\$ You’re welcome. Everything that can be used in a switch statement has === implemented. \$\endgroup\$
    – akuhn
    Jun 2 '12 at 12:49
12
\$\begingroup\$

Avoid length in if a.length<n

length is 6 bytes, a bit costly in code golf. in many situations, you can instead check if the array has anything at a given point. if you grab past the last index you will get nil, a falsey value.

So you can Change:

if a.length<5 to if !a[4] for -5 bytes

or

if a.length>5 to if a[5] for -6 bytes

or

if a.length<n to if !a[n-1] for -3 bytes

or

if a.length>n to if a[n] for -6 bytes

Note: will only work with an array of all truthy values. having nil or false within the array may cause problems.

\$\endgroup\$
1
  • 5
    \$\begingroup\$ I always use size… But this is definitely better. BTW, works for String too. \$\endgroup\$
    – manatwork
    Dec 7 '15 at 17:06
12
\$\begingroup\$

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"
\$\endgroup\$
5
  • \$\begingroup\$ Golf Math::E.ceil(1) to Math::E.ceil 1, and likewise for floor and truncate. \$\endgroup\$ Nov 20 '17 at 23:38
  • 1
    \$\begingroup\$ @SimplyBeautifulArt I expect that someone golfing in Ruby will be able to make that leap themselves. \$\endgroup\$
    – Jordan
    Nov 20 '17 at 23:50
  • \$\begingroup\$ For Enumerable#sum, .flatten.sum is 2 bytes shorter than .sum{|a,b|a+b} \$\endgroup\$ Jan 24 '18 at 10:45
  • \$\begingroup\$ (-Math::E).truncate(1) is equivalent to -Math::E.truncate(1) which is 1 byte shorter \$\endgroup\$ Mar 1 '18 at 12:21
  • 2
    \$\begingroup\$ &. can be used with subscripting like this a&.[]i (1 byte shorter than a&.at i). Although, if brackets are required, a||a[i] is 1 byte is shorter than a&.[](i) or a&.at(i) \$\endgroup\$ Mar 1 '18 at 12:24
11
\$\begingroup\$

Use string interpolation!

  1. To replace to_s. If you need parentheses around whatever you want to turn into a string, to_s is two bytes longer than string interpolation:

    (n+10**i).to_s
    "#{n+10**i}"
    
  2. To replace concatenation. If you concatenate something surrounded by two other strings, interpolation can save you one byte:

    "foo"+c+"bar"
    "foo#{c}bar"
    

    Also works if the middle thing is itself concatenated, if you just move the concatenation inside the interpolation (instead of using multiple interpolations):

    "foo"+c+d+e+"bar"
    "foo#{c+d+e}bar"
    
\$\endgroup\$
11
\$\begingroup\$

If you ever need to get a number from ARGV, get, or something similar to do something that many times, instead of calling to_i on it, you can just use ?1.upto x{do something x times} where x is a string.

So using ?1.upto(a){} instead of x.to_i.times{} will save you 2 characters.

You can also re-write things like p 1 while 1 or p 1 if 1 as p 1while 1 or p 1if 1

That example isn't very useful, but it could be used for other things.

Also, if you need to assign the first element of an array to a variable, a,=c will save two characters as opposed to a=c[0]

\$\endgroup\$
10
\$\begingroup\$

Don't use #each. You can loop over all elements just fine with #map. So instead of

ARGV.each{|x|puts x}

you can do the same in less bytes.

ARGV.map{|x|puts x}

Of course, in this case puts $* would be even shorter.


There are literals for rational and complex numbers:

puts 3/11r == Rational(3,11)
puts 3.3r == Rational(66,20)
puts 1-1.i == Complex(1,-1)

=> true
true
true

You can use most bytes within strings. "\x01" (6 bytes) can be shortened to "" (3 bytes). If you only need this one byte, this can be shortened even further to ? (2 bytes).

By the same token, you can get newlines shorter like this:

(0..10).to_a.join'
'

 => "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"

You can use ?\n and ?\t as well, which is one byte shorter than "\n" and "\t". For obfuscation, there also ?\s, a space.


Use constants instead of passing arguments around, even if you need to change them. The interpreter will give warnings to stderr, but who cares. If you need to define more variables related to each other, you can chain them like this:

A=C+B=7+C=9

=> A=17, B=16, C=9

This is shorter than C=9;B=16;A=17 or C=0;B=C+7;A=C+B.


If you need an infinite loop, use loop{...}. Loops of unknown length may be shorter with other loops:

loop{break if'
'==f(gets)}

while'
'!=f(gets);end

Some more gsub/regexp tricks. Use the special '\1' escape characters instead of a block:

"golf=great short=awesome".gsub(/(\w+)=(\w+)/,'(\1~>\2)')

"golf=great short=awesome".gsub(/(\w+)=(\w+)/){"(#{$1}~>#{$2})")

And the special variables $1 etc. if you need to perform operations. Keep in mind they are defined not only inside the block:

"A code-golf challenge." =~ /(\w+)-(\w+)/
p [$1,$2,$`,$']

=> ["code", "golf", "A ", " challenge."] 

Get rid of spaces, newlines, and parentheses. You can omit quite a bit in ruby. If in doubt, always try if it works without, and keep in mind this might break some editor syntax highlighting...

x+=1if$*<<A==????::??==??
\$\endgroup\$
3
  • \$\begingroup\$ "Please post one tip per answer." Also ?\n is nice, but not really shorter than actually putting a newline character inside quotes. (same for tab) \$\endgroup\$ May 7 '15 at 23:29
  • \$\begingroup\$ And puts$* is even shorter. \$\endgroup\$
    – Cyoce
    Nov 7 '17 at 5:20
  • \$\begingroup\$ I know you were trying to prove a point but I'm pretty sure that last example is the same as x+=1;$*<<A \$\endgroup\$ Mar 1 '18 at 14:03
10
\$\begingroup\$

Save some bytes when removing repeated elements of an array

a.uniq # before
a|[]   # after
    ^^

If you will be using an empty array [] in a variable, you can save even more bytes:

a.uniq;b=[] # before
a|b=[]      # after
      ^^^^^
\$\endgroup\$
1
  • 7
    \$\begingroup\$ For the first case, a&a is 1 byte shorter \$\endgroup\$ Mar 7 '18 at 12:59
8
\$\begingroup\$

Scientific notation can often be used to shave off a char or two:

x=1000
#versus
x=1e3
\$\endgroup\$
4
  • 9
    \$\begingroup\$ Note: This will return a Float value (1000.0) instead of an Integer, which may cause inaccurate results with large numbers. \$\endgroup\$
    – Dogbert
    Feb 25 '11 at 11:02
  • 4
    \$\begingroup\$ Ah, nice 1e2 is better than 100.0 when a percentage is needed. \$\endgroup\$
    – Phrogz
    Feb 26 '11 at 6:36
  • 1
    \$\begingroup\$ Similar to this principle, 1.0* is 1 char shorter than .to_f \$\endgroup\$
    – Unihedron
    Dec 16 '17 at 13:44
  • \$\begingroup\$ To my surprise, this trick works with Integer#upto and Integer#downto, e.g. 1.upto(1000) can become 1.upto(1e3). (Both forms iterate over integers.) \$\endgroup\$
    – Dingus
    May 20 '20 at 1:06
8
\$\begingroup\$

Kernel#p is a fun method.

Use p var instead of puts var. This works perfectly with integers and floats, but not with all types. It prints quotation marks around strings, which is probably not what you want.

Used with a single argument, p returns the argument after printing it.

Used with multiple arguments, p returns the arguments in an array.

Use p (with no arguments) instead of nil.

\$\endgroup\$
2
  • 10
    \$\begingroup\$ Unfortunately p 'some string' prints "some string" and not just some string which is often criticised by others. \$\endgroup\$ Jun 1 '13 at 21:11
  • 1
    \$\begingroup\$ Basically p s is the same as puts s.inspect, but it returns s \$\endgroup\$
    – Cyoce
    Nov 3 '16 at 23:37
8
\$\begingroup\$

Yet another way to use the splat operator: if you want to assign a single array literal, a * on the left-hand side is shorter than brackets on the right-hand side:

a=[0]
*a=0

With multiple values you don't even need the splat operator (thanks to histocrat for correcting me on that):

a=[1,2]
a=1,2
\$\endgroup\$
3
  • \$\begingroup\$ The latter case doesn't actually need the splat. \$\endgroup\$
    – histocrat
    Aug 15 '15 at 22:53
  • \$\begingroup\$ @histocrat Oh wow, I thought the second value would just be discarded in that case. \$\endgroup\$ Aug 15 '15 at 22:53
  • 1
    \$\begingroup\$ I can't believe I haven't known these in all the time I've spent golfing in Ruby. \$\endgroup\$
    – Doorknob
    Jan 2 '16 at 3:07
7
\$\begingroup\$

I just attempted a TDD code-golf challenge i.e. Write shortest code to make specs pass. The specs were something like

describe PigLatin do
  describe '.translate' do
    it 'translates "cat" to "atcay"' do
      expect(PigLatin.translate('cat')).to eq('atcay')
    end
    # And similar examples for .translate
  end
end

For the sake of code-golf, one need not create a module or class.

Instead of

module PigLatin def self.translate s;'some code'end;end

one can do

def(PigLatin=p).translate s;'some code'end

Saves 13 characters!

\$\endgroup\$
2
  • 8
    \$\begingroup\$ Ha, very thorough. Not only did you add the necessary behavior to PigLatin, but also to @pig_latin, $pig_latin, and 'pig'['latin']. \$\endgroup\$
    – histocrat
    Feb 27 '14 at 15:23
  • \$\begingroup\$ @histocrat: Now I get it. It's because translate has been defined on nil. \$\endgroup\$ Mar 20 '17 at 13:48
7
\$\begingroup\$

When a challenge requires that you output multiple lines, you don't have to loop through your results in order to print each line of e.g. an array. The puts method will flatten an array and print each element on a separate line.

> a = %w(testing one two three)
> puts a
testing
one
two
three

Combining the splat operator with #p you can make it even shorter:

p *a

The splat operator (technically the *@ method, I think) also casts your non-array enumerables to arrays:

> p a.lazy.map{|x|x*2}
#<Enumerator::Lazy: #<Enumerator::Lazy: [1, 2, 3]>:map>

vs

> p *a.lazy.map{|x|x*2}
2
4
6
\$\endgroup\$
1
  • 1
    \$\begingroup\$ *@ is not a method, splat is syntactic sugar \$\endgroup\$ Jan 24 '18 at 10:55
7
\$\begingroup\$

To join an array, instead of this

[...].join

do this

[...]*''

which saves 2 bytes. To join with a separator use

[...]*?,
\$\endgroup\$
7
\$\begingroup\$

Subscripting Numbers!

I just discovered this yesterday. n[i] returns n's bit at the i-th position. Example:

irb(main):001:0> n = 0b11010010
=> 210
irb(main):002:0> n[0]
=> 0
irb(main):003:0> n[1]
=> 1
irb(main):004:0> n[2]
=> 0
irb(main):005:0> n[3]
=> 0
irb(main):006:0> n[4]
=> 1
irb(main):007:0> n[5]
=> 0
\$\endgroup\$
1
7
\$\begingroup\$

New features in Ruby 2.7 (experimental)

Ruby 2.7 is in prerelease (as of 17 Jun 2019) and has some features that look great for golfing. Note that some of them might not make it into the final 2.7 release.

All changes in Ruby 2.7-preview1: https://github.com/ruby/ruby/blob/v2_7_0_preview1/NEWS

Numbered block parameters

This is my favorite. It lets you finally drop the |a,b| in a block:

%w[a b c].zip(1..) { puts @1 * @2 }
# => a
#    bb
#    ccc

Method reference operator: .:

.: is syntactic sugar for the .method method, e.g.:

(1..5).map(&1r.:/)
# => [(1/1), (1/2), (1/3), (1/4), (1/5)]

Pattern matching

I'm not sure how much use this will see in golf, but it's a great feature for which I only have a contrived example:

def div(*a)
  case a
    in [0, 0] then nil
    in [x, 0] if x > 0 then Float::INFINITY
    in [x, 0] then -Float::INFINITY
    in [x, y] then x.fdiv(y)
  end
end

div(-3, 0) # => -Infinity

The pattern matching syntax has lots of features. For a complete list, check out this presentation: https://speakerdeck.com/k_tsj/pattern-matching-new-feature-in-ruby-2-dot-7

This is also the feature most likely to change before 2.7 is finished; it even prints a warning when you try to use it, which you should heed:

warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!

Beginless Range: ..3

Analogous to the endless Range introduced in 2.6, it may or may not have much use in golfing:

%w[a v o c a d o].grep(..?m)
# => ["a", "c", "a", "d"]

Enumerable#tally to count like elements

This could be useful in golfing:

%w[a v o c a d o].tally
# => {"a"=>2, "v"=>1, "o"=>2, "c"=>1, "d"=>1}

Enumerable#filter_map to filter+map in one

(1..20).filter_map {|i| 10 * i if i.even? }
# => [20, 40, 60, 80, 100]

If the block returns nil or false the element will be omitted from the result.

Integer#[] takes a second argument or range:

You've long been able to get a specific bit from an integer with with subscript notation:

n = 77 # (binary 01001101)
n[3] # => 1

Now you can get the value of a range of bits by a second length argument or a range.

n = 0b01001101
n[2, 4] # => 3 (0011)
n[2..5] # => 3

Note that bits are indexed from least- to most-significant (right to left).

\$\endgroup\$
3
  • \$\begingroup\$ The range in the Enumerable#filter_map example should be (1..10), I think? \$\endgroup\$
    – Dingus
    May 1 '20 at 1:46
  • \$\begingroup\$ The method reference operator didn't make it in, as I sadly discovered just now trying to use it to do a restricted source challenge. \$\endgroup\$
    – histocrat
    Jun 6 '20 at 17:32
  • 1
    \$\begingroup\$ @1 and @2 are apparently now _1 and _2. \$\endgroup\$ Oct 12 '20 at 15:05
7
\$\begingroup\$

Avoid Array#repeated_permutation and Array#repeated_combination

Credit to @AsoneTuhid who golfed the code for repeated permutations of length \$\ge5\$.

Some of Ruby's built-in methods have unfortunately long names. Never use Array#repeated_permutation or Array#repeated_combination; save bytes as follows.

Repeated permutations

Assume a is an array. To get repeated permutations of length \$L = n + 1\$ of the elements of a:

a.product(a,a,a)  # L <= 4; number of arguments = n
a.product(*[a]*n) # L >= 5

Depending on context, the parentheses may not be required. Both of the above yield an array. To iterate over the permutations, simply call with a block.

Repeated combinations

For repeated combinations of length \$L\$, use one of

a.send(a.methods[42],L)    # enumerator
[*a.send(a.methods[42],L)] # array

The index that yields :repeated_combination depends on the Ruby version and possibly the OS (42 is correct for Ruby 2.5.5 on Linux, which is the version on TIO at the time of writing). The default indexing may also be disrupted if any libraries are loaded. The correct index in any case can always be found using [].methods.index(:repeated_combination).


In general, calling a method by index using Object#send and Object#methods, as demonstrated above for repeated combinations, is shorter than a direct method call when the number of bytes in the method name and the index of the method in the methods array satisfy:

+-------+-------+
| Bytes | Index |
+-------+-------+
|  19+  |  0-9  |
|  20+  | 10-99 |
|  21+  | 100+  |
+-------+-------+
Subtract 1 from byte count if parentheses not needed for send.
\$\endgroup\$
2
  • \$\begingroup\$ 17 bytes \$\endgroup\$ Jun 2 '20 at 22:17
  • \$\begingroup\$ @AsoneTuhid Very nice. Seems so obvious now that I see it. Thanks! \$\endgroup\$
    – Dingus
    Jun 3 '20 at 0:20
6
\$\begingroup\$

Use || instead or and && instead and.

Beside the one character from and you can save the spaces (and perhaps the bracket) around the operator.

p true and false ? 'yes' :'no'   #-> true (wrong result)
p (true and false) ? 'yes' :'no' #-> 'no'
p true&&false ? 'yes' :'no'      #-> 'no', saved 5 characters


p true or false ? 'yes' :'no'   #-> true (wrong result)
p (true or false) ? 'yes' :'no' #-> 'yes'
p true||false ? 'yes' :'no'      #-> 'yes', saved 4 characters

If you loop on an array you normally use each. But map loops also over an array and it is one character shorter.

\$\endgroup\$
6
\$\begingroup\$

Use Goruby instead of Ruby, which is something like an abbreviated version of Ruby. You can install it with rvm via

rvm install goruby

Goruby allows you to write most of your code as you would be writing Ruby, but has additional abbreviations built in. To find out the shortest available abbreviation for something, you can use the helper method shortest_abbreviation, for example:

shortest_abbreviation :puts
#=> "pts"

Array.new.shortest_abbreviation :map
#=> "m"

String.new.shortest_abbreviation :capitalize
#=> "cp"

Array.new.shortest_abbreviation :join
#=> "j"

Also very handy is the alias say for puts which itself can be abbreviated with s. So instead of

puts [*?a..?z].map(&:capitalize).join

you can now write

s [*?a..?z].m(&:cp).j

to print the alphabet in capitals (which is not avery good example). This blog post explains more stuff and some of the inner workings if you are interested in further reading.

PS: don't miss out on the h method ;-)

\$\endgroup\$
1
6
\$\begingroup\$

Re(ab)use predefined globals

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.

\$\endgroup\$
1
  • \$\begingroup\$ Especially for new golfers, it's worth noting that you generally can't use $. 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\$
    – Dingus
    Sep 16 '20 at 14:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.