# Define a \`mapat\` function to map a function over specific indices

**URL:** <https://discourse.julialang.org/t/define-a-mapat-function-to-map-a-function-over-specific-indices/90392>\
**Category:** General Usage\
**Tags:** performance, array, map\
**Created:** [November 17, 2022, 9:54am UTC](https://discourse.julialang.org/t/define-a-mapat-function-to-map-a-function-over-specific-indices/90392 "2022-11-17T09:54:26Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![singularitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/singularitti/32/17678_2.png) [@singularitti](https://discourse.julialang.org/u/singularitti)\
**Post date:** [November 17, 2022, 9:54am UTC](https://discourse.julialang.org/t/define-a-mapat-function-to-map-a-function-over-specific-indices/90392/1 "2022-11-17T09:54:26Z")

</div>

I have an `Array` and want to `map` a function over selected indices, I tried the following two implementations:

```julia
# Map function `f` at specific indices of an array and update in-place
function mapat!(f, array, indices...)
    area = view(array, indices...)
    map!(f, area, area)
    return array
end

# Map function `f` at specific indices of an array
function mapat(f, A, indices...)
    B = deepcopy(A)
    area = view(B, indices...)
    map!(f, area, area)
    return B
end

```

```julia
julia> a = rand(4, 4)
4×4 Matrix{Float64}:
 0.4023 0.315485 0.843238 0.190448
 0.73345 0.618197 0.296909 0.639623
 0.391128 0.953831 0.176423 0.569618
 0.139925 0.84709 0.346626 0.935079

julia> @btime mapat(-, $a, 1:2, 2:3)
  70.484 ns (3 allocations: 528 bytes)
4×4 Matrix{Float64}:
 0.4023 -0.315485 -0.843238 0.190448
 0.73345 -0.618197 -0.296909 0.639623
 0.391128 0.953831 0.176423 0.569618
 0.139925 0.84709 0.346626 0.935079

# The reason why this does not change `a` is probably because `a` is put into a function?
julia> @btime mapat!(-, $a, 1:2, 2:3)
  9.217 ns (0 allocations: 0 bytes)
4×4 Matrix{Float64}:
 0.4023 0.315485 0.843238 0.190448
 0.73345 0.618197 0.296909 0.639623
 0.391128 0.953831 0.176423 0.569618
 0.139925 0.84709 0.346626 0.935079

```

Are there more performant and type-stable implementations? And is there a general implementation for all iterables?

---

<div class="post-metadata">

**Author:** ![Vasily\_Pisarev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vasily_pisarev/32/7929_2.png) [@Vasily\_Pisarev](https://discourse.julialang.org/u/Vasily_Pisarev)\
**Post date:** [November 17, 2022, 10:32am UTC](https://discourse.julialang.org/t/define-a-mapat-function-to-map-a-function-over-specific-indices/90392/2 "2022-11-17T10:32:44Z")

</div>

I think that it works, just the number of calls inside `@btime` happened to be even, so that the result is identical to the original array. Try `mapat!` with a non-self-inverse function, like `x->x+1`.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [November 17, 2022, 2:12pm UTC](https://discourse.julialang.org/t/define-a-mapat-function-to-map-a-function-over-specific-indices/90392/3 "2022-11-17T14:12:34Z")

</div>

> [@singularitti](#):
>
> ```julia
> function mapat(f, A, indices...)
> B = deepcopy(A)
> area = view(B, indices...)
> map!(f, area, area)
> return B
> end
> 
> ```

You can simplify this as:

```julia
 mapat(f, A, indices...) = mapat!(f, copy(A), indices...) # or deepcopy

```

Not sure why you need `deepcopy`. Is it possible that `f` is a mutating function on the elements of the input array?

BTW, I tried broadcast instead of `map!`:

```julia
function mapat2!(f, A, inds...)
    area = view(A, inds...)
    area .= f.(area)
    return A
end

```

and that is \>30x slower. Anyone know why? I don’t think it’s a benchmarking issue, because it also happens when I wrap it in an outer function with the mapped function hard-coded.

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [November 17, 2022, 3:02pm UTC](https://discourse.julialang.org/t/define-a-mapat-function-to-map-a-function-over-specific-indices/90392/4 "2022-11-17T15:02:42Z")

</div>

> [@DNF](#):
>
> broadcast instead of `map!` […] is \>30x slower. Anyone know why?

Thought it might notice aliasing & making a preventative copy, but that seems not to be the case. Writing `mapat2!(f::F, A, inds...) where F` (etc.) does remove some small allocations, after which e.g.

```julia
julia> @btime mapat!(sqrt, $(rand(10^6)), 33:99); # map!
  90.394 ns (0 allocations: 0 bytes)

julia> @btime mapat2!(sqrt, $(rand(10^6)), 33:99); # broadcast
  93.487 ns (0 allocations: 0 bytes)

julia> @btime mapat3!(sqrt, $(rand(10^6)), 33:99); # just a loop
  68.476 ns (0 allocations: 0 bytes)

```

> [@singularitti](#):
>
> And is there a general implementation for all iterables?

Can you specify more closely what you want? Iterables don’t have to be indexable, but they do collect to arrays. It would surely be possible to collect while applying `f` only to some elements.
