Tag Archives: Ruby Tuesday

Ruby Tuesday – Array#join

Today’s Ruby Tuesday is on Array#join

["Now", "is", "the", "time", "for", "all", "good", "people"].join(" ")
# "Now is the time for all good people"
["1", "2", "3", "4", "5"].join("x")
# "1x2x3x4x5"

If the array contains items that are not strings, they will be coerced to strings and then joined together.

[1, 2, 3, 4, 5].join(" ")
"1 2 3 4 5"
[1, 2, 3, 4, 5].join("x")
# "1x2x3x4x5"
[1, 2, 3, 4, 5].join
# "12345"

If no argument is passed to Array#join, it uses the value set in $,.

$, = "n"
# "n"
[1, 2, 3, 4, 5].join
# "1n2n3n4n5"

–Proctor

Ruby Tuesday – String#split

Today’s Ruby Tuesday is on String#split.

String#split takes a pattern as it’s parameter, and uses that pattern as the deliminator[1] to split the string into an Array of tokens.

"It was the best of times, it was the worst of times".split
# => ["It", "was", "the", "best", "of", "times,", "it", "was",
#     "the", "worst", "of", "times"]
"It was the best of times, it was the worst of times".split(",")
# => ["It was the best of times", " it was the worst of times"]

If String#split is called with a single space as the pattern, it removes any leading and trailing whitespace, but if another pattern is specified, it will return empty strings representing a sequence of the deliminators.

"continuous        whitespace".split
# => ["continuous", "whitespace"]
" t  continuous t     whitespace t".split(" ")
# => ["continuous", "whitespace"]
"fizzle".split("z")
# => ["fi", "", "le"]
"continuous        whitespace".split(/s/)
# => ["continuous", "", "", "", "", "", "", "", "whitespace"]

String#split can take a regular expression as the pattern in addition to taking a string.

"fizzle".split(/z/)
# => ["fi", "", "le"]
"fizZle".split(/[zZ]/)
=> ["fi", "", "le"]

If an empty pattern is passed to String#split then the string is split into a list of it’s characters.

