# Can I use eachrow, eachcol, etc. to change array's values?

Can I use eachrow, eachcol, etc. to change array’s values?

For example,

``````x = rand(5, 5)
for c in eachcol(x)
for el in c
if el < maximum(x)/2
el = 0.0
end
end
end
display(x)
``````

does nothing, and

``````x = rand(5, 5)
for c in eachcol(x)
for el in c
if el < maximum(x)
el .= 0.0
end
end
end
display(x)
``````

throws

``````ERROR: MethodError: no method matching copyto!(::Float64, ::Base.Broadcast.Broadcasted{…})

Closest candidates are:
copyto!(::AbstractArray, ::Any)
@ Base abstractarray.jl:940

Stacktrace:
[1] materialize!
[3] top-level scope
``````

Or what would be the idiomatic way to do this?

This wouldn’t work even if `c` were an ordinary array. If you do

``````c = [1,2,3,4]
el = c[2]
``````

then `el = 17` doesn’t change the original array, and `el .= 17` is an error because `el` is not mutable. Please see the difference between assignment and mutation.

Whereas if you do:

``````for i in eachindex(c)
if c[i] < maximum(x)
c[i] = 0
end
end
``````

that will work, because an assignment `c[i] = 0` (equivalent to `setindex!(c, 0, i)`) mutates the contents of the array `c`.

And this will work with `eachcol` too, because `eachcol(x)` returns a sequence of views of `x`.

7 Likes

This could work:

``````x = rand(5, 5)
for c in eachcol(x)
c[c .< maximum(x) / 2] .= 0
end
display(x)
``````

This uses a `Bool` array to index the column, which is very typical in python, less so in Julia but it works. Once you iterate through the individual values in the column, though, you’re down to plain bits types (numbers), which are passed by value, not by reference, hence the `e` variable doesn’t refer to the memory location in your original `x` matrix, overwriting its values doesn’t change `x` itself.

That being said, you don’t even need the `eachcol` here, as Julia is ace when it comes to matrices:

``````x = rand(5, 5)
x[x .< maximum(x) / 2] .= 0
display(x)
``````
3 Likes

Note, by the way, that calling `maximum(x)` inside your innermost loop is horribly inefficient because computing the maximum involves traversing the whole array `x`, and you are repeating that for every loop iteration — you really want to compute `maximum(x)` once outside the loops.

Thanks for your replies. I thought I would be able to figure it out on a simple example, but I still cannot make it work, although

``````c = rand(4)
for i in eachindex(c)
if c[i] < maximum(c) / 2
c[i] = 0
end
end
``````

works fine.

Here is what I want to do eventually:
I have a matrix of type `Any` with the leftmost columns and topmost rows serving as headers. For each row, I want to nullify elements based on some simple conditions: for example, nullify every subsequent row element that is less that the current one multiplied by 2. Of course, I have to ignore the heading columns.

This is the simplest example, and I’m not sure how I can use boolean index masks here if I want to filter, say, by the name in the top column and values and something else.

Here is my attempt at filtering:

``````function filter!(table)
for r in eachrow(table[2:end, :])
for i in eachindex(r)
subr = r[i+1:end]
for j in eachindex(subr)
if r[i] > 0 && subr[j] < r[i] * 2
println("It is less")
setindex!(subr, 0, j)
else
println("Not less")
end
end
end
end
return table
end

A = [[Inf "b" "c" "d"]; [-1 2 3 10]; [-2 3 5 10]]
TARGET = [[Inf "b" "c" "d"]; [-1 2 0 10]; [-2 3 0 10]]

# nothing changed
display(filter!(A))
display(A)
``````

Is there limit in nesting of iterators? Or is slicing here should be done via some non-oblious macros to allow mutation?

I’d say that’s your problem right there, are you maybe looking for

?

2 Likes

There the problem is that slices in Julia create new arrays. Thus, here:

``````    for r in eachrow(table[2:end, :])
``````

you are creating a new array when doing `table[2:end, :]`, and thus then forward you won´t be modifying the original table.

Then, here,

``````            subr = r[i+1:end]
``````

the same happens.

Use `@view(table[2:end, :])` and `@view(r[i+1:end])` and your function will mutate the original arrays. A shortcut to that is adding `@views function...` for the whole function.

1 Like

It seems to be a nice wrapper, thanks for the recommendation.

In my case, the solution is simply to ditch this machinery and do it the old-school way, ignoring the linter warnings

``````function filter!(table)
for i in 2:size(table, 1)
for j in 1:size(table, 2)
current = table[i, j]
for k in (j+1):size(table, 2)
if current > 0 && table[i, k] < current * 2
table[i, k] = 0
end
end
end
end
return table
end
``````

Indeed. Those warnings are there so that people try to write code that works for arrays with indices that do not go from `1` to the length of the array, or the size in each dimension (OffsetArrays.jl in particular).

But if you know your arrays do not have such complications, you don’t need to worry.