ruby.onl / hidden-gems

Enumerable Methods: Replace Your Loops With One-Liners

2026-03-01

Ruby's Enumerable module gives arrays (and anything iterable) a massive collection of processing methods. These replace common Perl loops and they're chainable. Where Perl has you writing for loops with temporary variables, Ruby gives you map, select, reject, tally, and friends. Your loops become pipelines.

Part 1: Counting and Aggregation

nums.sum # add them up (Ruby 2.4+) nums.count # how many elements nums.count { |n| n > 10 } # how many match condition nums.minmax # returns [min, max] in one pass nums.min # smallest nums.max # largest nums.min_by { |n| n.abs } # min by custom criteria # Tally - count occurrences (Ruby 2.7+) %w~cat dog cat bird dog cat~.tally # => {"cat"=>3, "dog"=>2, "bird"=>1} # Perl: no built-in, you'd use a counting hash

Part 2: Filtering

# select / filter - keep matching items nums.select { |n| n > 10 } # keep if true nums.select(&:positive?) # shorthand # reject - remove matching items nums.reject { |n| n > 10 } # keep if false nums.reject(&:zero?) # shorthand # compact - remove nils [1, nil, 2, nil, 3].compact # => [1, 2, 3] # uniq - remove duplicates [1, 2, 2, 3, 3, 3].uniq # => [1, 2, 3] # flatten - flatten nested arrays [[1, 2], [3, [4, 5]]].flatten # => [1, 2, 3, 4, 5]

Part 3: Slicing and Taking

arr.first # first element arr.first(3) # first 3 elements arr.last # last element arr.last(2) # last 2 elements # Conditional taking arr.take_while { |x| x < 10 } # take until condition fails arr.drop_while { |x| x < 10 } # drop until condition fails
Perl has no direct equivalent for take_while/drop_while. You'd write a loop.

Part 4: Transformation

# map / collect - transform each element lines.map { |l| l.strip } # strip each line lines.map(&:strip) # shorthand nums.map { |n| n * 2 } # double each # flat_map - map then flatten lines.flat_map { |l| l.split(",") } # split each, flatten results # sort arr.sort # natural sort arr.sort_by { |x| x.length } # sort by criteria arr.sort_by { |x| -x } # reverse numeric sort # reverse arr.reverse

Part 5: Grouping and Chunking

# group_by - group into hash by criteria lines.group_by { |l| l[0] } # group by first character # => {"a"=>["apple","ant"], "b"=>["banana"], ...} # chunk - group consecutive matching items [1,1,2,2,2,1,1].chunk { |n| n }.to_a # => [[1, [1,1]], [2, [2,2,2]], [1, [1,1]]] # each_slice - process in fixed-size groups arr.each_slice(3) { |group| puts group.inspect } # [1,2,3], [4,5,6], [7,8,9], ... # each_cons - sliding window arr.each_cons(2) { |a, b| puts "#{a} -> #{b}" } # 1->2, 2->3, 3->4, ... # zip - combine parallel arrays names.zip(ages) # => [["Alice",30], ["Bob",25]]

Part 6: Checking and Finding

# find / detect - first matching element arr.find { |x| x > 10 } # returns first match or nil # Perl: first { $_ > 10 } @arr (with List::Util) # find_index - index of first match arr.find_index { |x| x > 10 } # include? - membership check arr.include?(42) # true if 42 is in array

Part 7: Chaining Is the Real Power

The real magic is chaining these together:
# Process log lines: strip, filter, downcase, count unique lines .map(&:strip) .reject(&:empty?) .map(&:downcase) .uniq .count # One-liner: top 5 most common words File.read("text.txt").split.tally.sort_by { |w, c| -c }.first(5)
Each method returns something the next method can consume. No temporary variables, no intermediate arrays cluttering your namespace. Data flows through transformations like water through pipes.

Created By: Wildcard Wizard. Copyright 2026