"fizzle".split(//)
# => ["f", "i", "z", "z", "l", "e"]
"banana".split("")
=> ["b", "a", "n", "a", "n", "a"]

String#split can take a second argument, which is a limit to the number of items returned in the array. A value of zero, behaves as if the limit was not passed as an argument. If the value for limit is positive it will return at most that many elements total, and will include any trailing empty strings, or “null fields” as called by the Ruby documentation, up to the limit. If a negative integer is passed as a value, all of the trailing empty strings that tokenized will be returned.

"banana".split("a")
# => ["b", "n", "n"]
"banana".split("a", 0)
# => ["b", "n", "n"]
"banana".split("a", 1)
# => ["banana"]
"banana".split("a", 2)
# => ["b", "nana"]
"banana".split("a", 4)
# => ["b", "n", "n", ""]
"banana".split("a", 7)
# => ["b", "n", "n", ""]
"bananaaa".split("a", 7)
# => ["b", "n", "n", "", "", ""]
"banana".split("a", -1)
# => ["b", "n", "n", ""]
"banana".split("a", -2)
# => ["b", "n", "n", ""]
"banana".split("a", -4)
# => ["b", "n", "n", ""]
"bananaaaaaaa".split("a", -1)
# => ["b", "n", "n", "", "", "", "", "", "", ""]

–Proctor


[1] Deliminator – A portmanteau of delimiter and eliminate, signifying that the delimiter is to be removed from the results. back

Ruby Tuesday – Enumerable#drop_while

Today’s Ruby Tuesday entry is on Enumerable#drop_while.

Enumerable#drop_while drops items from the beginning of the enum up to, but not including, the first element for which the block returns a non-truthy value.

[1, 2, 3, 4, 5, 6].drop_while {|item| item < 5}
# => [5, 6]
["a", "foo", "snafu"].drop_while {|item| item.length < 3}
=> ["foo", "snafu"]
[1, 4, 2, 5, 3, 6].drop_while {|item| true}
# => []
[1, 4, 2, 5, 3, 6].drop_while {|item| false}
# => [1, 4, 2, 5, 3, 6]
[].drop_while{|item| true }
# => []
[].drop_while{|item| false }
# => []

Evaluation stops, and does not check the rest of the list if a falsey value is returned. This will leave other values in the list that might return a falsey value, unlike Enumerable#reject which removes all items from the enum.

[1, 4, 2, 5, 3, 6].drop_while {|item| item < 5}
# => [5, 3, 6]

Enumerable#drop_while can also leave values in your enum that would normally cause the block to error out, because the evaluation against the list stops after the first falsey value is encountered.

["a", "foo", "snafu", "b", :c].drop_while {|item| item.length < 3}
# => ["foo", "snafu", "b", :c]

–Proctor

Ruby Tuesday – Enumerable#select

Today’s Ruby Tuesday is on Enumerable#select.

Enumerable#select iterates over a list, passing each element to the provided block, and returns an array of those items for which the block returns a truthy value.

[1, 2, 3, 4, 5].select &:even?
# => [2, 4]
[1, 2, 3, 4, 5].select {|item| item.even?}
# => [2, 4]
["a", "foo", "snafu", "rb", "qwerty"].select {|item| item.length > 3}
# => ["snafu", "qwerty"]

If no items are found to meet the criteria, Enumerable#select returns an empty array.

[1, 2, 3, 4, 5].select {|item| item > 25 }
# => []

Enumerable#reject

Enumerable#select has a counterpart of Enumerable#reject which returns an array of items for which the block returns a falsey value.

[1, 2, 3, 4, 5].reject &:even?
# => [1, 3, 5] 
[1, 2, 3, 4, 5].reject {|item| item.even?}
# => [1, 3, 5]
["a", "foo", "snafu", "rb", "qwerty"].reject {|item| item.length > 3}
# => ["a", "foo", "rb"]

If all items are found to meet the criteria, Enumerable#reject returns an empty array.

[1, 2, 3, 4, 5].reject {|item| item > 0 }
# => []

–Proctor

Ruby Tuesday – Net::HTTP::get

Today’s Ruby Tuesday is a short one, it is on Net::HTTP::get.

There are times when you need a very basic ability to make a HTTP call, and Net::HTTP::get can help you with that.

To first be able to use Net::HTTP::get you need to require net/http.

require 'net/http'

Net::HTTP::get takes either a URI object, or a hostname, path and optional port, which defaults to port 80 if not specified. Net::HTTP::get returns a string that is the body of the response.

Net::HTTP.get(URI('http://www.example.com'))
# => "<!doctype html>n<html>n<head>n    <title>Example Domain</title>nn    <meta charset="utf-8" />n    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />n    <meta name="viewport" content="width=device-width, initial-scale=1" />n    <style type="text/css">n    body {n        background-color: #f0f0f2;n        margin: 0;n        padding: 0;n        font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;n        n    }n    div {n        width: 600px;n        margin: 5em auto;n        padding: 50px;n        background-color: #fff;n        border-radius: 1em;n    }n    a:link, a:visited {n        color: #38488f;n        text-decoration: none;n    }n    @media (max-width: 700px) {n        body {n            background-color: #fff;n        }n        div {n            width: auto;n            margin: 0 auto;n            border-radius: 0;n            padding: 1em;n        }n    }n    </style>    n</head>nn<body>n<div>n    <h1>Example Domain</h1>n    <p>This domain is established to be used for illustrative examples in documents. You may use thisn    domain in examples without prior coordination or asking for permission.</p>n    <p><a href="http://www.iana.org/domains/example">More information...</a></p>n</div>n</body>n</html>n"
Net::HTTP.get('www.example.com', '/')
# => "<!doctype html>n<html>n<head>n    <title>Example Domain</title>nn    <meta charset="utf-8" />n    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />n    <meta name="viewport" content="width=device-width, initial-scale=1" />n    <style type="text/css">n    body {n        background-color: #f0f0f2;n        margin: 0;n        padding: 0;n        font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;n        n    }n    div {n        width: 600px;n        margin: 5em auto;n        padding: 50px;n        background-color: #fff;n        border-radius: 1em;n    }n    a:link, a:visited {n        color: #38488f;n        text-decoration: none;n    }n    @media (max-width: 700px) {n        body {n            background-color: #fff;n        }n        div {n            width: auto;n            margin: 0 auto;n            border-radius: 0;n            padding: 1em;n        }n    }n    </style>    n</head>nn<body>n<div>n    <h1>Example Domain</h1>n    <p>This domain is established to be used for illustrative examples in documents. You may use thisn    domain in examples without prior coordination or asking for permission.</p>n    <p><a href="http://www.iana.org/domains/example">More information...</a></p>n</div>n</body>n</html>n"
[11] pry(main)> res = Net::HTTP.get('www.example.com', '/')
=> "<!doctype html>n<html>n<head>n    <title>Example Domain</title>nn    <meta charset="utf-8" />n    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />n    <meta name="viewport" content="width=device-width, initial-scale=1" />n    <style type="text/css">n    body {n        background-color: #f0f0f2;n        margin: 0;n        padding: 0;n        font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;n        n    }n    div {n        width: 600px;n        margin: 5em auto;n        padding: 50px;n        background-color: #fff;n        border-radius: 1em;n    }n    a:link, a:visited {n        color: #38488f;n        text-decoration: none;n    }n    @media (max-width: 700px) {n        body {n            background-color: #fff;n        }n        div {n            width: auto;n            margin: 0 auto;n            border-radius: 0;n            padding: 1em;n        }n    }n    </style>    n</head>nn<body>n<div>n    <h1>Example Domain</h1>n    <p>This domain is established to be used for illustrative examples in documents. You may use thisn    domain in examples without prior coordination or asking for permission.</p>n    <p><a href="http://www.iana.org/domains/example">More information...</a></p>n</div>n</body>n</html>n"
res.class
# => String

Be warned, that this is for very simple cases, and the documentation notes that this is not recommended if you are doing performing many HTTP requests, as it does not persist the connection.

If you need more features or a higher level of abstraction for making HTTP requests I would suggest checking out Faraday or Typhoeus, but I do want to call out that there is Net::HTTP::get if you need it.

–Proctor

Ruby Tuesday – Proc#call

Today’s Ruby Tuesday is Proc#call.

Procs are blocks of code that are bound to local variables, and as such, we need a way to be able to invoke them at a later time. Enter Proc#call.

Proc#call takes a list of arguments which are then used as the arguments to the Proc that call is being invoked on.

Proc.new {|x| x * x }.call 9
# => 81
Kernel.proc{|x| x * x }.call 11
# => 121
lambda {|x| x * x }.call 7
# => 49
->(x){x * x}.call 5
# => 25
->(x){x * x}.call 5
# => 25

If Proc#call is invoked with extra arguments, it will either discard the extra arguments, or raise an error if it was created as a lambda.

Proc.new {|x| x * x }.call 9, 3
# => 81
Kernel.proc{|x| x * x }.call 11, 15
# => 121
lambda {|x| x * x }.call 7, 13
# ArgumentError: wrong number of arguments (2 for 1)
# from (pry):94:in `block in __pry__'
->(x){x * x}.call 5, 3
# ArgumentError: wrong number of arguments (2 for 1)
# from (pry):93:in `block in __pry__'

Proc#call also has some other syntactic sugar providing alias for calling the method. You can use [], .(), or even the method Proc#yield as variations of Proc#call.

Proc.new {|x| x * x }[9]
=> 81
Proc.new {|x| x * x }.(9)
=> 81
Proc.new {|x| x * x }.yield 9
=> 81

As an extra bonus, there is also the method Method#call that can be used when you have an named method and you have a Method object for it. This call method behaves in the manner of lambda in that it expects the correct number of arguments to be passed to it.

def square(x)
  x * x
end

method(:square).call 2
# => 4
method(:square).call 2, 4
# ArgumentError: wrong number of arguments (2 for 1)
# from (pry):78:in `square'

–Proctor

Ruby Tuesday – Benchmark::realtime

Today’s Ruby Tuesday is on Benchmark::realtime.

How many times have you written this Ruby method?

def time_it
  start = Time.now  
  yield
  Time.now - start
end

And truthfully, how many different times have you seen this method defined in the same application even?

We all know this method, and have likely written it, even if it was in other programming languages. It is the standard, “How long is this part of the code taking?” wrapper that we decide we should generalize into a method.

time_it { (1..20000).map{|x| x * x * x} }
# => 0.004334
time_it { (1..200000).map{|x| x * x * x} }
# => 0.029896
time_it { (1..2000000).map{|x| x * x * x} }
# => 0.414218
time_it { (1..20000000).map{|x| x * x * x} }
# => 8.827975

There is good news about this though. The Ruby language team has written this for you and all you have to do is require 'benchmark' to get access to it. It is Benchmark::realtime.

require 'benchmark'
# => true
Benchmark.realtime { (1..20000000).map{|x| x * x * x} }
# => 6.681451

And make sure to check out the Benchmark module for other methods such as measure, which gives you a breakdown of the user CPU time, the system CPU time, total CPU time, total elapsed time and time of children processes.

Benchmark.measure { (1..20000000).map{|x| x * x * x} }
# => #<Benchmark::Tms:0x007f89ce401cf8
#  @cstime=0.0,
#  @cutime=0.0,
#  @label="",
#  @real=13.406651,
#  @stime=3.1399999999999997,
#  @total=12.549999999999997,
#  @utime=9.409999999999997>

–Proctor

Ruby Tuesday – Enumerable#any?

This weeks Ruby Tuesday entry covers Enumerable#any?.

Enumerable#any? takes a block and returns true if the block returns a truthy value for any of the elements in the enumeration, otherwise it returns false.

[1, 2, 3, 4, 5, 6].any? {|x| x.even?}
# => true
["foo", "bar", "snafu", "Snuffleupagus"].any? {|x| x.size > 10}
# => true
["foo", "bar", "snafu", "Snuffleupagus"].any? {|x| x.size < 2}
#=> false

The block is optional for Enumerable#any?, and if not specified, any uses the block {|obj| obj} by default.

[nil, nil, nil].any?
# => false
[false, nil].any?
# => false
[1, 2, 3].any?
# => true

Enumerable#any? is eager, so if it finds an element where the block evaluates to a truthy value it stops processing the rest of the enumerable.

Benchmark.realtime{  (1..20000000).lazy.select(&:even?).any?{|x| x.even? } }
# => 3.6e-05
Benchmark.realtime{  (1..20000000).lazy.select(&:even?).any?{|x| x.odd? } }
# => 5.709414

Ruby also has counterparts for any? which are Enumerable#all? and Enumerable#none?. These behave the similiar as Enumerable#any?, except that Enumerable#all? checks to that the return value of the block is truthy for all elements.

[1, 3, 5, 8].all?{|x| x.odd?}
# => false
[1, 3, 5, 81].all?{|x| x.odd?}
# => true
["foo", "bar", "snafu", "Snuffleupagus"].all?{|x| x.size > 10}
# => false
["foo", "bar", "snafu", "Snuffleupagus"].all?{|x| x.size > 2}
# => true
[true, true, true].all?
# => true
["false", true, true].all?
# => true
[true, true, nil].all?
# => false
[false, nil].all?
# => false

While Enumerable#none checks the that return value of the block is a falsey value for all elements.

[1, 3, 5, 7, 9].none?{|x| x.even?}
# => true
[1, 3, 5, 7, 8].none?{|x| x.even?}
# => false
[true, true, true].none?
# => false
[nil, nil, false, nil].none?
# => true

Both Enumerable#all? and Enumerable#none? are both eager as well.

Benchmark.realtime{  (1..20000000).lazy.select(&:even?).all?{|x| x.even? } }
# => 5.250464
Benchmark.realtime{  (1..20000000).lazy.select(&:odd?).all?{|x| x.even? } }
# => 3.3e-05
Benchmark.realtime{  (1..20000000).lazy.select(&:odd?).none?{|x| x.even? } }
# => 5.365924
Benchmark.realtime{  (1..20000000).lazy.select(&:even?).none?{|x| x.even? } }
# => 3.7e-05

–Proctor

Ruby Tuesday – Enumerable#partition

Today’s Ruby Tuesday method is Enumerable#partition.

Enumerable#partition takes a block and returns an array of two arrays. The first item in the returned array is an array of items for which the block returns a truthy value. The second item in the returned array are the items for which the block returns a falsey value.

[:a, 1, "b", :c, [1, 2, 3,]].partition{|x| x.is_a? Symbol}
# => [[:a, :c], [1, "b", [1, 2, 3]]]
[1, 2, 3, 4, 5, 6, 7].partition{|x| x.even?}
# => [[2, 4, 6], [1, 3, 5, 7]]
["foo", "snafu", "bar", "hello world"].partition{|x| x.size < 5}
# => [["foo", "bar"], ["snafu", "hello world"]]
["foo", "snafu", "bar", "hello world"].partition{|x| x.size < 15}
# => [["foo", "snafu", "bar", "hello world"], []]
[1, 2, 3, 4].partition{|x| x}
# => [[1, 2, 3, 4], []]
[1, 2, 3, 4].partition{|x| nil}
# => [[], [1, 2, 3, 4]]

–Proctor

Ruby Tuesday – Enumerable#zip

Today’s Ruby Tuesday covers Enumerable#zip.

Enumerable#zip weaves together elements of multiple Enumerables. It iterates through each item in the enum and combines it with the corresponding element from each of the arguments passed to zip, resulting in an array of arrays.

[:a, :b, :c].zip([1, 2, 3])
# => [[:a, 1], [:b, 2], [:c, 3]]
[:a, :b, :c].zip([1, 2, 3], [:foo, :bar, :baz])
# => [[:a, 1, :foo], [:b, 2, :bar], [:c, 3, :baz]]

If an item in the argument list is shorter then the enum that zip is being called on, nil is used where the item would have been added.

[:a, :b, :c].zip([1], [:foo, :bar, :baz, :snafu])
# => [[:a, 1, :foo], [:b, nil, :bar], [:c, nil, :baz]]

The length of the returned array is the length of the enum that zip is being called on. Any extra elements in the enumerable passed to zip are ignored, and if are in a lazy enumerble not computed.

[:a, :b, :c].zip([1, 2, 3, 4], [:foo, :bar, :baz, :snafu])
# => [[:a, 1, :foo], [:b, 2, :bar], [:c, 3, :baz]]

### Only takes what is needed ###
require 'benchmark'
# => true
Benchmark.measure do
  [:a, :b].zip( (1..1_000_000_000_000).lazy.map{|x| x*x*x} ) 
end
# => #<Benchmark::Tms:0x007fd5749d9288
#  @cstime=0.0,
#  @cutime=0.0,
#  @label="",
#  @real=4.3e-05,
#  @stime=0.0,
#  @total=0.0,
#  @utime=0.0>

#### For much much less, took 8+ seconds ####
Benchmark.measure do
  [:a, :b].zip( (1..10_000_000).map{|x| x*x*x} )   
end
# => #<Benchmark::Tms:0x007faed4779e00
#  @cstime=0.0,
#  @cutime=0.0,
#  @label="",
#  @real=8.616259,
#  @stime=0.7200000000000002,
#  @total=8.53,
#  @utime=7.809999999999999>

Enumerable#zip can also be given a block. If a block is provided, the combined array of items is passed to the block, and not returned as a result. This behavior is distinct from Enumerable#each in that each still returns the Enumerable it was invoked on, where as zip returns nil as it’s result when a block is given.

[:a, :b, :c].zip([1, 2, 3, 4], [:foo, :bar, :baz, :snafu]) { |items| puts items.to_s }
# [:a, 1, :foo]
# [:b, 2, :bar]
# [:c, 3, :baz]
# => nil
[:a, :b, :c].zip([1, 2, 3, 4], [:foo, :bar, :baz, :snafu]).each { |items| puts items.to_s }
# [:a, 1, :foo]
# [:b, 2, :bar]
# [:c, 3, :baz]
# => [[:a, 1, :foo], [:b, 2, :bar], [:c, 3, :baz]]

–Proctor