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
.absMath.abs().all?.every(with block) or.every(Boolean)(without block).any?.some(with block) or.some(Boolean)(without block).ceilMath.ceil().charsArray.from().chrfromCharCode.clear.length = 0.compact.filter(x => x != null).compact!.splice(0, a.length, ...a.filter(x => x != null))(mutating)debuggerdebugger(JS debugger statement).define_methodklass.prototype.meth = function ....deletedelete target[arg].downcase.toLowerCase.each.forEach.each_keyfor (i in ...) {}.each_pairfor (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_all {}.filter()(alias for.select).find_indexfindIndex.first[0].first(n).slice(0, n).flat_map {}.flatMap().floorMath.floor().freezeObject.freeze().group_by {}Object.groupBy()(ES2024+) or.reduce()fallback.group_by {|k,v| ...}destructuring support ([k, v]) => ....gsubreplace(//g).has_key?key in hash.include?.indexOf() != -1.indexindexOf(when using arg) orfindIndex(when using block).inspectJSON.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+/, "").maxMath.max.apply(Math).max_by {}.reduce().member?key in hash.mergeObject.assign({}, ...).merge!Object.assign().method_defined?klass.prototype.hasOwnProperty(meth)ormeth in klass.prototype.minMath.min.apply(Math).min_by {}.reduce().negative?< 0.none?!.some(with block) or!.some(Boolean)(without block)[-n] = x[*.length-n] = xfor literal negative index assignment.new(size,default)== .new(size).fill(default).nil?== null.ordcharCodeAt(0).positive?> 0putsconsole.lograndMath.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.roundMath.round().rstrip.replace(/s+$/, "").scan.match(//g).select {}.filter().senddynamic method dispatch obj.send(:foo, x)becomesobj.foo(x)orobj[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).timesfor (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).eachfor (let i=num; i<=lim; i+=n).step(lim, -n).eachfor (let i=num; i>=lim; i-=n)(0..a).to_a[...Array(a+1).keys()](b..a).to_aArray.from({length: (a-b+1)}, (_, idx) => idx+b)(b...a).to_aArray.from({length: (a-b)}, (_, idx) => idx+b).strip.trim.sub.replace.tap {|n| n}(n => {n; return n})(...).to_fparseFloat.to_iparseInt.to_s.toString.to_sym(removed - symbols are strings in JS) .to_jsonJSON.stringify(obj)JSON.generate(x)JSON.stringify(x)JSON.pretty_generate(x)JSON.stringify(x, null, 2)JSON.parse(x)JSON.parse(x)typeof(x)typeof x(JS type checking operator).upcase.toUpperCase.yield_self {|n| n}(n => n)(...).zero?=== 0[-n][*.length-n]for literal values ofn[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] * nArray(n).fill(element)[a, b] * nArray.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 xdelete x(note lack of parenthesis)
Regular Functions with Function.new
By default, Ruby blocks become JavaScript arrow functions (=>), which have
lexical this binding. When you need a regular function with dynamic this
binding (e.g., for DOM event handlers, method composition, or prototype methods),
use Function.new:
fn = Function.new { |x| x * 2 }
# => let fn = function(x) {x * 2}
callback = Function.new { handle_click(this) }
# => let callback = function() {handle_click(this)}
Compare with regular procs (arrow functions):
fn = proc { |x| x * 2 }
# => let fn = x => x * 2
When to use Function.new:
- jQuery/DOM event handlers where
thisshould refer to the element - Method composition with dynamic super calls
- Situations where
thismust be determined at call time, not definition time
Alternative: You can also use the Pragma filter with
# Pragma: function to force regular function syntax for any block.
Additional Features
.sub!and.gsub!become equivalentx = x.replacestatements.map!,.reverse!, and.select!become equivalent.splice(0, .length, *.method())statementssetIntervalandsetTimeoutallow 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
returnis added prior to the expression:sub,gsub,any?,all?,map,find,find_index. - New classes subclassed off of
Exceptionwill become subclassed off ofErrorinstead; and default constructors will be provided loop do...endwill be replaced withwhile (true) {...}n.times do...endandn.times { |i| ... }will be replaced withforloopsraise Exception.new(...)will be replaced withthrow new Error(...)block_given?will check for the presence of optional argument_implicitBlockYieldwhich is a function made accessible through the use ofyieldin a method body.alias_methodworks both inside of a class definition as well as called directly on a class name (e.g.MyClass.alias_method)define_methodandmethod_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]) => ...)
Collection Methods: group_by, sort_by, max_by, min_by
These methods provide Ruby-like collection operations with intelligent JavaScript output based on your ES level.
group_by
Groups elements by a key extracted from each element.
# Group users by role
users.group_by { |u| u.role }
# ES2024+: Object.groupBy(users, u => u.role)
# Pre-ES2024: users.reduce(($acc, u) => {
# let $key = u.role;
# ($acc[$key] = $acc[$key] ?? []).push(u);
# return $acc
# }, {})
# Group with computed key
items.group_by { |i| i.price > 100 ? "expensive" : "affordable" }
# ES2024+: Object.groupBy(items, i => i.price > 100 ? "expensive" : "affordable")
# Destructuring support for key-value pairs
pairs.group_by { |k, v| k }
# ES2024+: Object.groupBy(pairs, ([k, v]) => k)
sort_by
Sorts elements by a key, returning a new array (non-mutating).
# Sort by property
users.sort_by { |u| u.name }
# ES2023+: users.toSorted((u_a, u_b) => {
# if (u_a.name < u_b.name) {return -1}
# else if (u_a.name > u_b.name) {return 1}
# else {return 0}
# })
# Pre-ES2023: users.slice().sort(...)
# Sort by computed value
items.sort_by { |i| i.price * i.quantity }
# Compares (i.price * i.quantity) for each element
# Sort by method result (with filter chaining)
words.sort_by { |w| w.length }
# Sorts words by their length
max_by
Finds the element with the maximum value for the given key.
# Find user with highest score
users.max_by { |u| u.score }
# => users.reduce((a, b) => a.score >= b.score ? a : b)
# Find longest word
words.max_by { |w| w.length }
# => words.reduce((a, b) => a.length >= b.length ? a : b)
# Find item with highest total value
items.max_by { |i| i.price * i.qty }
# => items.reduce((a, b) => a.price * a.qty >= b.price * b.qty ? a : b)
min_by
Finds the element with the minimum value for the given key.
# Find user with lowest score
users.min_by { |u| u.score }
# => users.reduce((a, b) => a.score <= b.score ? a : b)
# Find shortest word
words.min_by { |w| w.length }
# => words.reduce((a, b) => a.length <= b.length ? a : b)
# Find cheapest item
items.min_by { |i| i.price }
# => items.reduce((a, b) => a.price <= b.price ? a : b)
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.constructora.calla()