Functions

The Functions filter provides a large number of convenience methods Rubyists are familar with. Statements such as "252.3".to_i transform into parseInt("252.3"), or [1,3,5].yield_self { |arr| arr[1] } into (arr => arr[1])([1, 3, 5]). Generally you will want to include this filter in your configuration unless you have specific reason not to.

List of Transformations

  • .abs Math.abs()
  • .all? .every (with block) or .every(Boolean) (without block)
  • .any? .some (with block) or .some(Boolean) (without block)
  • .ceil Math.ceil()
  • .chars Array.from()
  • .chr fromCharCode
  • .clear .length = 0
  • .compact .filter(x => x != null)
  • .compact! .splice(0, a.length, ...a.filter(x => x != null)) (mutating)
  • debugger debugger (JS debugger statement)
  • .define_method klass.prototype.meth = function ...
  • .delete delete target[arg]
  • .downcase .toLowerCase
  • .each .forEach
  • .each_key for (i in ...) {}
  • .each_pair for (let key in item) {let value = item[key]; ...}
  • .each_value .forEach
  • .each_with_index .forEach
  • .end_with? .slice(-arg.length) == arg
  • .empty? .length == 0
  • .find_index findIndex
  • .first [0]
  • .first(n) .slice(0, n)
  • .flat_map {} .flatMap()
  • .floor Math.floor()
  • .freeze Object.freeze()
  • .group_by {} Object.groupBy() (ES2024+) or .reduce() fallback
  • .group_by {|k,v| ...} destructuring support ([k, v]) => ...
  • .gsub replace(//g)
  • .has_key? key in hash
  • .include? .indexOf() != -1
  • .index indexOf (when using arg) or findIndex (when using block)
  • .inspect JSON.stringify()
  • .join .join('') (Ruby defaults to "", JS to ",")
  • .key? key in hash
  • .keys() Object.keys()
  • .last [*.length-1]
  • .last(n) .slice(*.length-1, *.length)
  • .lstrip .replace(/^\s+/, "")
  • .max Math.max.apply(Math)
  • .max_by {} .reduce()
  • .member? key in hash
  • .merge Object.assign({}, ...)
  • .merge! Object.assign()
  • .method_defined? klass.prototype.hasOwnProperty(meth) or meth in klass.prototype
  • .min Math.min.apply(Math)
  • .min_by {} .reduce()
  • .negative? < 0
  • .none? !.some (with block) or !.some(Boolean) (without block)
  • [-n] = x [*.length-n] = x for literal negative index assignment
  • .new(size,default) == .new(size).fill(default)
  • .nil? == null
  • .ord charCodeAt(0)
  • .positive? > 0
  • puts console.log
  • rand Math.random
  • .reject {} .filter(x => !(...)) (negated condition)
  • .reject(&:method) .filter(item => !item.method()) (symbol-to-proc)
  • .replace .length = 0; ...push.apply(*)
  • .respond_to? right in left
  • .rindex .lastIndexOf
  • .round Math.round()
  • .rstrip .replace(/s+$/, "")
  • .scan .match(//g)
  • .send dynamic method dispatch obj.send(:foo, x) becomes obj.foo(x) or obj[method](x)
  • .sort_by {} .toSorted() (ES2023+) or .slice().sort() fallback
  • .sum .reduce((a, b) => a + b, 0)
  • .reduce(:+) .reduce((a, b) => a + b) (symbol-to-proc for operators)
  • .reduce(:merge) .reduce((a, b) => ({...a, ...b})) (hash merge)
  • .times for (let i = 0; i < n; i++)
  • .start_with? .startsWith(arg)
  • .upto(lim) for (let i=num; i<=lim; i+=1)
  • .downto(lim) for (let i=num; i>=lim; i-=1)
  • .step(lim, n).each for (let i=num; i<=lim; i+=n)
  • .step(lim, -n).each for (let i=num; i>=lim; i-=n)
  • (0..a).to_a [...Array(a+1).keys()]
  • (b..a).to_a Array.from({length: (a-b+1)}, (_, idx) => idx+b)
  • (b...a).to_a Array.from({length: (a-b)}, (_, idx) => idx+b)
  • .strip .trim
  • .sub .replace
  • .tap {|n| n} (n => {n; return n})(...)
  • .to_f parseFloat
  • .to_i parseInt
  • .to_s .toString
  • .to_sym (removed - symbols are strings in JS)
  • .to_json JSON.stringify(obj)
  • typeof(x) typeof x (JS type checking operator)
  • .upcase .toUpperCase
  • .yield_self {|n| n} (n => n)(...)
  • .zero? === 0
  • [-n] [*.length-n] for literal values of n
  • [n...m] .slice(n,m)
  • [n..m] .slice(n,m+1)
  • [start, length] .slice(start, start+length) (Ruby 2-arg slice)
  • [n..m] = v .splice(n, m-n+1, ...v)
  • .slice!(n..m) .splice(n, m-n+1)
  • [/r/, n] .match(/r/)[n]
  • [/r/, n]= .replace(/r/, ...)
  • (1..2).each {|i| ...} for (let i=1; i<=2; i+=1)
  • "string" * length "string".repeat(length)
  • [element] * n Array(n).fill(element)
  • [a, b] * n Array.from({length: n}, () => [a, b]).flat()
  • array + [elements] array.concat([elements])
  • @foo.call(args) this._foo(args)
  • @@foo.call(args) this.constructor._foo(args)
  • Array(x) Array.from(x)
  • delete x delete x (note lack of parenthesis)

Additional Features

  • .sub! and .gsub! become equivalent x = x.replace statements
  • .map!, .reverse!, and .select! become equivalent .splice(0, .length, *.method()) statements
  • setInterval and setTimeout allow block to be treated as the first parameter on the call
  • for the following methods, if the block consists entirely of a simple expression (or ends with one), a return is added prior to the expression: sub, gsub, any?, all?, map, find, find_index.
  • New classes subclassed off of Exception will become subclassed off of Error instead; and default constructors will be provided
  • loop do...end will be replaced with while (true) {...}
  • n.times do...end and n.times { |i| ... } will be replaced with for loops
  • raise Exception.new(...) will be replaced with throw new Error(...)
  • block_given? will check for the presence of optional argument _implicitBlockYield which is a function made accessible through the use of yield in a method body.
  • alias_method works both inside of a class definition as well as called directly on a class name (e.g. MyClass.alias_method)
  • define_method and method_defined? work inside class bodies (with or without explicit receiver), including inside loops like [:a, :b].each { |m| define_method(m) { ... } }
  • Block parameter destructuring is supported: .map {|k, v| ...} becomes .map(([k, v]) => ...)

Methods Always Called with Parentheses

Ruby allows calling methods without parentheses, but JavaScript requires them for methods that return values. The following methods always output with () in JS, even when written without parentheses in Ruby:

reverse, pop, shift, sort, dup, clone

arr.reverse.each { |x| puts x }
# => for (let x of arr.reverse()) { console.log(x) }

x = arr.pop
# => let x = arr.pop()

Methods Requiring Parentheses

Some Ruby method names like keys, values, index, max, etc. could also be property accesses in JavaScript (e.g., on DOM nodes). To avoid incorrect transformations, these methods are only converted when called with parentheses:

a.keys     # => a.keys (no conversion - could be property access)
a.keys()   # => Object.keys(a) (converted - clearly a method call)

The following methods require parentheses for automatic conversion: keys, values, entries, index, rindex, clear, reverse!, max, min

To force conversion even without parentheses, explicitly include the method:

Ruby2JS.convert('a.keys', include: [:keys])  # => Object.keys(a)

Or use include_all: true to enable conversion for all such methods:

Ruby2JS.convert('a.keys', include_all: true)  # => Object.keys(a)

Methods Requiring Explicit Inclusion

The following mappings will only be done if explicitly included (pass include: [:class, :call] as a convert option to enable):

  • .class .constructor
  • a.call a()

Next: Jest