12/08/2018, 13:09

Ruby Iterator

Todays, I am going to write an article about iterator which is the most useful methods in ruby language. In this article I'll so you the powerful and awesomess of iterator. But first we need to know the meaning iterator first, so what is iterator? What is iterator? According to Cooper Peter. ...

Todays, I am going to write an article about iterator which is the most useful methods in ruby language. In this article I'll so you the powerful and awesomess of iterator. But first we need to know the meaning iterator first, so what is iterator?

What is iterator?

According to Cooper Peter. Beginning Ruby: From Novice to Professional (2007): "Iterator is A special method such as each , upto , or times that steps through a list element by element. This process is called iteration, and each , upto , and times are iterator methods.". In fact, iterator work a lot like ruby loop. Now let take a look on iterator methods in ruby which we group them by data type:

1. Integer
  • times: int times, passing in values from zero to int - 1. If no block is given, an Enumerator is returned instead. Sample code:
      # with given block
      => 5.times {|i| print "#{i} "}
      # output should be
      #=> 0 1 2 3

      #with no block is given
      => 5.times
      #output should be
      #=> <Enumerator: 5:times>
  • upto(limit)/downto(limit):step increasing/decreasing values from int up/down to and including limit. If no block is given, an Enumerator is returned instead. Sample code:
      #upto method
      => 1.upto(5) { |i| print "#{i} " }
      #output should be
      #=> 1 2 3 4 5

      #downto method
      => 5.downto(1) { |i| print "#{i} " }
      #output should be
      #=> 5 4 3 2 1

      #with no block is given
      => 1.upto(5)
      #output should be
      #<Enumerator: 1:upto(5)>
      => 5.downto(1)
      #output should be
      #<Enumerator: 5:downto(1)>
  • step(limit[, step]): step incremented value by step (default start is 1) and finish when increasing value is greater than limit (if step is positive) or less than limit (if step is negative). If no block is given, an enumerator is returned instead. Sample code:
        => 1.step(10, 2) { |i| print "#{i} " }
        #output should be
        #=> 1 3 5 7 9

        => 1.step(10, 2)
        #output should be
        #=> #<Enumerator: 1:step(10, 2)>
2. Range
  • each: passing each element of range into block. If no block is given, an enumerator is returned instead. Sample code:
      => (1..10).each{|i| print "#{i} "}
      #output should be
      #=> 1 2 3 4 5 6 7 8 9 10

      => (1..10).each
      #output should be
      #=> #<Enumerator: 1..10:each>
  • step(n=1): passing each nth element of range into block. If no block is given, an enumerator is returned instead. Sample code:
       => (1..10).step(2){|i| print "#{i} "}
       #output should be
       #=> 1 3 5 7 9

       => (1..10).step(2)
       #output should be
       #=> #<Enumerator: 1..10:step(2)>
3. String
  • each_byte/ each_char/ each_line: Passes each byte/char/line in str to the given block, or returns an enumerator if no block is given. Sample code:
       => %{Rathanak
           and
           Framgia}.each_byte{|ch| print "#{ch} "}
       #output should be
       #=> 82 97 116 104 97 110 97 107 10 97 110 100 10 70 114 97 109 103 105 97

       => %{Rathanak
           and
           Framgia}.each_char{|ch| print "#{ch} "}
       #output should be
       #=> R a t h a n a k
       # a n d
       # F r a m g i a

       => %{Rathanak
           and
           Framgia}.each_line{|ch| print "#{ch} "}
       #output should be
       #=> Rathanak
       # and
       # Framgia

       => "Rathanak".each_byte"
       #output should be
       #=> #<Enumerator: "Rathanak":each_byte>
