2.2. Multi-dimensional Arrays#

So far, we’ve only seen one-dimensional arrays, or vectors. But arrays can have multiple dimensions. A 2D array is a natural way to represent a matrix.

To create a matrix manually, you can use a syntax similar to vectors. Use spaces to separate elements within a row (columns) and semicolons to separate the rows themselves.

# Create a 2x3 matrix. Spaces separate columns, and a semicolon starts a new row.
A = [1 2 3; -4 -5 -6]
2×3 Matrix{Int64}:
  1   2   3
 -4  -5  -6
# Let's check the type of our new variable A.
typeof(A)
Matrix{Int64} (alias for Array{Int64, 2})

Notice the type: Matrix{Int64}. This is just an alias for Array{Int64, 2}, which tells us it’s a 2-dimensional array of 64-bit integers.

To access elements in a multi-dimensional array, you provide each index separated by a comma, typically as [row, column].

# Access the element in the 2nd row, 3rd column.
A[2, 3]
-6

2.2.1. Querying Array Dimensions#

You can inspect the dimensions of multi-dimensional arrays using familiar functions. For instance, length returns the total number of elements in the array (i.e., rows × columns for a matrix).

# Get the total number of elements in the matrix (2 * 3 = 6).
length(A)
6

The size function is more specific. You can use it to get the size of the array along a particular dimension.

# Get the size of the first dimension (number of rows).
size(A, 1)
2
# Get the size of the second dimension (number of columns).
size(A, 2)
3

2.2.2. Iterating Over Matrices#

We can use the size function to construct loops that traverse every element in a matrix. A common pattern for this is a nested for-loop.

sumA = 0

for i = 1:size(A, 1)
    for j = 1:size(A, 2)
        sumA += A[i, j]
    end
end

println("Sum of the elements in A = ", sumA)
Sum of the elements in A = -9

For each iteration of the outer loop (controlled by i), the inner loop (controlled by j) completes its entire cycle. For our 2x3 matrix A, the loop produces index pairs (i, j) in the following order:

(1,1), (1,2), (1,3), then (2,1), (2,2), (2,3).

2.2.2.1. Compact Nested Loops#

Julia provides a more compact syntax for nested for-loops by placing the loop variables on the same line, separated by a comma. This is functionally identical to the code above.

sumA = 0

# This compact syntax is equivalent to the nested loop above.
for i = 1:size(A, 1), j = 1:size(A, 2)
    sumA += A[i, j]
end

println("Sum of the elements in A = ", sumA)
Sum of the elements in A = -9

2.2.2.2. Iterating Over Elements Directly#

If you don’t need the indices i and j and just want to access each element, you can use the simpler in syntax, just like with 1D arrays.

sumAsquared = 0

# Here, 'a' will take on the value of each element in A, one by one.
for a in A
    sumAsquared += a^2
end

print("Sum of the squares of the elements in A = ", sumAsquared)
Sum of the squares of the elements in A = 91

2.2.3. Creating Multi-dimensional Arrays#

The built-in functions for creating arrays can be extended to any number of dimensions by providing a tuple of the desired dimensions instead of a single length. 📏

Function

Description

zeros(T, dims)

An array of size dims filled with zeros of type T.

ones(T, dims)

An array of size dims filled with ones of type T.

trues(dims)

A boolean array of size dims with all values true.

falses(dims)

A boolean array of size dims with all values false.

fill(value, dims)

An array of size dims filled with the specified value.

Here, dims is a tuple of integers specifying the size of each dimension, like (rows, columns). For zeros and ones, the type T is optional and defaults to Float64.

# Create a 5x3 matrix of Float64s, initialized to 1.0. 
# Note: If the type is omitted, it defaults to Float64.
C = ones(5, 3)
5×3 Matrix{Float64}:
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0
 1.0  1.0  1.0

2.2.3.1. Vectors vs. Matrices: An Important Distinction#

It’s crucial to understand that Julia distinguishes between a 1D array (Vector) and a 2D array (Matrix) that happens to have only one column or one row. This is different from other languages like MATLAB, which treats all arrays as 2D.

# A 5-element, 1-dimensional array (a true Vector).
ones(5)
5-element Vector{Float64}:
 1.0
 1.0
 1.0
 1.0
 1.0
# A 5x1, 2-dimensional array (a Matrix), also known as a column vector.
ones(5, 1)
5×1 Matrix{Float64}:
 1.0
 1.0
 1.0
 1.0
 1.0
# A 1x5, 2-dimensional array (a Matrix), also known as a row vector.
ones(1, 5)
1×5 Matrix{Float64}:
 1.0  1.0  1.0  1.0  1.0

2.2.4. Concatenating Arrays#

You can build new matrices by concatenating or “stacking” existing arrays. The square bracket syntax [] is used for this, just as with creation.

  • Semicolons (;) stack arrays vertically.

  • Spaces concatenate arrays horizontally.

# Vertical concatenation: Arrays must have the same number of columns.
D = [A; ones(1, 3)]

# Horizontal concatenation: Arrays must have the same number of rows.
E = [zeros(2) A]

# General concatenation: Dimensions of blocks must be consistent.
F = [A ones(2, 2); zeros(1, 5)]
3×5 Matrix{Float64}:
  1.0   2.0   3.0  1.0  1.0
 -4.0  -5.0  -6.0  1.0  1.0
  0.0   0.0   0.0  0.0  0.0

When you include ranges in horizontal concatenation, Julia automatically expands them into column vectors.

# The ranges 1:5 and 101:105 are each treated as a 5x1 column vector here.
G = [1:5 ones(Int64, 5) 101:105]
5×3 Matrix{Int64}:
 1  1  101
 2  1  102
 3  1  103
 4  1  104
 5  1  105

2.2.5. Element-wise Operations and Slicing#

The dot-syntax for element-wise operations and the colon (:) for slicing work just as powerfully on multi-dimensional arrays.

# The dot-syntax applies operations element-by-element.
B = A.^2 .- 3A
2×3 Matrix{Int64}:
 -2  -2   0
 28  40  54
# The @. macro is a convenient shortcut to apply the dot to every operation in an expression.
B = @. A^2 - 3A
2×3 Matrix{Int64}:
 -2  -2   0
 28  40  54

2.2.5.1. Slicing Matrices#

Slicing works as before, but now you can specify ranges for each dimension to extract sub-matrices, rows, or columns.

# Select all columns (:) from the first row. The result is a 1D Vector.
A[1, :]
3-element Vector{Int64}:
 1
 2
 3
# Select all rows (:) from the first column.
A[:, 1]
2-element Vector{Int64}:
  1
 -4
# Select all rows from columns 2 through 3. The result is a new 2x2 Matrix.
A[:, 2:3]
2×2 Matrix{Int64}:
  2   3
 -5  -6

2.2.5.2. Modifying with Slices#

You can also use slicing on the left side of an assignment to modify parts of an array in-place.

# The . in `.=` is important! It broadcasts the assignment, setting every element 
# in the selected columns (1 and 3) to 0.
A[:, [1, 3]] .= 0
A
2×3 Matrix{Int64}:
 0   2  0
 0  -5  0
# Assign a new matrix slice to another. The dimensions on both sides must match.
# Here, we set columns 2 & 3 to be twice the values of columns 1 & 2.
A[:, 2:3] = 2 * A[:, 1:2]
A
2×3 Matrix{Int64}:
 0  0    4
 0  0  -10