It

Iteration in Crystal

1 exercise

About Iteration

In programming, iteration refers to the process of repeating a block of code multiple times. This can be done by using concepts such as while loops and until loops. It can also be done by using recursion, which is a function that calls itself.

However, most often you want to iterate over a collection of items, such as an Array, Range or String. Both the Array and Range classes in Crystal include the Enumerable module, which provides a number of methods for iterating over the elements. Meanwhile, the String class has its own set of methods for iterating over the characters.

Crystal also doesn't have any for statement like other languages. Instead it has several methods that can be used to iterate.

Iterating

The most common way to iterate over a collection is to use the each method, which yields each element in the collection to a block. This can be done easily with a Range. Say you want to loop between 1 and 3, you can use the each method to iterate over the range.

(1..3).each do |n|
  puts n
end

# Output:
# 1
# 2
# 3

Even simpler, if you just want to iterate a number of times you can use the times method, which exists on the Int class.

3.times do |n|
  puts n
end

# Output:
# 0
# 1
# 2

Iterating over a String

A String is a sequence of characters and doesn't belong to the Enumerable module, which means it has its own set of methods for iterating over the characters. The most common way to iterate over a String is to use the each_char method, which yields each character in the String to a block.

Note

The each_char method feeds a Char object and not a String object to the block.

str = "hello"
str.each_char { |char| puts char }

# Output:
# h
# e
# l
# l
# o

Another way of iterating over a String is to use the each_line method. This method is mostly used when reading a file line by line.

str = "hello\nworld"
str.each_line do |line|
  puts line
end

# Output:
# hello
# world

Iterating over an object that includes the Enumerable module

The Enumerable module provides a number of methods for iterating over the elements of a collection. Collections that include the Enumerable module are Array, Range, Hash, Set, and others, the later ones will be covered in later concepts.

The most common way to iterate over an Array is to use the each method, which yields each element in the Array to a block.

arr = [5, 2, 3]
arr.each do |element|
  puts element
end

# Output:
# 5
# 2
# 3

Map

The map method is another way to iterate over an Array, it returns a new Array containing the results of applying the block to each element. It is similar to the each method, but it returns a new Array with the transformed elements. Transformation is very useful when you want to apply a function to each element of the collection, which is a common operation.

arr = [1, 2, 3]
new_arr = arr.map do |element|
  element * 2
end

new_arr
# => [2, 4, 6]

With a control flow such as if so can you filter which elements to transform.

arr = [1, 2, 3]
new_arr = arr.map do |element|
  if element.odd?
    element * 2
  else
    element
  end
end

new_arr
# => [2, 2, 6]

Shorthand

In Crystal, there is a shorthand syntax for iterating and applying a method to each element of a collection. This is usefull when working with simple logic which only requires to be transformed with one method. This is done by using the &. syntax followed by the method name.

arr = [1, 2, 3]
arr.map(&.to_s)
# => ["1", "2", "3"]

With Index

Sometimes you need to know the index of the element you are iterating over, you can use the each_with_index or map_with_index method for that. It yields each element and its index to a block, the method also accepts an optional argument to specify the starting index.

arr = [1, 2, 3]
arr.each_with_index(4) do |element, index|
  puts "Element: #{element}, Index: #{index}"
end

# Output:
# Element: 1, Index: 4
# Element: 2, Index: 5
# Element: 3, Index: 6

Note that the each_char_with_index method is also available for String objects.

sum

The sum method returns the sum of all elements in the collection, it also accepts an optional block to transform the elements before summing them. It also accepts an optional argument to specify the initial value of the sum.

arr = [1, 2, 3]
arr.sum
# => 6

arr.sum(2) { |n| n * 2 }
# => 14

reduce

reduce or fold as it is known in other languages, is a method that with a combine method and an initial value, it will combine all the elements in the collection. The reduce method has an accumulator that is passed to the block, the accumulator is the result of the previous iteration. This becomes a recursive process that will combine all the elements in the collection.

arr = [1, 2, 3]
arr.reduce(0) do |acc, n|
  acc + n
end
# => 6

reduce might seem similar to sum, but reduce is more flexible because it allows you to specify the initial value of the accumulator and the combine method. reduce can be used to implement sum, count, and other methods.

Edit via GitHub The link opens in a new window or tab

Learn Iteration