4. Array
  • each: Calls the given block once for each element in self, passing that element as a parameter. An Enumerator is returned if no block is given. Sample code:
       => [1,2,3,4,5,6,7,8].each {|v| print "#{v} "}
       #output should be
       #=> 1 2 3 4 5 6 7 8

       => [1,2,3,4,5,6,7,8].each
       #output should be
       #=> #<Enumerator: [1, 2, 3, 4, 5, 6, 7, 8]:each>
  • each_index: Same as #each, but passes the index of the element instead of the element itself. Sample code:
       => [1,2,3,4,5,6,7].each_index{|n| print "#{n} "}
       #output should be
       #=> 0 1 2 3 4 5 6

       => [1,2,3,4,5,6,7].each_index
       #output should be
       #=> #<Enumerator: [1, 2, 3, 4, 5, 6, 7]:each_index>
  • each_with_index: Same as #each_index, but passes both value and index of the element instead of the element itself. Sample code:
      => [1,2,3,4,5,6].each_with_index{|v, n| print "v=#{v} n=#{n}, "}
      #output should be
      #=> v=1 n=0, v=2 n=1, v=3 n=2, v=4 n=3, v=5 n=4, v=6 n=5,

      => [1,2,3,4,5,6].each_with_index
      #output should be
      #=> #<Enumerator: [1, 2, 3, 4, 5, 6]:each_with_index>
5. Hash
  • each or each_pair: passing the key-value pair as parameters into block. If no block is given, an enumerator is returned instead. Sample code:
       => hash = { "a" => 100, "b" => 200 }
       => hash.each {|key, value| puts "#{key} is #{value}" }
       #output should be
       #=> a is 100
       #=> b is 200

       => hash.each_pair {|key, value| puts "#{key} is #{value}" }
       #output should be
       #=> a is 100
       #=> b is 200
  • each_key: passing the key as parameters into block. If no block is given, an enumerator is returned instead. Sample code:
       => hash = { "a" => 100, "b" => 200 }
       => hash.each_key {|key| puts "key is #{key}" }
       #output should be
       #=> key is a
       #=> key is b
  • each_value: passing the value as parameters into block. If no block is given, an enumerator is returned instead. Sample code:
       => hash = { "a" => 100, "b" => 200 }
       => hash.each_key {|value| puts "value is #{value}" }
       #output should be
       #=> value is 100
       #=> value is 100

Other methods

For some ruby methods have proccess similar to iterator methods. Now let take a look on those methods:

1. Find

For find methods commonly work on array, range, hash.

  • find/detect: Returns the first for which block is true. If no object matches returns nil otherwise. Sample code:
       => (1..10).find{|i| i%3 == 0}
       #output should be
       #=> 3

       => (1..10).detect{|i| i > 20}
       #output should be
       #=> nil
  • find_all/select: returns an array containing all elements in given data for which the given block returns a true value. If no object matches return empty array. Sample code:
       => (1..10).select{|i| i%2 == 0}
       #output should be
       #=> [2, 4, 6, 8, 10]

       => (1..10).find_all{|i| i > 20}
       #output should be
       #=> []
  • any?: return true if at least one of the collection members is not false or nil. Sample code:
       => ["Rathanak", "Framgia", "Dev"].any? {|word| word.length <= 3}
       #output should be
       #=> true

       => ["Rathanak", "Framgia", "Dev"].any? {|word| word.length < 2}
       #output should be
       #=> false

       => [nil, true, 99].any?
       #output should be
       #=> true
  • all?: returns true if the block never returns false or nil. Sample code:
       => ["Rathanak", "Framgia", "Dev"].all? {|word| word.length >= 3}
       #output should be
       #=> true

       => ["Rathanak", "Framgia", "Dev"].all? {|word| word.length > 3}
       #output should be
       #=> false

       => [nil, true, 99].all?
       #output should be
       #=> false
  • delete_if: deletes every element of self for which block evaluates to true. Sample code:
       => ["Rathanak", "Framgia", "Dev"].delete_if {|word| word.length > 3}
       #output should be
       #=> ["Dev"]

       => ["Rathanak", "Framgia", "Dev"].delete_if {|word| word.length <= 3}
       #output should be
       #=> ["Rathanak", "Framgia"]

       => ["Rathanak", "Framgia", "Dev"].delete_if {|word| word.length < 3}
       #output should be
       #=> ["Rathanak", "Framgia", "Dev"]
