Skip to content

Instantly share code, notes, and snippets.

@quachngocxuan
Last active January 29, 2018 20:27
Show Gist options
  • Save quachngocxuan/bfcd2aede04a7d8995828c291dbcde38 to your computer and use it in GitHub Desktop.
Save quachngocxuan/bfcd2aede04a7d8995828c291dbcde38 to your computer and use it in GitHub Desktop.
Learn enough to be dangerous - Ruby

Learn enough to be dangerous - Ruby

Symbol

Ruby has a data type not often seen in other programming languages, and that’s the symbol. Symbols are similar to strings in that they are made of characters, but instead of being surrounded by quotes, symbols are prefixed with a colon

:name

Symbols are typically used as identifiers.

Comparison method

<=> return -1 if x < y return 0 if x = y return 1 if x > y

a.sort { |x, y| x <=> y }

Assignment

x ||= 6	# assign 6 if x is nil or false

Separators between command in the same line

p a; p b; p c; p d; p e

(p is a shorthand of print)

String

Strings are objects of the String class

my_string = "Hello." # create a string from a literal
my_empty_string = String.new # create an empty string
my_copied_string = String.new(my_string) # copy a string to a new variable

With encoding

str = "With ♥!"
print("My String's encoding: ", str.encoding.name) 
print("\nMy String's size: ", str.size)
print("\nMy String's bytesize: ", str.bytesize)

Some more actions

str[0,4] # first four characters
str[str.size, 0] = " World!" # append by assigning at the end of the string
str[5, 0] = "," # insert a comma after the 5th position
str[5, 6] = ""  # delete 6 characters starting from index 5. 
str[5,1] = " World" # replace the string starting from index 5 and of length 1 with the given string. 
puts "Hi" * 3	# "HiHiHi"

With long string and free of escape characters, use this format

document = <<-HERE         # We begin with <<- followed by the ending delimiter HERE
This is a string literal.
It has two lines and abruptly ends with a newline...
HERE

Clean string

Chomp

> "Hello World!  \r\n".chomp
"Hello World!  "
> "Hello World!".chomp("orld!")
"Hello W"
> "hello \n there".chomp
"hello \n there"

Strip

> "    hello    ".strip
"hello"
> "\tgoodbye\r\n".strip
"goodbye"

Chop: Returns a new string with the last character removed. Note that carriage returns (\n, \r\n) are treated as single character

> "string\n".chop
"string"
> "string".chop
"strin"
arr.map(&:strip).join(' ')

String finding

Include

> "hello".include? "lo"   #=> true 

Gsub: String.gsub(pattern, <hash|replacement>)

"hello".gsub(/[aeiou]/, '*')                  #=> "h*ll*"
"hello".gsub(/([aeiou])/, '')             #=> "hll"

Interpolation - insert value in a string

print "Hello #{name}"

Iterate

money.each_char {|x| p x} # prints each character
money.each_byte {|x| p x} # first char represented by two bytes
money.each_line {|line| p line}

Block

. Passing blocks is one way to pass functions as arguments to other functions. . Blocks are not objects, and they can't be saved to variables. https://www.hackerrank.com/challenges/ruby-blocks/problem

def call_block
	print "start"
	yield
	print "end"
end
call_block do
	print "inside"
end
def calculate(a, b)
	yield(a, b)
end

print calculate(15, 10) {|a, b| a-b}

Hash

Define a hash

h = Hash.new
h = Hash.new(1)	# key = 1
h = {"simple" => 1, "complex" => 2}
h["simple"] = 1

manipulate

h.store(key, value)	# is identical to h[key] = value
h.delete(key)
h.keep_if {|key, value| key % 2 == 0}
h.delete_if {|key, value| key % 2 == 0}

get keys, values

h.keys
h.values

merge

> another_guy.merge job: "none"
=> {:name=>"Ben", :age=>20, :job=>"none"}

loop through a hash

h.each do |key, value|
	# codes
end

h.each do |arr|
	# arr[0] is key, arr[1] is value
end

Array

a = Array.new
a = []
a = Array.new(1)	# initialize with one nil element
a = [nil]
a = Array.new(2, 10)	# array of 2 elements with value 10
a[0]
a.at[0]
a[1..3]
a[1...3] # last index is excluded
a[1, 4] # start index is 1 and 4 is length of range
a[-1]	# last element
a.first
a.first(10)
a.last
a.take(3)	# take 3 first elements
a.drop(3)	# every elements but 3 first elements
a.push(1)
a << 1
a.insert(2, 2.5)	# insert at index 2
a.unshift(1, 2, 3)	# insert some elements at the start of array
a.pop	# delete an element from the end of the array
a.shift	# delete first element
a.delete_at(2)
arr.delete(5)	# delete all occurrences of a given element
> list + [4, 5, 6]
=> [1, 2, 3, 4, 5, 6]

Filter

arr.select{|a| a > 2}
arr.reject{|a| a > 2}
a.keep_if {|a| a % 2 == 0}
a.delete_if {|a| a % 2 == 0} 

