# Robust method of modifying corners of an Array

I’m trying to write a simple function that replaces the corners of an array with a value. This is what I have so far:

``````function replaceCorners!(a::AbstractArray{String, 2}, v::String)
a[1, 1] = a[1, size(a)[2]] = a[size(a)[1], 1] = a[size(a)...] = v
end
``````

1. Is this the most “julian” way to replace the corners of an array?
2. This method works for an array that looks like `["a" "b" "c"]`, but not for an array such as `["a"; "b"; "c"]`. How can I make it work for arrays of any size?
3. In the function above, I only allowed for arrays that contain strings and values that are strings - how would I go about making this applicable to any type?

``````replaceCorners!(a::AbstractArray{T, 2}, v::T) where T
``````

also, reuse `size(a)`

2 Likes

For question 3, the corredt way is to have the function be `function replaceCorners!(a::AbstractMatrix{T}, v::T) where T`

1 Like

Do you mind clarifying on this? From what I can tell, I am reusing `size(a)` in the function.

my bad, I think in this inline case `size` only gets evaluated once.

Here’s a simple solution for doing this in general.

``````firstlast(x) = first(x):length(x)-1:last(x)
function replace_corners!(a::AbstractArray, v)
a[firstlast.(axes(a))...] .= (v,)
return a
end
``````
7 Likes

Are you aware that you can use `end`?

``````function repcorners!(a, v)
a[1, 1] = a[1, end] = a[end, 1] = a[end, end] = v
end
``````

This is less general than @mbauman’s solution, but it’s the obvious implementation of what you are doing.

Also, it is more idiomatic to write `size(a, 1)` than `size(a)[1]`.

3 Likes

Thanks for the alternative solution! I’m having some trouble figuring out what exactly is happening on the third line.

With an example array like this

``````a = [
12 11 10 9;
8 7 6 5;
4 3 2 1;
]
``````

Running `firstlast.(axes(a))` gives me `(1:2:3, 1:3:4)`. This makes sense, but splatting that then indexing a by the result is hard to visualize. Secondly, what is the significance of using `(v,)`?

I actually had the following at first (before a ninja edit):

``````firstlast(x) = [first(x), last(x)]
``````

That is, I just want an array with two elements. The above isn’t ideal because it’ll actually allocate an `Array` for every dimension. I swapped to a cleverly constructed StepRange that contains the same elements — but will probably avoid allocations entirely.

So I splat that into the array, which effectively does:

``````a[[1,end],[1,end],[1,end], #=etc=#]
``````

This selects the cartesian product of all the endpoints of each dimension in the array. For a two-dimensional array, there are 4 (`2*2`) corners. A cube has 8 (`2*2*2`). And so on.

Then I broadcast the value `v` into all those locations I’ve selected. If `v` itself is broadcastable container, well, then it’ll try to match its shape with the shape of the indices I used — potentially “spreading” its values out amongst the selected locations (but more likely just throwing a shape mismatch error). Using `(v,)` “protects” the value inside a container I know is only one element and ensures that `v` itself is assigned to each and every single chosen location.

Also note that I’m not restricting the type of `v` at all. Upon assignment, Julia will attempt to `convert` v to the element type of the array for you — and it’ll happily throw an error at that point if it’s not possible.

7 Likes

FWIW, ImageTransformations has CornerIterator

2 Likes