|
| 1 | +# Indexing Behavior |
| 2 | + |
| 3 | +## Views and slices |
| 4 | +`ComponentArray`s slice, rather than view, when indexing. This catches some people by surprise when they are trying to use indexing on `ComponentVector`s for dynamic field access. Let's look at an example. We'll make a `ComponentVector` with a nested structure. |
| 5 | +```julia |
| 6 | +julia> using ComponentArrays |
| 7 | + |
| 8 | +julia> ca = ComponentArray(a=5, b=[4, 1]) |
| 9 | +ComponentVector{Int64}(a = 5, b = [4, 1]) |
| 10 | +``` |
| 11 | +Using dot notation, we can access and change properties as if `ca` was a regular `struct` or `NamedTuple`. |
| 12 | +```julia |
| 13 | +julia> ca.b[1] = 99; |
| 14 | + |
| 15 | +julia> ca.a = 22; |
| 16 | + |
| 17 | +julia> ca |
| 18 | +ComponentVector{Int64}(a = 22, b = [99, 1]) |
| 19 | +``` |
| 20 | +Now let's try with indexing: |
| 21 | +```julia |
| 22 | +julia> ca[:b][1] = 0 |
| 23 | +0 |
| 24 | + |
| 25 | +julia> ca[:a] = 0 |
| 26 | + |
| 27 | +julia> ca |
| 28 | +ComponentVector{Int64}(a = 0, b = [99, 1]) |
| 29 | +``` |
| 30 | +We see that the `a` field changed but the `b` field didn't. When we did `ca[:b]`, it sliced into `ca`, thus creating a copy that would not update the original when we went to set the first element to `0`. On the other hand, since the update of the `a` field calls `setindex!` which updates in-place. |
| 31 | + |
| 32 | +If viewing, rather than slicing, is the desired behavior, use the `@view` macro or `view` function: |
| 33 | +```julia |
| 34 | +julia> @view(ca[:b])[1] = 0 |
| 35 | + |
| 36 | +julia> ca |
| 37 | +ComponentVector{Int64}(a = 0, b = [0, 1]) |
| 38 | +``` |
| 39 | + |
| 40 | +## Retaining component labels through index operations |
| 41 | +Sometimes you might want to index into a `ComponentArray` without dropping the component name. Let's look at a new example with a more deeply nested structure: |
| 42 | +```julia |
| 43 | +julia> ca = ComponentArray(a=5, b=[4, 1], c=(a=2, b=[6, 30])) |
| 44 | +ComponentVector{Int64}(a = 5, b = [4, 1], c = (a = 2, b = [6, 30])) |
| 45 | +``` |
| 46 | +If we wanted to get the `b` component while keeping the name, we can use the `KeepIndex` wrapper around our index: |
| 47 | +```julia |
| 48 | +julia> ca[KeepIndex(:b)] |
| 49 | +ComponentVector{Int64}(b = [4, 1]) |
| 50 | +``` |
| 51 | +Now instead of just returning a plain `Vector`, this returns a `ComponentVector` that keeps the `b` name. Of course, this is still compatible with `view`s, so we could have done `@view ca[KeepIndex(:b)]` if we wanted to retain the view into the origianl. |
| 52 | + |
| 53 | +Similarly, we can use plain indexes like ranges or integers and they will keep the names of any components they capture: |
| 54 | +```julia |
| 55 | +julia> ca[KeepIndex(1)] |
| 56 | +ComponentVector{Int64}(a = 5) |
| 57 | + |
| 58 | +julia> ca[KeepIndex(2:3)] |
| 59 | +ComponentVector{Int64}(b = [4, 1]) |
| 60 | + |
| 61 | +julia> ca[KeepIndex(1:3)] |
| 62 | +ComponentVector{Int64}(a = 5, b = [4, 1]) |
| 63 | + |
| 64 | +julia> ca[KeepIndex(2:end)] |
| 65 | +ComponentVector{Int64}(b = [4, 1], c = (a = 2, b = [6, 30])) |
| 66 | +``` |
| 67 | +But what if our range doesn't capture a full component? We can see below that using `KeepIndex` on the first five elements returns a `ComponentVector` with those elements but only the `a` and `b` names, since the `c` component wasn't fully captured. |
| 68 | +```julia |
| 69 | +julia> ca[KeepIndex(1:5)] |
| 70 | +5-element ComponentVector{Int64} with axis Axis(a = 1, b = 2:3): |
| 71 | + 5 |
| 72 | + 4 |
| 73 | + 1 |
| 74 | + 2 |
| 75 | + 6 |
| 76 | +``` |
0 commit comments