Check present or none of element

sentences = [[]]
if sentences.first.present?
if sentences.first.none?
# Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
# By default column length +len+ equals 72 characters and indent
# +indent+ equal two spaces.
#
#   my_text = 'Here is a sample text with more than 40 characters'
#
#   format_paragraph(my_text, 25, 4)
#   # => "    Here is a sample text with\n    more than 40 characters"
def format_paragraph(text, len = 72, indent = 2)
  sentences = [[]]

  text.split.each do |word|
    if sentences.first.present? && (sentences.last + [word]).join(' ').length > len
      sentences << [word]
    else
      sentences.last << word
    end
  end

  indentation = " " * indent
  sentences.map! { |sentence|
    "#{indentation}#{sentence.join(' ')}"
  }.join "\n"
end

#map! to join the words with a space

Transpose to convert row into column

a = [1, 2, 3]
b = [4, 5, 6]
c = [7, 8, 9]

[a, b, c].transpose.map { |x| x.reduce :+ }
# => [12, 15, 18]

Proc

Proc objects are blocks of code that can be bound to a set of local variables. You can think of a proc object as a "saved" block. https://www.hackerrank.com/challenges/ruby-procs/problem

def foo(a, b, my_proc)
	my_proc.call(a, b)
end

add = proc {|x,y| x + y}
foo(10, 20, add)

Turns the symbol into a simple proc

(which is especially useful for enumerations) https://stackoverflow.com/questions/9429819/what-is-the-functionality-of-operator-in-ruby

my_array = gets.split().map(&:to_i)
# is identical to (& 
my_array = gets.split().map(|a| a.to_i)
my_proc = Proc.new { puts "foo" }

my_method_call(&my_proc) # is identical to:
my_method_call { puts "foo" }

Higher order constructs

map

http://flats.github.io/blog/2015/12/02/getting-to-know-rubys-map-and-reduce/

toppings = %w(perpperoni mushroom bacon)
def pizza(toppings)
	toppings.map do |topping|
		"I love #{topping} pizza"
	end
end
# => return array of strings

ROT 13 encrypt/decrypt: https://www.hackerrank.com/challenges/ruby-enumerable-collect/problem

def rot13(secret_messages)
  # your code here
  secret_messages.map {|str|
    str.split('').map {|c|
      if c.between?('A', 'M') or c.between?('a', 'm')
        (c.ord+13).chr
      elsif c.between?('N', 'Z') or c.between?('n', 'z')
        (c.ord-13).chr
      else
        c
      end
    }.join('')
  }
end

reduce & inject

http://flats.github.io/blog/2015/12/02/getting-to-know-rubys-map-and-reduce/ reduce (or inject) returns a value that is the result of applying a binary operation to the return value of applying the supplied block to each element of the enumerable. Whoa. What a mouthful. In other words, #reduce “reduces” each element of an enumerable to a single value, accumulates that value in a single variable, and then returns the value of the accumulator.

array = [1, 2, 3, 4] # => [1, 2, 3, 4]
array.reduce(0, :+) => 10

is the same as detail form

array.reduce(0) { |sum, element| sum + element } # => 10 with initial value is 0

If you don’t declare an initial value, the initial value will be the first element of the collection.

Inject

module_name = "X::Y::Z"
module_name.split('::').inject([]) { |memo,x| memo.unshift(memo.empty? ? x : "#{memo[0]}::#{x}") }
=> ["X::Y::Z", "X::Y", "X"]

sum of an array

return a.inject :+

collect

The same as map but it is used for hashes. Collect imtes into an array

>>> [1,2,3].map { |x| 2*x }
=> [2, 4, 6]
>>> {:a=>1, :b=>2, :c=>3}.collect { |key, value| 2*value }
=> [2, 4, 6]

Method

when you write

def hello_world
    'Eha! Ruby'
end

You are essentially adding a private method to Object class

class Object
    private

    def hello_world2
        'Eha! Ruby'
    end
end

These methods, unlike functions in other object oriented programming language (e.g., Python) are not a first-class citizens, because they cannot be passed to other methods as arguments, returned by other methods, or assigned to variables.

Method arguments Default value

def take(arr, omit=1)

Multiple arguments

def take(arr, *rest)

Hash arguments

def fetch_file(uri, options)
    if options.has_key?(:proxy)
        # do something
    end
end
def foo(x, str: "foo", num: 424242)
  [x, str, num]
end

foo(13) #=> [13, 'foo', 424242]
foo(13, str: 'bar') #=> [13, 'bar', 424242]
def foo(str: "foo", num: 424242, **options)
  [str, num, options]
end

foo #=> ['foo', 424242, {}]
foo(check: true) # => ['foo', 424242, {check: true}]

Lambdas

https://www.hackerrank.com/challenges/ruby-lambdas/problem

Lambdas are anonymous functions. Lambdas in Ruby are objects of the class Proc.

#Ruby version <= 1.8
lambda { .... } 

lambda do
	....
end

#Ruby version >= 1.9, "stabby lambda" syntax is added
-> { .... }

