Ruby - Understanding Enumerable
Ruby - Understanding Enumerable
by Pete Nicholls on 17 Oct 2013 at Christchurch Ruby The first few sections are aimed at newcomers to Ruby, so feel free to skip it if you already know it. Enumerable is a fascinating mixin. ts use ma!es for unusual" characteristic" an# ex$ressi%e Ruby co#e. n this tal!" &e'll ta!e a loo! at Ruby's Array class" #iscuss &hy Rubyists #on't use for" an# learn ho& to count the uncountable.
Forming arrays
(hen first starting &riting Ruby" remember being im$resse# &ith the brea#th an# ex$ressi%eness of the stan#ar# library. Perha$s none stoo# out to me more than the Array class. )et's loo! at a fe& exam$les. *ou can try any of these exam$les using the interacti%e console that shi$s &ith Ruby" irb. +ust ty$e irb in a terminal an# hit return to start an interacti%e Ruby session. )et's start by creating an array,
parents = ["Ned", "Catelyn"] # => ["Ned", "Catelyn"]
Nothing out of the or#inary here" but there's a sim$ler &ay to &rite arrays of &hites$acese$arate# %alues,
children = %w( Robb ansa Arya !ran Ric"on # # => ["Robb", " ansa", "Arya", "!ran", "Ric"on"]
0ccessing items,
star"s&first star"s&at('# star"s[%(] # star"s['&&)] # => "Ned" # => " ansa" => "Ric"on" # => [" ansa", "Arya", "!ran"]
n the last exam$le" '&&) is a Ran*e. (e'll come bac! to those later.
Array iteration
Ruby has a for loo$,
for na+e in star"s p,ts na+e end
*ou &on't often see it use#" though. nstea#" most Rubyists &ill use each,
star"s&each do -na+ep,ts na+e end
1here's an im$ortant #istinction" an# &e'll come bac! to it later. 2or the moment" 3ust ta!e a loo! at this exam$le. 4et&een the do an# end &e ha%e a bloc!. *ou can thin! of a bloc! as an anonymous function or a closure" if you're familiar &ith those conce$ts. Other&ise" 3ust thin! of it as a 5bloc!6 of co#e. 1he bloc! gets execute# once $er item in the array. 0nother &ay to &rite a bloc! is &ith curly braces. 1he follo&ing is i#entical to the last exam$le,
star"s&each . -na+e- p,ts na+e /
(hen using select" the return value of the bloc! &ill be e%aluate# to #etermine &hich elements are returne# in the ne& array. n Ruby" all metho#s an# bloc!s ha%e a return %alue" in this case the last line of the bloc!.
is a%ailable,
star"s&sort # => ["Arya", "!ran", "Catelyn", "Ned", "Ric"on", "Robb", " ansa"]
4ecause most metho#s on array return another array" you can chain metho#s together,
star"s&sort&re6erse # => [" ansa", "Robb", "Ric"on", "Ned", "Catelyn", "!ran", "Arya"]
f you &ant to call a single metho# on each item" there's a shorthan# syntax for that,
star"s&sort0by(78len*th#
&on't #iscuss ho& this &or!s" but it's $retty neat if you &ant to fin# out for yourself.
Transforming arrays
(hat if &e &ante# to transform all the names/
star"s&+ap . -star"- star"&,pcase / # => ["N9:", "CA;9<=N", "R>!!", " AN A", "AR=A", "!RAN", "R?C@>N"]
)et's a## the last name to each member of the .tar! house. 1his time" &e're going to use +ap's brother" +apA,
star"s&+apA . -first0na+e- "#.first0na+e/ # => ["Ned tar"", "Catelyn tar"", "Robb tar"", "!ran tar"", "Ric"on tar""] tar"" / tar"", " ansa tar"", "Arya
1he #ifference bet&een +ap an# +apA is that +ap &ill return a new array" &hile +apA &ill mo#ify the existing array. n the same &ay" select an# re5ect ha%e selectA an# re5ectA counter$arts that mo#ify the original array rather than retuen a ne& one. Ruby Tip: 1he exclamation mar! 9also !no&n as the bang:" is a $erfectly %ali# character to use as $art of a metho# name. Rubyists ten# to use exclamation mar!s at the en# of their metho#s to let other #e%elo$ers !no& that the metho# is #angerous. t might be ma!ing $ermanent changes to your ob3ect" #atabase" or #oing something unex$ecte#.
8o& &oul# &e !no& if this array containe# someone &hose last name &as .no&/ (e coul# use select" but it &oul# ha%e to iterate through e%ery item in the array" e%en if it ha# alrea#y foun# a .no&. (oul#n't it be better if &e ha# a metho# that returne# tr,e the moment it foun# one/
star"s&any3 . -na+e- na+e&ends0with3(4 now4# / # => tr,e
1he ;uestion mar! in the any3 metho# is similar to the exclamation mar! before < it's sim$ly a Ruby con%ention" this time to in#icate a metho# that returns a true 9or truthy: %alue.
star"s&all3 . -na+e- na+e&ends0with3(4 tar"4# / # => false star"s&delete(4Cohn now4# star"s&all3 . -na+e- na+e&ends0with3(4 tar"4# / # => tr,e
Range iteration
No& you'%e seen a fe& tric!s from the Array class" let's ta!e a loo! at another common class" Ran*e,
(D&&)#&each . -n- p,ts n /
Notice ho& &e're using each again. 8ere's a metho# that allo&s us to iterate o%er the range in slices,
((&&(DD#&each0slice()# . -set- p,ts set&inspect /
!ounting to "nfinity
n most languages" #i%i#ing by =ero raises an error,
>>> (&DJD ;racebac" (+ost recent call last#8 Kile "Bstrin*>", line (, in B+od,le>
>
Not so in Ruby,
?nfinity = (&DJD # => ?nfinity
*ou can e%en create a range bet&een negati%e an# $ositi%e nfinity,
all0n,+bers = %?nfinity&&?nfinity all0n,+bers&incl,de3(%')F2'# # => tr,e
*ou coul#" if you &ante# to" use each to iterate o%er e%ery $osti%e number.
all0positi6e0n,+bers&each . -n- p,ts n /
Or $erha$s in slices,
all0positi6e0n,+bers&each0slice()#&ta"e(2# # => [[D, (, 2, ', E], [), F, G, H, I]]
Notice ho& &e #i#n't $ass a bloc! to each0slice this time/ &on#er &hat ha$$ens if &e 3ust call each by itself/
en,+erator = all0positi6e0n,+bers&each # => #B9n,+erator8 D&&?nfinity8each> en,+erator&ne1t # => D en,+erator&ne1t # => ( en,+erator&ne1t # => 2
nteresting.
#ashes
8ashes allo& table-li!e ma$$ings bet&een names an# %alues,
si*ils = . 4 tar"4 => 4:irewolf4, 4<annister4 => 4<ion4, 4Nrey5oy4 => 4@ra"en4
8mm" these are the same metho#s that a$$ear on the Array an# Ran*e classes.
"ntroducing Enumerable
Array" Ran*e"
an# Oash share a number of i#entical 9an# useful: metho#s. 8o& is this
ha$$ening/
Oash&incl,ded0+od,les # => [9n,+erable, P] Array&incl,ded0+od,les # => [9n,+erable, P] Ran*e&incl,ded0+od,les # => [9n,+erable, P]
Each of these classes inclu#e the 9n,+erable mo#ule. (hat #oes that #o/
9n,+erable&instance0+ethods # => [8to0a, 8entries, 8sort, 8sort0by, 8*rep, 8co,nt, 8find, 8detect, 8find0inde1, 8find0all, 8select, 8re5ect, 8collect, 8+ap, 8flat0+ap, 8collect0concat, 8in5ect, 8red,ce, 8partition, 8*ro,p0by, 8first, 8all3, 8any3, 8one3, 8none3, 8+in, 8+a1, 8+in+a1, 8+in0by, 8+a10by, 8+in+a10by, 8+e+ber3, 8incl,de3, 8each0with0inde1, 8re6erse0each, 8each0entry, 8each0slice, 8each0cons, 8each0with0ob5ect, 8Mip, 8ta"e, 8ta"e0while, 8drop, 8drop0while, 8cycle, 8ch,n", 8slice0before, 8laMy]
0ha@ 1he metho#s &e'%e been using ha%e been #efine# insi#e the 9n,+erable mo#ule.
$hat%s a module&
0 mo#ule is a collection of metho#s. 7o#ules are sometimes calle# 5mixins6" because you can mix in the metho#s #efine# in a mo#ule into a class. Ao you ba!e/ 'm terrible at it. can ne%er remember to !ee$ an eye on the time. )et's &rite some Ruby that !no&s ho& to ba!e,
+od,le !a"in* def ba"eA "!a"in* for #.coo"in*0ti+e/ at #.te+perat,re/" end end
class Ca"e incl,de !a"in* def coo"in*0ti+e "E) +ins" end def te+perat,re "(HDQC" end end
'rowsing books
)et's say &e ha# a !oo" an# a !oo"shop class,
class !oo" B end tr,ct&new(8title, 8price#
class !oo"shop def initialiMe(boo"s# Rboo"s = boo"s end def n,+ber0of0boo"s Rboo"s&len*th end end
1he tr,ct is a little tric! &e're using to s!etch out the classes.
!oo"
boo"s = [ !oo"&new(4;he <ittle !oo" of Cal+4, 2&2D#, !oo"&new(4Co+plete Sor"s of :ic"ens4, 'DD#, !oo"&new(4;he 9lephant and the !alloon4, )# ] blac"0boo"s = !oo"shop&new(boo"s# blac"0boo"s&n,+ber0of0boo"s # => '
8o& coul# &e bro&se through each of the boo!s in the boo!store/
(efining each
)et's ma!e our o&n each" &hich &ill han# us each of the boo!s,
class !oo"shop # &&&snip&&&
8ere's ho& you coul# use the each metho# &e'%e im$lemente#,
boo"shop&each do -boo"p,ts boo"&title end
Our each 3ust calls each on the un#erlying Rboo"s array. *our each coul# be much more interesting < e%ery iteration of each coul# execute a #atabase ;uery" or ;uery a &eb ser%ice" or ta!e a rea#ing from a &eather station" for exam$le. t's entirely u$ to you.
No&" &e can use all the &on#erful metho#s &e coul# &ith Array" Ran*e" an# Oash,
boo"shop&filter . -boo"- boo"&price B= )&DD / boo"shop&any3 . -boo"- boo"&title&start0with3(4Co+plete4# / &&& 9n,+erable
gi%es you all these metho#s for free. *ou 3ust nee# to #efine &hat each means
to you.
Neat@
the
(ell" here's ho& +ap coul# ha%e been im$lemente# insi#e 9n,+erable,
+od,le 9n,+erable def +ap new0collection = [] self&each do -ite++odified0ite+ = yield ite+ new0collection BB +odified0ite+ end new0collection end end
n other &or#s" all the metho#s you'%e seen so far 9select" each0with0inde1" all3" +ap" etc.: ha%e been &ritten $urely in terms of each. 1his is a beautiful exam$le of $olymor$hism in the &il#. 4y abstracting a&ay the un#erlying class or #ata structure" 9n,+erable sol%es a number of common $roblems concerning collections in one fell s&oo$. t can han#le e%erything from hashes to boo!sho$s.
9n,+erable,
Further reading