- Data Types
- Truthyness
- Operators
- Data Structures
- Arguments
- Classes
In Python, Strings are immutable.
s = "Sun"
s[0] = "F" # => TypeError: 'str' object does not support item assignment
In Ruby, Strings are mutable.
s = "Sun"
s[0] = "F"
s #=> Fun
In JavaScript, Strings are immutable.
let s2 = "Sun";
s2[0] = "F"; // The action is not performed but no error is triggered
console.log(s2[0]); // Sun
In Python
q = "quick"
f"The {q} brown box"
In Ruby
q = "quick"
"The #{q} brown box"
In JavaScript
q = "quick";
`The ${q} brown box`;
In Python, we use the []
function, passing in three params:
- Start index
- End index (which is excluded)
- Stepper
s = "abcdefghijk"
s[0:3] # From index 0 to 3 (exclusive) => "abc"
s[:3] # From the start to index 3 (exclusive) => "abc"
s[1:] # From index 1 to the end => "bcdefghijk"
s[::2] # From the start to the end in steps of 2 => "acegik"
In Ruby, we use the slice
function, passing two params:
- Start index
- Number of chars you want to take
s = "abcdefghijk"
s.slice(0,3) # From 0, take the 3 first chars => "abc"
Alternatively, you can pass a range (considering the end index is included)
s = "abcdefghijk"
s[0..3] # From index 0 to 3 (inclusive) => "abcd"
Or a regex, with an optional capture group
s = "abcdefghijk"
s[/[aeiou]/] #=> "a"
In JavaScript, we use the slice
function, passing two params:
- Start index
- End index (which is excluded)
s = "abcdefghijk"
s.slice(0,3) # From index 0 to 3 (exclusive) => "abc"
In Python
s[::-1] # From the start to the end in steps of 1, counting backwards => "kjihgfedcba"
In Ruby
s.reverse #=> "kjihgfedcba"
In JavaScript
There's no built-in method to do this.
In Python
s.upper()
s.lower()
In Ruby
s.upcase
s.downcase
In JavaScript
s.toUpperCase();
s.toLowerCase();
In Python
1 / 2 #=> 0.5 True Division
In Ruby
1 / 2 #=> 0 Integer Division
1.0 / 2.0 #=> 0.5 Float Division
In JavaScript
1 / 2; //=> 0.5 True Division
In Python
True
False
In Ruby
true
false
In JavaScript
true;
false;
In Python
None
In Ruby
nil
In JavaScript
null;
For quick reference:
Python | Ruby | JavaScript | |
---|---|---|---|
None /nil /null |
❌ | ❌ | ❌ |
"" |
❌ | ✅ | ❌ |
[] |
❌ | ✅ | ✅ |
In Python
not(not(None)) #=> False
not(not("")) #=> False
not(not([])) #=> False
In Ruby
!!nil #=> False
!!"" #=> True
!![] #=> True
In JavaScript
!!null; //=> False
!!""; //=> False
!![]; //=> True
In Python and Ruby we use strict equality.
==
In JavaScript we have two distinct operators for abstract and strict equality. See MDN for more details.
== // Abstract equality comparison (compares values after converting them to the same type)
=== // Strict equality comparison
In Python we use:
and
or
not
Note the &&
, ||
and !
boolean operators are not available in Python.
In Ruby we use:
&&
||
!
Note that though the and
, or
and not
operators are available in Ruby, they are better suited as control flow operators. If we choose to use them as boolean operators (which is against general advice), we must be aware of the fact that they have a lower precedence than their counterparts (&&
, ||
and !
).
In Javascript we use:
&&
||
!
Note the and
, or
and not
operators are not available in JavaScript.
These work in the same way in all three languages.
>
<
>=
<=
In Python
my_list = ['a', 'b', 'c']
In Ruby
my_list = ['a', 'b', 'c']
In JavaScript
const myList = ["a", "b", "c"];
In Python
my_list.append('d')
In Ruby
my_list.push('d')
In JavaScript
myList.push("d");
In Python
my_list.pop()
my_list.pop(1) #=> "b" Removes and returns the element at index 1
In Ruby
my_list.pop()
my_list.pop(2) #=> ["b", "c"] Removes and returns as many items as indicated, in this case 2 items
In JavaScript
myList.pop();
myList.pop(1); //=> "b" Removes and returns the element at index 1
In Python
len(my_list)
In Ruby
my_list.length
In JavaScript
myList.length;
In Python
min([1, 2, 3])
max([1, 2, 3])
In Ruby
[1, 2, 3].min
[1, 2, 3].max
In JavaScript
Math.min(...[1, 2, 3]);
Math.max(...[1, 2, 3]);
In Python
1 in [1, 2, 3]
In Ruby
[1, 2, 3].include?(1)
In JavaScript
[1, 2, 3].includes(1);
In Python
my_list.sort() # Sorts the list in place, doesn't return anything
In Ruby
my_list.sort # Sorts
my_list.sort! # Sorts the list in place
In JavaScript
myList.sort(); // Sorts the list in place, and returns it
In Python
my_list.reverse() # Reverses the list in place, doesn't return anything
In Ruby
my_list.reverse # Reverses
my_list.reverse! # Reverses the list in place
In JavaScript
myList.reverse(); // Reverses the list in place, and returns it
In Python
a = [1, 2, 3]
b = ["a", "b", "c"]
for item in list(zip(a, b)):
print(item)
# (1, 'a')
# (2, 'b')
# (3, 'c')
In Ruby
a = [1, 2, 3]
b = %w[a b c]
a.zip(b).each do |item|
puts item
end
# 1
# a
# 2
# b
# 3
# c
In JavaScript
There's no built-in method to do this.
In Python
range(10) # From 0 to 10 (exclusive) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
range(0, 10) # From 0 to 10 (exclusive)
range(0, 10, 2) # From 0 to 10, in steps of 2 => [0, 2, 4, 6, 8]
To convert ranges to lists, we do list(range(0,10))
.
In Ruby
0..10 # From 0 to 10 (inclusive) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
0...10 # From 0 to 10 (exclusive) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
To convert ranges to arrays, we do (0..10).to_a
.
In JavaScript
There's no built-in method to create ranges.
In Python
new_list = [some_method(i) for i in old_list if condition_method(i)]
In Ruby and JavaScript, there's no built-in method to do this.
In Python
def square(n):
return n ** 2
map(square, [1, 2, 3]) #=> [1, 4, 9]
Or, with a lambda:
map(lambda n: n ** 2, [1, 2, 3])
In Ruby
def square(n)
n ** 2
end
[1, 2, 3].map { |n| square(n) }
Or:
[1, 2, 3].map { |n| n ** 2 }
In JavaScript
function square(n) {
return n ** 2;
}
[1, 2, 3].map(n => square(n));
Or:
[1, 2, 3].map(n => n ** 2 );
In Python
def is_even(n):
return n % 2 == 0
filter(is_even, [1, 2, 3]) # => [2]
Or, with a lambda:
filter(lambda n: n % 2 == 0, [1, 2, 3])
In Ruby
def is_even?(n)
n.even?
end
[1, 2, 3].filter { |n| is_even?(n) }
In this specific case, we could use a more idiomatic approach with the even?
built-in method.
[1, 2, 3].filter(&:even?)
In JavaScript
function isEven(n) {
return n % 2 == 0;
}
[1, 2, 3].filter(n => isEven(n));
Or:
[1, 2, 3].filter(n => n % 2 == 0);
In Python
Sets are unordered collections of unique values.
my_set = set()
my_set.add(1) # {1}
my_set.add(2) # {1,2}
my_set.add(2) # {1,2}
In Ruby
In order to use them, the Set module needs to be required.
require 'set'
my_set = Set[]
my_set.add(1)
my_set.add(2)
my_set.add(2)
In JavaScript
mySet = new Set();
mySet.add(1);
mySet.add(2);
mySet.add(2);
In Python
Tuples, unlike lists, are immutable, to ensure elements don't get flipped or reassigned.
my_tuple = ('a', 'a', 'b')
my_tuple.index('a')
my_tuple.count('a')
In Ruby and JavaScript, there's no concept of tuples.
In Python
color = (red=255, green=0, blue=0)
In Ruby, there's no concept of named tuples, however, Struct
or OpenStruct
could be used as alternatives.
require 'ostruct'
Color = Struct.new(:red, :green, :blue)
color = Color.new(255, 0, 0)
In JavaScript, there's no concept of named tuples, object literals are used instead.
const color = { red: 255, green: 0, blue: 0 };
In Python
my_dict = {
'apple': 2.99,
'oranges': 1.99,
'watermelon': 0.5
}
my_dict['apple']
my_dict.get('banana', 2.5) # Augments the method above, providing a default value
my_dict.keys()
my_dict.values()
my_dict.items() # Converts the dictionary into a list
In Ruby
my_hash = {
apple: 2.99,
oranges: 1.99,
watermelon: 0.5
}
my_hash[:apple]
my_hash.fetch(:banana, 2.5) # Augments the method above, providing a default value
my_hash.keys
my_hash.values
my_hash.to_a # Converts the hash into an array
In JavaScript
myObj = {
apples: 2.99,
oranges: 1.99,
watermelon: 0.5,
};
myObj.apple;
Object.keys(myObj);
Object.values(myObj);
Object.entries(myObj);
In Python
"a" in {"a": 1}
In Ruby
{ a: 1 }.key?(:a)
In JavaScript
"a" in { a: 1 };
In Python
if True:
print("âś…")
elif True:
print("❌")
else:
print("❌")
In Ruby
if true
puts('âś…')
elsif true
puts('❌')
else
puts('❌')
end
In JavaScript
if (true) {
console.log("âś…");
} else if (true) {
console.log("❌");
} else {
console.log("❌");
}
In Python
"✅" if True else "❌"
In Ruby and JavaScript
true ? "✅" : "❌"
In Python
Switch statements are not available.
In Ruby
location = 'Beach'
case location
when 'Beach'
puts 'Flip flops'
when 'Home'
puts 'Chewbacca slippers'
when 'Mountain', 'Jungle'
puts 'Boots'
else
puts 'Sneakers'
end
In JavaScript
const location = "Beach";
switch (location) {
case "Beach":
console.log("Flip flops");
break;
case "Home":
console.log("Chewbacca slippers");
break;
case "Mountain":
case "Jungle":
console.log("Boots");
break;
default:
console.log("Sneakers");
}
In Python
for n in [1, 2, 3]:
print(n)
In Ruby
%w[a b c].each do |n|
puts n
end
In JavaScript
for (n of [1, 2, 3]) {
console.log(n);
}
In Python
for index, n in enumerate([1, 2, 3]):
print(f"index: {index}, n: {n}")
In Ruby
[1, 2, 3].each_with_index do |n, index|
puts "index: #{index}, n: #{n}"
end
In JavaScript
for ([index, n] of [1, 2, 3].entries()) {
console.log(`index: ${index}, n: ${n}`);
}
In Python
my_dict = {"a": 1, "b": 2, "c": 3}
for k, v in my_dict.items():
print(f"k:{k}, v:{v}")
In Ruby
my_hash = { a: 1, b: 2, c: 3 }
my_hash.each do |k, v|
puts "k: #{k}, v: #{v}"
end
In JavaScript
const myObject = { a: 1, b: 2, c: 3 };
for (const k in myObject) {
console.log(`k:${k}, v:${myObject[k]}`);
}
In Python
x = 0
while x < 11:
print(x)
x += 1
else:
print("Stop")
In Ruby
x = 0
while x < 11
puts x
x += 1
end
In JavaScript
let n = 0;
while (n < 11) {
console.log(n);
n++;
}
Breaks out of the closest enclosing loop. The same break
keyword is used in all three languages.
Skips the rest of the current iteration and goes to the top of the closeset enclosing loop.
In Python
continue
In Ruby
next
In JavaScript
continue;
In Python
def my_function(a, b, c=3):
return f"a:{a}, b:{b}, c:{c}"
print(my_function(1, 2)) # => a:1, b:2, c:3
In Ruby
def my_function(a, b, c = 3)
"a:#{a}, b:#{b}, c:#{c}"
end
puts(my_function(1, 2)) # => a:1, b:2, c:3
In JavaScript
function myFunction(a, b, c = 3) {
return `a:${a}, b:${b}, c:${c}`;
}
console.log(myFunction(1, 2)); //=> a:1, b:2, c:3
In Python
def my_function(a, b, c = 3, *args):
return f"a:{a}, b:{b}, c:{c}, args:{args}"
print(my_function(1, 2, 33, 4, 5, 6)) #=> a:1, b:2, c:33, args:(4, 5, 6)
Note *args
is treated as a tuple.
In Ruby
def my_function(a, b, c = 3, *args)
"a:#{a}, b:#{b}, c:#{c}, args:#{args}"
end
puts(my_function(1, 2, 33, 4, 5, 6)) # => a:1, b:2, c:33, args:[4, 5, 6]
Note *args
is treated as an array.
In JavaScript
function myFunction(a, b, c = 3, ...args) {
return `a:${a}, b:${b}, c:${c}, args:${args}`;
}
console.log(myFunction(1, 2, 33, 4, 5, 6)); //=> a:1, b:2, c:33, args:4,5,6
Note ...args
is a destructured array. This is known as the Rest Parameters Syntax, with ...
being referred to as the "Rest Operator".
Alternatively, we could achieve a similar result by destructuring ("spreading") an array inside a function call. In this context ...
is referred to as the Spread Operator.
Here's how that looks like:
Math.max(...[1, 2, 3]) //=> 3
In Python
def my_function(a, b, c=3):
return f"a:{a}, b:{b}, c:{c}"
print(my_function(b=2, a=1)) # => a:1, b:2, c:3
In Ruby
def my_function(a:, b:, c: 3)
"a:#{a}, b:#{b}, c:#{c}"
end
puts(my_function(b: 2, a: 1)) # => a:1, b:2, c:3
In JavaScript
JavaScript doesn’t have named/keyword parameters. The official way of simulating them is via object literals.
function myFunction({ a, b, c = 3 } = {}) {
return `a:${a}, b:${b}, c:${c}`;
}
console.log(myFunction({ b: 2, a: 1 })); //=> a:1, b:2, c:3
In Python
def my_function(a, b, c=3, **kwargs):
return f"a:{a}, b:{b}, c:{c}, kwargs:{kwargs}"
print(my_function(b=2, a=1, d=4, e=5, f=6))
# => a:1, b:2, c:3, kwargs:{'d': 4, 'e': 5, 'f': 6}
In Ruby
def my_function(a:, b:, c: 3, **kwargs)
"a:#{a}, b:#{b}, c:#{c}, kwargs:#{kwargs}"
end
puts(my_function(b: 2, a: 1, d: 4, e: 5, f: 6))
# => a:1, b:2, c:3, kwargs:{:d=>4, :e=>5, :f=>6}
In JavaScript
function myFunction({ a, b, c = 3, options = {} } = {}) {
return `a:${a}, b:${b}, c:${c}, options:${JSON.stringify(options)}`;
}
console.log(myFunction({ b: 2, a: 1, options: { d: 4, e: 5, f: 6 } }));
//=> a:1, b:2, c:3, options:{"d":4,"e":5,"f":6}
In Python
class Triangle():
def __init__(self, *sides):
self.sides = sides
def perimeter(self):
return sum(self.sides)
Triangle(2, 3, 5).perimeter() # => 10
In Ruby
class Triangle
def initialize(*sides)
@sides = sides
end
def perimeter
@sides.sum
end
end
Triangle.new(2, 3, 5).perimeter #=> 10
In JavaScript
class Triangle {
constructor(...sides) {
this.sides = sides;
}
get perimeter() {
return this.sides.reduce((partialSum, n) => partialSum + n, 0);
}
}
new Triangle(2, 3, 5).perimeter; //=> 10
In Python
class Polygon():
def __init__(self, *sides):
self.sides = sides
def perimeter(self):
return sum(self.sides)
class Triangle(Polygon):
def area(self):
pass
class Rectangle(Polygon):
def area(self):
return self.sides[0] * self.sides[1]
rectangle = Rectangle(5, 2, 5, 2)
rectangle.perimeter() # => 14
rectangle.area() # => 10
In Ruby
class Polygon
def initialize(*sides)
@sides = sides
end
def perimeter
@sides.sum
end
end
class Triangle < Polygon
def area
# ...
end
end
class Rectangle < Polygon
def area
@sides[0] * @sides[1]
end
end
rectangle = Rectangle.new(5, 2, 5, 2) #=> #<Rectangle:0x00007fd8c3033740>
rectangle.perimeter #=> 14
rectangle.area #=> 10
In JavaScript
class Polygon {
constructor(...sides) {
this.sides = sides;
}
get perimeter() {
return this.sides.reduce((partial_sum, a) => partial_sum + a, 0);
}
}
class Triangle extends Polygon {
get area() {
// ...
}
}
class Rectangle extends Polygon {
get area() {
return this.sides[0] * this.sides[1];
}
}
const rectangle = new Rectangle(5, 2, 5, 2); //=> Rectangle { sides: [ 5, 2, 5, 2 ] }
rectangle.perimeter; // 14
rectangle.area; // 10
In Python
issubclass(rectangle.__class__, Polygon)
In Ruby
rectangle.class.ancestors.include?(Polygon)
In JavaScript
rectangle instanceof Polygon;
In Python
class Polygon():
def __init__(self):
pass
def greet(self):
print('Hi from Polygon')
class Triangle(Polygon):
def greet(self):
super().greet()
print('Hi from Triangle')
Triangle().greet()
# Hi from Polygon
# Hi from Triangle
# None
In Ruby
class Polygon
def initialize; end
def greet
puts 'Hi from Polygon'
end
end
class Triangle < Polygon
def greet
super
puts 'Hi from Triangle'
end
end
Triangle.new.greet
# Hi from Polygon
# Hi from Triangle
In JavaScript
class Polygon {
constructor() {}
greet() {
console.log("Hi from Polygon");
}
}
class Triangle extends Polygon {
greet() {
super.greet();
console.log("Hi from Triangle");
}
}
new Triangle().greet();
// Hi from Polygon
// Hi from Triangle
In Python
class Triangle():
def __init__(self, *sides):
self.sides = sides
def greet(self):
return f"Hi! {self.__triangle_type()} triangle here."
def __triangle_type(self):
unique_side_count = len(set(self.sides))
if unique_side_count == 1:
return "Equilateral"
elif unique_side_count == 2:
return "Isosceles"
else:
return "Scalene"
t = Triangle(3, 3, 3)
t.greet()
# => Hi! Equilateral triangle here.
t.__triangle_type()
# => AttributeError: 'Triangle' object has no attribute '__triangle_type'
In Ruby
class Triangle
def initialize(*sides)
@sides = sides
end
def greet
"Hi! #{triangle_type} triangle here."
end
private
def triangle_type
case @sides.uniq.count
when 1 then 'Equilateral'
when 2 then 'Isosceles'
when 3 then 'Scalene'
end
end
end
t = Triangle.new(3, 3, 3)
t.greet
# => Hi! Equilateral triangle here.
t.triangle_type
# => private method `triangle_type' called for #<Triangle:0x00007fa795033dd0 @sides=[3, 3, 3]> (NoMethodError)
In JavaScript
As of today, there's no native support for private methods or properties in JavaScript.
The prevalent convention is to prefix them with an underscore (_
) to indicate they should be treated as private, however, this is just an artifact to aid communication; it has no actual effect.
class Triangle {
constructor(...sides) {
this.sides = sides;
}
greet() {
return `Hi! ${this._triangleType()} triangle here.`;
}
_triangleType() {
const uniqueSideCount = new Set(this.sides).size;
switch (uniqueSideCount) {
case 1:
return "Equilateral";
case 2:
return "Isosceles";
case 3:
return "Scalene";
}
}
}
const triangle = new Triangle(3, 3, 3);
triangle.greet();
// Hi! Equilateral triangle here.
triangle._triangleType();
// Equilateral
In Python
We'd need 2 separate files.
In extrudable.py
:
def volume(area, height):
return area * height
In circle.py
:
import extrudable
import math
class Circle():
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * (self.radius ** 2)
def volume(self, height):
return extrudable.volume(self.area(), height)
c = Circle(radius=4) #=> <__main__.Circle object at 0x7ff05587c9d0>
c.area() #=> 50.26548245743669
c.volume(height=10) #=> 502.6548245743669
In Ruby
module Extrudable
def volume(height:)
area * height
end
end
class Triangle
include Extrudable
def area
# ...
end
end
class Circle
include Extrudable
def initialize(radius:)
@radius = radius
end
def area
Math::PI * (@radius ** 2)
end
end
c = Circle.new(radius: 4)
c.area
c.volume(height: 10)
In JavaScript
We'd need 2 separate files.
In extrudable.js
:
function volume(area, height) {
return area * height;
}
module.exports = { volume };
In circle.js
:
const { volume } = require("./extrudable");
class Circle {
constructor(radius) {
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
volume(height) {
return volume(this.area(), height);
}
}
const c = new Circle(4);
c.area();
c.volume(10);
Alternatively, ES6 modules (currently experimental) could be used:
- In
extrudable.js
:export default { volume };
- In
circle.js
:import { volume } from "./extrudable";
In Python
class Triangle():
NUMBER_OF_SIDES = 3
In Ruby
class Triangle
NUMBER_OF_SIDES = 3
end
In JavaScript
There's no native support yet, however, there's an experimental feature which refers to class-level constants as a class' public static fields.
In Python
class Triangle():
NUMBER_OF_SIDES = 3
def __init__(self, *sides):
self.__validate_number_of_sides(sides)
self.sides = sides
def __validate_number_of_sides(self, sides):
if len(sides) != self.NUMBER_OF_SIDES:
raise ValueError(f"expected {self.NUMBER_OF_SIDES} sides")
Triangle(1)
# => ValueError: expected 3 sides
In Ruby
class Triangle
NUMBER_OF_SIDES = 3
def initialize(*sides)
validate_number_of_sides(sides)
@sides = sides
end
private
def validate_number_of_sides(sides)
raise ArgumentError, "invalid number of sides (expected #{NUMBER_OF_SIDES})" if sides.size != NUMBER_OF_SIDES
end
end
Triangle.new(1)
# invalid number of sides (expected 3) (ArgumentError)
In JavaScript
class Triangle {
constructor(...sides) {
this._validateNumberofSides(sides);
this.sides = sides;
}
_validateNumberofSides(sides) {
const maxNumberOfSides = 3;
if (sides.length != maxNumberOfSides) {
throw new Error(`invalid number of sides (expected ${maxNumberOfSides})`);
}
}
}
new Triangle(1);
// => Error: invalid number of sides (expected 3)