ruby.onl / dark-arts

Numbered Block Parameters: 1 Is the New $

2026-03-23

Ruby 2.7 introduced _1, _2, _3 as implicitly numbered block parameters. Skip naming your block variables entirely. No Perl equivalent exists for this one. It's Ruby going full minimalist, and it's glorious for short blocks where a variable name adds nothing.

Part 1: Basic Usage

# Instead of naming the block variable: lines.map { |line| line.strip } # Use _1: lines.map { _1.strip } # Instead of two named variables: hash.select { |key, value| value > 10 } # Use _1 and _2: hash.select { _2 > 10 }

Part 2: When to Use Them

Best for short, simple blocks where naming the variable adds no clarity:
# Good - simple operations lines.map { _1.chomp } pairs.map { _1 + _2 } hash.select { _2 > 10 } lines.map { _1.split[2] } # grab third field from each line # Less good - complex operations where names help readability # Use named params when the logic is complex: records.map { |record| record.split(",")[3].strip.to_i * 100 }

Part 3: With Various Methods

# map nums.map { _1 * 2 } # select/reject words.select { _1.length > 5 } lines.reject { _1.empty? } # each lines.each { puts _1 } # sort_by words.sort_by { _1.length } # With two parameters hash.each { puts "#{_1}: #{_2}" } pairs.map { "#{_1}=#{_2}" }

Part 4: Combining with Other Features

# With regex lines.select { _1 =~ %r~ERROR~ } # With method chaining File.readlines("log.txt") .map { _1.chomp } .select { _1 =~ %r~error~i } .map { _1.split[0] } # grab timestamp # With then/yield_self data.then { JSON.parse(_1) }.then { _1["users"] }

Part 5: The Rules

You can't mix numbered params with named params in the same block:
# ERROR: hash.map { |key| "#{key}: #{_2}" } # can't mix!
_1 through _9 are available, but if you need more than 2-3, use named params for clarity. If you're using _4, you should probably rethink your approach.

Part 6: Symbol-to-Proc vs Numbered Params

# Symbol-to-proc: for no-argument method calls lines.map(&:chomp) # cleanest for simple methods # Numbered params: for anything more complex lines.map { _1.split(",")[2] } # can't do this with &: lines.select { _1.length > 80 } # needs an argument
Rule of thumb: use &:method when calling a single no-argument method. Use _1 when you need to do something more with the value. Use named params when the logic is complex enough that names help readability.

Created By: Wildcard Wizard. Copyright 2026