7
votes

I have two arrays that I want to iterate over at the same time.

I'm using this:

julia> xs = [1,2,3];

julia> ys = [4,5,6];

julia> for i in 1:length(xs)
           x = xs[i]
           y = ys[i]
           @show x, y
       end
(x, y) = (1, 4)
(x, y) = (2, 5)
(x, y) = (3, 6)

Is there a better way to iterate over multiple arrays in Julia?

3

3 Answers

11
votes

Use zip along with tuple destructuring:

julia> xs = [1,2,3];

julia> ys = [4,5,6];

julia> for (x, y) in zip(xs, ys)
           @show x, y
       end
(x, y) = (1, 4)
(x, y) = (2, 5)
(x, y) = (3, 6)

zip will stop iteration at the shortest array:

julia> for (x, y) in zip([1,2], [0,0,0])
           @show x, y
       end
(x, y) = (1, 0)
(x, y) = (2, 0)

This pattern can be generalized to an arbitrary number of lists:

julia> for (x, y, z) in zip([1,2], [3,4], [5,6])
           @show x, y, z
       end
(x, y, z) = (1, 3, 5)
(x, y, z) = (2, 4, 6)
4
votes

One possibility consists in using the eachindex function: if it is given multiple Array-like arguments, it will return a iterable set of indices suitable to iterate on all arguments at once.

This is useful in particular in the following situations:

  • when you need to use the index itself (for example because you don't only need to access the elements of the collections, but also set some of them), or
  • when you want to check that both arrays indeed have the same number of elements (this might or might not be a desired property depending on your use case).


Example 1: using the index itself to fill the first array with values coming from the second

julia> x = [1,2,3];

julia> y = [4,5,6];
julia> @inbounds for i in eachindex(x, y)
           x[i] = 2*y[i]
       end
julia> x
3-element Array{Int64,1}:
  8
 10
 12


Example 2: check that the arrays have the same range

julia> x = [1,2];
julia> y = [4,5,6];

julia> @inbounds for i in eachindex(x, y)
           x[i] = 2*y[i]
       end
ERROR: DimensionMismatch("all inputs to eachindex must have the same indices, got [1, 2] and [1, 2, 3]")


Example 3: note that eachindex generalizes well for multi-dimensional arrays too.

julia> x = zeros(2, 3);
julia> y = ones(2, 3);

julia> @inbounds for i in eachindex(x, y)
           x[i] = 2*y[i]
       end
julia> x
2×3 Array{Float64,2}:
 2.0  2.0  2.0
 2.0  2.0  2.0
2
votes

You can iterate over multiple collections using map and foreach. For example, with map:

julia> x, y = 1:3, 4:6;

julia> map(hypot, x, y)
3-element Array{Float64,1}:
 4.123105625617661
 5.385164807134504
 6.708203932499369

For more complicated multi-line anonymous functions, you can use do-block syntax:

julia> xs, ys = 1:4, 10:10:40;

julia> map(xs, ys) do x, y
           if isodd(x)
               x + y
           else
               x * y
           end
       end
4-element Array{Int64,1}:
  11
  40
  33
 160

foreach is very similar to map, but is intended for use when a function is applied for its side effect, like printing or plotting, rather than its return value. An example with foreach:

julia> x, y = ["a", "b", "c"], 1:3;

julia> foreach(println ∘ ^, x, y)
a
bb
ccc

Note the use of the function composition operator in the foreach call.