Skip to content

O-I/Facets

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 

Repository files navigation

Facets

A community-curated list of one-liners (or several-liners if elegance demands) in Ruby

Files, Folders, and Paths

Convert the contents of filename to a string
File.open(filename, 'rb') { |file| file.read }
Get the current user's home directory
Dir.home

This only works if ENV['HOME'] is set, though. RubyTapas #10 gives us an alternative using Etc to find the current user's login name and passing that to Dir.home:

Dir.home(Etc.getlogin)

Strings

Calculate the Hamming distance between two strings str1 and str2 (returns nil if str1 and str2 are of unequal length):
str1.chars.zip(str2.chars).reduce(0) { |sum, (x, y)| sum + (x == y ? 0 : 1) } if str1.size == str2.size
Determine whether one string str1 is a rotation of another string str2:
(str1 + str1)[str2] == str2
Get the nth bit of an Integer j:

My first inclination would be to convert to binary and use [], i.e., j.to_s(2)[n].to_i, but you can call [] directly on an Integer:

j[n]

Arrays

Remove all instances of value l from an array a and return the result
a - [l]

Or a -= [l] if you would like to reasign the result to a. This is the most elegant way I've seen of doing this. Doing something like a.delete(l) returns l rather than the updated value of a which disrupts method chaining. You could always do a.tap { |x| x.delete(l) }, but I think the above line is superior and just as chainable if enclosed in parentheses.

Convert an array a with even index whose elements are alternating key value pairs (e.g., [k1, v1, k2, v2,..., kn, vn] into the respective hash h
h = Hash[*a]

This is likely not a good idea if a happens to be a large dataset as the splat operator will expand all contents of the array to the stack. In that case, you can use h = Hash[a.each_slice(2).to_a].

In the event a is already a 2D array of the form [[k1, v1], [k2, v2],..., [kn, vn]], you can simply do h = Hash[a]. And from Ruby 2.1 onwards, you can simply use Array#to_h: h = a.to_h.

Create an nxn multiplication table represented by a 2-dimensional array:
[*1..n].product([*1..n]).map { |arr| arr.reduce(:*) }.each_slice(n).to_a

Or, if you're using ActiveSupport:

[*1..n].product([*1..n]).map { |arr| arr.reduce(:*) }.in_groups(n)

And in terms of clarity, this one's about as good as it gets:

Array.new(n) { |x| Array.new(n) { |y| (x+1)*(y+1) } }
Calculate the frequency distribution of an array a:

There are many ways to do this, but using Enumerable#each_with_object seems to be the most idiomatic:

a.each_with_object(Hash.new(0)) { |element, frequency| frequency[element] += 1 }

Enumerable#reduce also works, but feels clumsier. Notice that the block parameters are reversed compared to the above and the need to explicitly return the accumulator on each pass:

a.reduce(Hash.new(0)) { |frequency, element| frequency[element] += 1; frequency }

And yet another way using Ruby 2.2+ that seems truest to a functional style:

a.group_by(&:itself).map { |k, v| [k, v.size] }.to_h
Get all but the first element of an array a:

You can use parallel assignment in conjunction with the splat operator if you're planning on using the head:

head, *rest = a

But if you just want the rest, using drop is better:

a.drop(1)

Both are preferable to a[1..-1] in that they return [] rather than nil if a is empty.

Find all duplicate values in an array a (Ruby 2.2+):
a.group_by(&:itself).select { |_, v| v.size > 1 }.keys

You can replace group_by(&:itself) with group_by { |n| n } for lesser versions of Ruby.

Calculate rolling averages of an array a over interval length n:
a.each_cons(n).map { |interval| interval.reduce(:+) / n.to_f }
Reduce an array a of Boolean values

For and, or, and xor, we can use Enumerable#reduce paired with the appropriate logical operator, i.e., a.reduce(:&), a.reduce(:|), and a.reduce(:^), respectively.

For reducing over and and or, I find a.all? and a.any? to be more idiomatic than explicit use of reduce.

"Map compact" or "select map"

This one is best illustrated by an example. Note, I don't think there is a particularly elegant way to do what I'm about to describe in Ruby. A common idiom is to map over a collection and select the resulting values for those elements which conform to some predicate.

For example, suppose we have an array a = [1, 2, 3, 4, 5] and want to get a resulting collection that adds 1 to each element only if that element is even. We can achieve this in several ways:

a = [1, 2, 3, 4, 5]

a.map { |l| l + 1 if l.even? }.compact                      # => [3, 5]
a.select(&:even?).map { |l| l + 1 }                         # => [3, 5]
a.each_with_object([]) { |l, res| res << l + 1 if l.even? } # => [3, 5]
a.reduce([]) { |res, l| res.push(l + 1) if l.even?; res }   # => [3, 5]

All of these one-liners return the desired result, all of them feel clumsy.

Hashes

Create a hash h from two arrays k and v of equal length that represent h's keys and values, respectively:
h = Hash[k.zip(v)]
Decompose a hash h into two arrays k and v that respresent h's keys and values, respectively:
  1. The straightforward way:
k, v = h.keys, h.values
  1. Or more cryptically for impressing your nerdy friends:
k, v = *h.to_a.transpose
Remove key-value pairs in hash h given keys in array a:

ActiveSupport has a nifty little method for this called Hash#except. The best I could come up with for doing this in one line with Ruby non-destructively is

hash.tap { |h| a.map { |k| h.delete(k) } }
Weighted random sampling without replacement of a hash h's keys whose values are weighted probabilities that sum to 1:
h.max_by { |_, weight| rand ** (1.0/weight) }.first

This is essentially the weighted analogue of Array#sample. With Ruby 2.2+, we also have the analogue of Array#sample(n):

h.max_by(n) { |_, weight| rand ** (1.0/weight) }.map(&:first)

Regular Expressions

Calculations

Compute n! for all nonnegative integers n (returns nil otherwise):
(1..n).reduce(1, :*) if n.is_a?(Integer) && n > -1

Miscellaneous

Given an mxn matrix, return an array that represents the clockwise spiral path from the top left to the center. For example, given
matrix = [[1,2,3],
          [8,9,4],
          [7,6,5]]

spiral(matrix) should return [1, 2, 3, 4, 5, 6, 7, 8, 9]

def spiral(matrix)
  matrix.empty? ? [] : matrix.shift + spiral(matrix.transpose.reverse)
end

Note that this implementation has one major drawback — it mutates its argument.

Symbol#to_proc with arguments

This isn't a one-liner nor is it something I recommend without extreme caution as it patches a core class, but it is one of the slickest — if not the slickest — Ruby hacks I've ever seen.

Open up the Symbol class and add a call method to it that contains the following lambda:

class Symbol
  def call(*args, &block)
    ->(caller, *rest) { caller.send(self, *rest, *args, &block) }
  end
end

This gives Symbol#to_proc the superpower of accepting arguments. so, instead of doing, e.g.,

nums = [1, 2, 3, 4]
text = %w(this is a test)

nums.map { |num| num ** 1i }
text.map { |word| word.gsub('s', '*') }

we can do

nums.map(&:**.call(1i))
text.map(&:gsub.call('s', '*'))

But there's one more trick here. By naming our method call, we can use the shorter .() syntax:

nums.map(&:**.(1i))
text.map(&:gsub.('s', '*'))

About

A community-curated list of one-liners in Ruby

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published