Dom Christie

Destructuring: JavaScript vs. Ruby

Combining Ruby 2.7’s pattern matching, and Ruby 3’s rightward assignment, we can achieve something similar to JavaScript’s destructuring assignment. Jared White covers this well in Everything You Need to Know About Destructuring in Ruby 3. For those familiar the JavaScript syntax, here are some comparisons for destructuring objects/hashes:

// JavaScript
const { a } = { a: 1 } // a === 1
# Ruby
{a: 1} => {a:} # a == 1

(Rightward assignment might take some getting used to: variables on the right get assigned values from the left.)

Variable Naming

// JavaScript
const { a: b } = { a: 1 } // b === 1
# Ruby
{a: 1} => {a: b} # b == 1

…rest

// JavaScript
const { a, ...rest } = { a: 1, b: 2 } // a === 1, rest === { b: 2 }
# Ruby
{a: 1, b: 2} => {a:, **rest} # a == 1, rest == {b: 2}

Default Values

There’s currently no Ruby syntax equivalent to that of JavaScript for setting default values whilst destructuring. merge is probably the best choice here. Let’s say we wanted defaults of a = 42 and b = 2:

// JavaScript
const { a = 42, b = 2 } = { a: 1 } // a === 1, b === 2
# Ruby
{a: 42, b: 2}.merge({a: 1}) => {a:, b:} # a == 1, b == 2

Nil Values

In JavaScript, attempting to destructure a key that does not exist on the source, results in a variable being undefined. In Ruby, pattern matching expects a pattern to match (obviously), so if a key does not exist on the source it will throw NoMatchingPatternError. However, we can mimic JavaScript’s relaxed behaviour by defining a deconstruct_keys method on our source, and merge any undefined keys.

// JavaScript
const { a, b } = { a: 1 } // a === 1, b === undefined
class Props < Hash
  def deconstruct_keys(keys)
    Array(keys).to_h { |k| [k, nil] }.merge(self)
  end
end

Props[{a: 1}] => {a:, b:} # a == 1, b == nil

Thanks to Elliot Crosby-McCullough on the StimulusReflex Discord for this idea and code snippet.