-> do
	....
end

Ruby version >= 1.9 can use both lambda and stabby lambda, ->.

Lambda that takes no arguments.

def area (l, b)
   -> { l * b } 
end

x = 10.0; y = 20.0

area_rectangle = area(x, y).call
area_triangle = 0.5 * area(x, y).()

puts area_rectangle     #200.0
puts area_triangle      #100.0

Lambda that takes one or more arguments.

area = ->(a, b) { a * b }

x = 10.0; y = 20.0

area_rectangle = area.(x, y)
area_triangle = 0.5 * area.call(x, y)

puts area_rectangle     #200.0
puts area_triangle      #100.0    

Yes, there is difference between a proc and a lambda in Ruby. Equivalent to Proc.new, except the resulting Proc objects check the number of parameters passed when called.

Closure

https://www.hackerrank.com/challenges/ruby-closures/problem Closure is a function/method that: . Can be passed around like an object. It can be treated like a variable, which can be assigned to another variable, passed as an argument to a method. . Remembers the value of variables no longer in scope. It remembers the values of all the variables that were in scope when the function was defined. It is then able to access those variables when it is called even if they are in a different scope.

def plus_1(y)
	x = 100
	y.call	#remembers the value of x = 1
end

x = 1
y = -> { x + 1 }
puts plus_1(y)	# 2

Blocks, Procs and Lambdas are closures in Ruby.

Check which block is given to a method or not by block_given

def block_message_printer
    message = "Welcome to Block Message Printer"
    if block_given?
        yield
    end
  puts "But in this function/method message is :: #{message}"
end

message = gets
block_message_printer { puts "This message remembers message :: #{message}" }

Lazy evaluation

Lazy evaluation is an evaluation strategy that delays the assessment of an expression until its value is needed. Ruby 2.0 introduced a lazy enumeration feature. Lazy evaluation increases performance by avoiding needless calculations, and it has the ability to create potentially infinite data structures.

power_array = -> (power, array_size) do 
    1.upto(Float::INFINITY).lazy.map { |x| x**power }.first(array_size) 
end

puts power_array.(2 , 4)    #[1, 4, 9, 16]
puts power_array.(2 , 10)   #[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
puts power_array.(3, 5)     #[1, 8, 27, 64, 125]

In this example, lazy avoids needless calculations to compute power_array. If we remove lazy from the above code, then our code would try to compute all x ranging from 1 to Float::INFINITY. To avoid timeouts and memory allocation exceptions, we use lazy. Now, our code will only compute up to first(array_size).

Class

Properties

object properties

@a

class properties

@@a

Constructor

def initialize(params)

Make class can be enumerable

require 'forwardable'

class Results
  include Enumerable
  extend Forwardable
  def_delegators :@result_array, :each, :<<
  
  def initialize()
    @result_array = ["Hello", "World"]
  end
end

obj = Results.new()
obj.each {|a| puts a}

Create new object

obj = ClassName.new

Enumerable

Checking value and value range

a?1	# return True or False
a.range?(1,10)
a.range?1,10
14.between?(10,20) # true

(10..20).member?(14) # true

(10..20).include?(14) # true

group_by

Used with data collections is one which groups the elements according to some evaluation result.

> (1..5).group_by {|x| x%2}
{1=>[1,3,5], 0=>[2, 4]}
def group_by_marks(marks, pass_marks)
  # your code here
  marks.group_by {|key, value| value >= pass_marks ? "Passed" : "Failed"}
end

Iterate with index

One of such useful methods is each_with_index which allows you to iterate over items along with an index keeping count of the item.

> colors = ['red', 'green', 'blue']
> colors.each_with_index { |item, index| p "#{index}:#{item}" }
"0:red"
"1:green"
"2:blue"

'any', 'all', 'none', and 'find'

https://www.hackerrank.com/challenges/ruby-enumerable-any-all-none-find/problem

> arr = [1, 2, 3, 4, 5, 6]
=> [1, 2, 3, 4, 5, 6]
> h = {"a" => 1, "b" => 2, "c" => 3}
=> {"a" => 1, "b" => 2, "c" => 3}
> arr.any? {|a| a % 2 == 0} # checks if any number in the array is even
=> True
> h.any? {|key, value| value.is_a? String} # checks if any value of the Hash object is of the type String
=> False
> arr.all? {|a| a.is_a? Integer} # checks if all elements of the array are of the type Integer
=> True
> h.all? {|key, value| key.is_a? String} # checks if all keys of the Hash object are of the type String
=> True
> arr.none? {|a| a.nil?} # Checks if none of the elements in the array are of nil type
=> True
> h.none? {|key, value| value < 3} # checks if all values of the Hash object are less than 3
=> False
> arr.find {|a| a > 5} # returns the first element greater than 5 and `nil` if none satisfies the condition
=> 6
> h.find {|key, value| key == "b"} # returns an Array of the first match [key, value] that satisfies the condition and nil otherwise
=> ["b", 2]

File processing

file = File.read("test.txt")
puts file
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment