# Map a function at a given level of a tree

**URL:** <https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910>\
**Category:** General Usage\
**Created:** [August 23, 2019, 9:42pm UTC](https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910 "2019-08-23T21:42:02Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [August 23, 2019, 9:42pm UTC](https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910/1 "2019-08-23T21:42:03Z")

</div>

I feel a bit dumb asking this very basic question, but I have looked and looked but cannot figure it out.

Imagine I have a “tree”, in the sense of a container of containers of containers… Such as

```julia
julia> tree = [[[1,2],[3,4]],[[5,6,7],[8]],[[9,10]]]
3-element Array{Array{Array{Int64,1},1},1}:
 [[1, 2], [3, 4]]
 [[5, 6, 7], [8]]
 [[9, 10]]

```

And say I want to map a function `f` at a given level of this tree. For the first level I just do `map(f, tree)`. For the second level I can do `map.(f, tree)`. For the third level I run out of ideas and I am forced to spell it out with a loop. Is there something like a `mapat(f, tree, level)`?

Thanks for any pointers!

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [August 23, 2019, 10:19pm UTC](https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910/2 "2019-08-23T22:19:46Z")

</div>

It can be done with a recursive definition like this:

> [@Idiomatic way to write element-wise combination of array of arrays?](https://discourse.julialang.org/t/idiomatic-way-to-write-element-wise-combination-of-array-of-arrays/18467/10):
>
> More generally, you can recursively define this broadcast up to any depth n like this julia\> function rebroadcast(f,n,args...) n\>1 ? rebroadcast(broadcast,n-1,(f,args...)) : broadcast(f, args...) end rebroadcast (generic function with 1 method) julia\> rebroadcast(div,1,[1],[2]) 1-element Array{Int64,1}: 0 julia\> rebroadcast(div,2,b,a) 2-element Array{Array{Int64,1},1}: [1, 0, 0] [0, 0, 0] julia\> rebroadcast(div,3,ab,aa) 2-element Array{Array{Array{Int64,1},1},1}: [[1, 1…

---

<div class="post-metadata">

**Author:** ![pablosanjose](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pablosanjose/32/7006_2.png) [@pablosanjose](https://discourse.julialang.org/u/pablosanjose)\
**Post date:** [August 23, 2019, 10:31pm UTC](https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910/3 "2019-08-23T22:31:50Z")

</div>

Thanks @tkf, but I don’t think that is quite what I need. `rebroadcast` applies `f` at _every level_ down to `n` (right?). I want to apply it strictly at level `n`. But your idea is great. I can do something similar, like

```julia
julia> mapat(f, t, level) = level == 0 ? f(t) : mapat.(f, t, level-1);

julia> mapat(string, tree, 3)
3-element Array{Array{Array{String,1},1},1}:
 [["1", "2"], ["3", "4"]]
 [["5", "6", "7"], ["8"]]
 [["9", "10"]]

```

Thanks!

EDIT: I had misunderstood! No, your `rebroadcast` is just like the above `mapat`, only written in a different form. Thanks again.

---

<div class="post-metadata">

**Author:** ![StevenSiew](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevensiew/32/218393_2.png) [@StevenSiew](https://discourse.julialang.org/u/StevenSiew)\
**Post date:** [August 23, 2019, 10:51pm UTC](https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910/4 "2019-08-23T22:51:55Z")

</div>

The solution is this I think

```julia
function MagicMapAtLevel(level,func,tree)
    if level == 1
        return map(func,tree)
    else
        for subtree in tree
          MagicMapAtLevel(level-1,func,subtree)
        end
    end
end

tree = [[[1,2],[3,4]],[[5,6,7],[8]],[[9,10]]]

println("level 1")
MagicMapAtLevel(1,println,tree)

println("level 2")
MagicMapAtLevel(2,println,tree)

println("level 3")
MagicMapAtLevel(3,println,tree)

```

with output

```julia
level 1
Array{Int64,1}[[1, 2], [3, 4]]
Array{Int64,1}[[5, 6, 7], [8]]
Array{Int64,1}[[9, 10]]
level 2
[1, 2]
[3, 4]
[5, 6, 7]
[8]
[9, 10]
level 3
1
2
3
4
5
6
7
8
9
10

```

---

<div class="post-metadata">

**Author:** ![mhw](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mhw/32/8469_2.png) [@mhw](https://discourse.julialang.org/u/mhw)\
**Post date:** [August 23, 2019, 11:34pm UTC](https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910/5 "2019-08-23T23:34:08Z")

</div>

If you don’t need the level information you can omit level as parameter. For instance when using arrays with Int64 as base type the function treeMap could have the following form:

```julia
function treeMap(f, tree)
   if typeof(tree) == Array{Int64,1}
      return map(f, tree)
   else
      return map( t -> treeMap(f,t), tree)
   end

end

```

So for instance we get for

```julia
treeMap(x -> x^2, tree)

```

the following result

```julia
3-element Array{Array{Array{Int64,1},1},1}:
 [[1, 4], [9, 16]]
 [[25, 36, 49], [64]]
 [[81, 100]]

```

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [August 23, 2019, 11:53pm UTC](https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910/6 "2019-08-23T23:53:28Z")

</div>

The requirement of the OP is to specify a level. For more general solution, maybe I’d go with something like

```julia
julia> mapover(f, iselement, x) =
           iselement(x) ? f(x) : map(e -> mapover(f, iselement, e), x)
mapover (generic function with 1 method)

julia> mapover(string, x -> x isa Number, [[1, [2]], (3, (four = 4,))])
2-element Array{Any,1}:
 Any["1", ["2"]]
 ("3", (four = "4",))

```

---

<div class="post-metadata">

**Author:** ![hhaensel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hhaensel/32/1207_2.png) [@hhaensel](https://discourse.julialang.org/u/hhaensel)\
**Post date:** [January 20, 2022, 12:33pm UTC](https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910/7 "2022-01-20T12:33:06Z")

</div>

In order to reduce the number of calls and to generalise to more arguments, I’d propose

```julia
mapat(f, c...; level::Int = 1) = level == 1 ? f.(c...) : level > 1 ? mapat.(f, c..., level = level - 1) : f(c...)

```

---

<div class="post-metadata">

**Author:** ![xor0110](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/xor0110/32/7926_2.png) [@xor0110](https://discourse.julialang.org/u/xor0110)\
**Post date:** [October 22, 2022, 1:59pm UTC](https://discourse.julialang.org/t/map-a-function-at-a-given-level-of-a-tree/27910/8 "2022-10-22T13:59:56Z")

</div>

This package [GitHub - nlw0/ArrayTrees.jl: ArrayTrees for Julia](https://github.com/nlw0/ArrayTrees.jl) allows you to `map` a function to an array-of-arrays. You cannot control the depth of the traversal, but in the OP example, and in many practical situations, the tree has the same depth in every branch, and all the elements at the third level are of type `Int64`, so the package will work if this is specified as the leaf type. Specifying `Vector{Int64}` as the leaf type would allow you to traverse on the second-to-last level.