2. Merge
  • merge/merge!: merge is working on hash only. it use to merge two hash together. Sample code:
    => hash1 = {name: "Rathanak", age: 21, com: "Framgia"}
    => hash2 = {location: "VN", age: 23, sex: "M"}

    => hash1.merge(hash2)
    #output should be
    #=> {:name=>"Rathanak", :age=>23, :com=>"Framgia", :location=>"VN", :sex=>"M"}

    => hash1.merge(hash2) do |key, old, new|
       if old < new
         old
       else
         new
       end
    end
    #output should be
    #=> {:name=>"Rathanak", :age=>21, :com=>"Framgia", :location=>"VN", :sex=>"M"}

3. Collect
  • collect/map: work best with array, hash, range. it word similar to each method but it creates a new array containing the values returned by the block. Sample code:
       => [12,34,54,67,56].map{|value| value * 10}
       #output should be
       #=> [120, 340, 540, 670, 560]

       => ["rathanak", "dev", "framgia"].collect{|word| word.capitalize}
       #output should be
       #=> ["Rathanak", "Dev", "Framgia"]
4. Sort
  • sort: returns a new array created by sorting self. Comparisons for the sort will be done using the <=> operator or using an optional code block.let take a look on how <=> operator work:

    Value Meaning Action
    -1 Less than Move to "left"
    0 Equal Stay
    1 More than Move to "right"

    Sample code:

       =>[2,5,3,7,5,9,6,2,7,9,1].sort
       #output should be
       #=> [1, 2, 2, 3, 5, 5, 6, 7, 7, 9, 9]

       =>[2,5,3,7,5,9,6,2,7,9,1].sort {|v1, v2| v1 <=> v2}
       #output should be
       #=> [1, 2, 2, 3, 5, 5, 6, 7, 7, 9, 9]

       =>[2,5,3,7,5,9,6,2,7,9,1].sort.reverse
       #output should be
       #=> [9, 9, 7, 7, 6, 5, 5, 3, 2, 2, 1]

       =>[2,5,3,7,5,9,6,2,7,9,1].sort {|v1, v2| v2 <=> v1}
       #output should be
       #=> [9, 9, 7, 7, 6, 5, 5, 3, 2, 2, 1]
5. Inject
  • inject: each element in self is passed an accumulator value (memo) and the element. If you specify a symbol instead, then each element in the collection will be passed to the named method of memo. In either case, the result becomes the new value for memo. At the end of the iteration, the final value of memo is the return value for the method. If you do not explicitly specify an initial value for memo, then the first element of collection is used as the initial value of memo.

    Sample code:

    => (5..10).inject { |sum, n| sum + n }
    #output should be
    #=> 45

    => (5..10).inject(2) { |memo, n| memo * n }
    #output should be
    #=> 302400

    %w{ Rathanak Framgia Dev }.inject do |memo, word|
      memo.length > word.length ? memo : word
    end
    #output should be
    #=> Rathanak

Control Structures

We can also use loop control structures in iterator as well, and here they are:

1. break: Terminate the whole loop 2. next: jump to next loop 3. redo: redo this loop 4. retry: start the whole loop over again

Resources

  • Book: Beginning Ruby: From Novice to Professional (2007)
  • Ruby Doc: http://ruby-doc.org/
    • Video: http://www.lynda.com/Ruby-tutorials/Iterators/47905/57960-4.html

Conclusion

No matter how small your application are, when you want to do some looping. You should try to use iterator methods instead of loop. Because it will make your code clean, easy to read and understand.

In this world, there are no free food. You learn it you know it.

Learn smart not learn hard.

0