Is there a good way to map a function over a list to lists exclusively of a certain depth?

Let's say I have some ragged list. If some elements have some depth $n$, then is there a way I can map a function to only those elements?

I.e., for some list foo,

foo = {1, {2, 3}, {4, 5, {6, 7}}, {8}, {9}}


The result should then be for $n=2$:

{1, {bar[2], bar[3]}, {4, 5, {6, 7}}, {bar[8]}, {bar[9]}}


I want to apply a function bar to all parts of it that are of depth 2, and depth 2 only. So far, I have this attempt:

# /. (p_List /; Depth[p] == 2) :> (bar /@ p) & /@ foo
(* {1, {bar[2], bar[3]}, {4, 5, {bar[6], bar[7]}}, {bar[8]}, {bar[9]}} *)


As you can see, pattern matching looks at ALL lists, so I can't really use what I have. I suspect there could be some better, more functional solution to this (possibly using Scan, Reap, and Sow)?

• It was a good idea to withhold awarding the checkmark until a few more answers came in. They were worth the wait. And halirutan apparently spotted just what you were looking for. Commented Sep 19, 2012 at 4:12

To achieve the requested output it is not sufficient to Map the function bar at a specific level. What you maybe want to express is map the function on level 1 if it consists only of list of numbers

Map[If[MatchQ[#, {__Integer}], bar /@ #, #] &, foo, {1}]

(*
{1, {bar[2], bar[3]}, {4, 5, {6, 7}}, {bar[8]}, {bar[9]}}
*)


Another possibility is to use Replace and a specific level. But the idea is the same

Replace[foo, l : {__Integer} :> bar /@ l, {1}]

• This solution is what I'm looking for. But how does pattern matching for l:{__Integer} not pick up {6,7}? Is that not a list of an integer sequence?
– VF1
Commented Sep 19, 2012 at 3:33
• Yes, but the Map and the Replace act only on the first level. This means does not see {6,7} it sees {4,5,{6,7}} which does not match! Commented Sep 19, 2012 at 3:36
• Great. Thanks. I confused my /. with Replace, whereas it actually is ReplaceAll.
– VF1
Commented Sep 19, 2012 at 3:37
• +1 For a good read of the motivation behind the question. Commented Sep 19, 2012 at 3:41
• @DavidCarraher that's funny. I upvoted your other answer seconds ago because you extended your explanation ;-) Commented Sep 19, 2012 at 3:45

Level 2 of foo is:

Level[foo, {2}]


{2, 3, 4, 5, {6, 7}, 8, 9}

So if you try to map bar onto level 2, you will get:

Map[bar, foo, {2}]


{1, {bar[2], bar[3]}, {bar[4], bar[5], bar[{6, 7}]}, {bar[8]}, {bar[9]}}

Let's look at the structure of foo, using TreeForm[foo]:

If you really want to obtain {1, {bar[2], bar[3]}, {4, 5, {6, 7}}, {bar[8]}, {bar[9]}}, you should use MapAt:

MapAt[bar, foo, {{2, 1}, {2, 2}, {4, 1}, {5, 1}}]


{1, {bar[2], bar[3]}, {4, 5, {6, 7}}, {bar[8]}, {bar[9]}}

• Now I feel a bit silly for asking... But you had to use specific positions for the MapAt function. What if you didn't know foo?
– VF1
Commented Sep 19, 2012 at 3:05
• @VF1 Not at all. This sort of issue is easy to stumble on. Commented Sep 19, 2012 at 3:08
• By the way, you feel free to hold off awarding the check until you've received more input. There are several ways to skin the cat. Commented Sep 19, 2012 at 3:10
• David, I think your initial Map results are off, it should give: {1, {bar[2], bar[3]}, {bar[4], bar[5], bar[{6, 7}]}, {bar[8]}, {bar[9]}}. Commented Sep 19, 2012 at 3:32
• Good catch. I must have copied the answer from some other place. Commented Sep 19, 2012 at 3:35

Mapping f[] over foo

 Map[f[#] &, foo, {2}]


returns

{1, {f[2], f[3]}, {f[4], f[5], f[{6, 7}]}, {f[8]}, {f[9]}}


Which agrees with the comment above. If you wanted to apply the function at a certain level, then you don't have to know about the list in advance; just specify the level as

Map[function[#]&,list,{level}]


or some version of that construction. By the way, mapping is a much easier way to apply functions to lists.

• Perhaps my title is misleading. I actually want what my "expectation" shows. I'll reword the title to say "Is there a good way to map a function over a list to lists exclusively of a certain depth?"
– VF1
Commented Sep 19, 2012 at 3:14
• But doesn't Map[...] map the function over all parts of the list of a certain level, instead of just the ones with only a certain depth (which is what I want)?
– VF1
Commented Sep 19, 2012 at 3:19
• So it's not at certain depth, but instead, at certain places? Commented Sep 19, 2012 at 3:20
• You could think of it that way. I only want parts that are one-dimensional lists to be affected.
– VF1
Commented Sep 19, 2012 at 3:26

I find your question and examples rather opaque. Some operations that may be what you're looking for, or at least usefully related:

Map[bar, foo, {2}]

{1, {bar[2], bar[3]}, {bar[4], bar[5], bar[{6, 7}]}, {bar[8]}, {bar[9]}}

Map[bar, foo, {-2}]

{1, bar[{2, 3}], {4, 5, bar[{6, 7}]}, bar[{8}], bar[{9}]}

Replace[foo, x_Integer :> bar@x, {2}]

{1, {bar[2], bar[3]}, {bar[4], bar[5], {6, 7}}, {bar[8]}, {bar[9]}}

Replace[foo, x : {__Integer} :> bar /@ x, {1}]

{1, {bar[2], bar[3]}, {4, 5, {6, 7}}, {bar[8]}, {bar[9]}}

Replace[foo, p_List /; Depth[p] == 2 :> bar /@ p, {1}]

{1, {bar[2], bar[3]}, {4, 5, {6, 7}}, {bar[8]}, {bar[9]}}


On reflection I suspect the last one is what you're actually seeking.

• In regard to the last one (which is indeed something I'm looking for), what the difference with what you wrote compared to what I wrote in my question? Is it that /. mean ReplaceAll, which looks at all levels for lists of depth two?
– VF1
Commented Sep 19, 2012 at 3:35
• @VF1 /. is indeed ReplaceAll, and it tries to match the given pattern at all levels. Replace only looks for the pattern at the specified level(s). Your Map construct would be equivalent to Replace[foo, p_List /; Depth[p] == 2 :> bar /@ p, {1, -1}] meaning from the first level to the last, but not "level zero" which is the entire expression. Commented Sep 19, 2012 at 4:39