### 8.1 Index Expressions ¶

An index expression allows you to reference or extract selected elements of a vector, a matrix (2-D), or a higher-dimensional array. Arrays may be indexed in one of three ways: Component Indexing, Linear Indexing, and Logical Indexing.

#### Component Indexing ¶

Component indices may be scalars, vectors, ranges, or the special operator ‘`:`’, which selects entire rows, columns, or higher-dimensional slices.

Component index expression consists of a set of parentheses enclosing M expressions separated by commas. Each individual index value, or component, is used for the respective dimension of the object that it is applied to. In other words, the first index component is used for the first dimension (rows) of the object, the second index component is used for the second dimension (columns) of the object, and so on. The number of index components M defines the dimensionality of the index expression. An index with two components would be referred to as a 2-D index because it has two dimensions.

In the simplest case, 1) all components are scalars, and 2) the dimensionality of the index expression M is equal to the dimensionality of the object it is applied to. For example:

    A = reshape (1:8, 2, 2, 2)  # Create 3-D array
    A =

    ans(:,:,1) =

       1   3
       2   4

    ans(:,:,2) =

       5   7
       6   8

    A(2, 1, 2)   # second row, first column of second slice
                 # in third dimension: ans = 6

The size of the returned object in a specific dimension is equal to the number of elements in the corresponding component of the index expression. When all components are scalars, the result is a single output value. However, if any component is a vector or range then the returned values are the Cartesian product of the indices in the respective dimensions. For example:

    A([1, 2], 1, 2) ≡ [A(1,1,2); A(2,1,2)]
    ⇒
    ans =
       5
       6

The total number of returned values is the product of the number of elements returned for each index component. In the example above, the total is 2*1*1 = 2 elements.

Notice that the size of the returned object in a given dimension is equal to the number of elements in the index expression for that dimension. In the code above, the first index component (`[1, 2]`) was specified as a row vector, but its shape is unimportant. The important fact is that the component specified two values, and hence the result must have a size of two in the first dimension; and because the first dimension corresponds to rows, the overall result is a column vector.

    A(1, [2, 1, 1], 1)    # result is a row vector: ans = [3, 1, 1]
    A(ones (2, 2), 1, 1)  # result is a column vector: ans = [1; 1; 1; 1]

The first line demonstrates again that the size of the output in a given dimension is equal to the number of elements in the respective indexing component. In this case, the output has three elements in the second dimension (which corresponds to columns), so the result is a row vector. The example also shows how repeating entries in the index expression can be used to replicate elements in the output. The last example further proves that the shape of the indexing component is irrelevant, it is only the number of elements (2x2 = 4) which is important.

The above rules apply whenever the dimensionality of the index expression is greater than one (M > 1). However, for one-dimensional index expressions special rules apply and the shape of the output is determined by the shape of the indexing component. For example:

    A([1, 2])  # result is a row vector: ans = [1, 2]
    A([1; 2])  # result is a column vector: ans = [1; 2]

The shape rules for A(P) are:

- When at least one of A or P has two or more dimensions, then A(P) takes the shape of P. This happens when at least one of the variables is a 2-D matrix or an N-D array.
- When both A and P are 1-D vectors, then A(P) takes the shape of A itself. In particular, when A is a row vector, then A(P) is also a row vector irrespective of P’s shape. The case when A is a column vector is analogous.

A colon (‘`:`’) may be used as an index component to select all of the elements in a specified dimension. Given the matrix,

    A = [1, 2; 3, 4]

all of the following expressions are equivalent and select the first row of the matrix.

    A(1, [1, 2])  # row 1, columns 1 and 2
    A(1, 1:2)     # row 1, columns in range 1-2
    A(1, :)       # row 1, all columns

When a colon is used in the special case of 1-D indexing the result is always a column vector. Creating column vectors with a colon index is a very frequently encountered code idiom and is faster and generally clearer than calling `reshape` for this case.

    A(:)    # result is column vector: ans = [1; 2; 3; 4]
    A(:)'   # result is row vector: ans = [1, 2, 3, 4]

In index expressions the keyword `end` automatically refers to the last entry for a particular dimension. This magic index can also be used in ranges and typically eliminates the needs to call `size` or `length` to gather array bounds before indexing. For example:

    A(1:end/2)        # first half of A => [1, 2]
    A(end + 1) = 5;   # append element
    A(end) = [];      # delete element
    A(1:2:end)        # odd elements of A => [1, 3]
    A(2:2:end)        # even elements of A => [2, 4]
    A(end:-1:1)       # reversal of A => [4, 3, 2, 1]

For more information see the `"end" keyword`.

#### Linear Indexing ¶

It is permissible to use a 1-D index with a multi-dimensional object. This is also called linear indexing. In this case, the elements of the multi-dimensional array are taken in column-first order like in Fortran. That is, the columns of the array are imagined to be stacked on top of each other to form a column vector and then the single linear index is applied to this vector.

    A = [1, 2, 3; 4, 5, 6; 7, 8, 9];

    A(4)    # linear index of 4th element in 2-D array: ans = 2
    A(3:5)  # result has shape of index component: ans = [7, 2, 5]
    A([1, 2, 2, 1]) # result includes repeated elements: ans = [1, 4, 4, 1]

#### Logical Indexing ¶

Logical values can also be used to index matrices and cell arrays. When indexing with a logical array the result will be a vector containing the values corresponding to `true` parts of the logical array. The following examples illustrates this.

    data = [ 1, 2; 3, 4 ];
    idx = [true, false; false true];
    data(idx)
         ⇒ ans = [ 1; 4 ]

    idx = (data <= 2);
    data(idx)
         ⇒ ans = [ 1; 2 ]

Instead of creating the `idx` array it is possible to replace `data(idx)` with `data( data <= 2 )` in the above code.

While the size of the logical index expressions is usually the same as that of the array being indexed, this is not a necessary condition. If the logical index is a different size than the array, then elements from the array are matched against elements from the logical index based on their linear order, just as with linear indexing.

    data = [ 1, 2, 3; 4, 5, 6 ];
    idx = [ true, false, false, true ];
    data(idx)
         ⇒ ans = [ 1 5 ]  # idx selected the 1st and 4th position elements

If the logical index is larger than then array an out of bounds error will occur if any true values attempt to select a linear position larger than the number of elements in the array.

    idx = [ true, true, false; false, true, false; true; false; false ];
    data(idx)
         ⇒ ans = [ 1; 2; 5; 3 ] # returns positions 1, 3, 4, 5 in a column

    idx = [ true, true, false; false, true, false; true; false; true ];
    data(idx)
         ⇒ error: a(9): out of bound 6 (dimensions are 2x3)

False elements of a logical index outside the array size are ignored, but when the ninth element of the logical index is true it triggers an error as it tries to select a nonexistent 9th element of the array.