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. – DavidC Sep 19 '12 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 Sep 19 '12 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! – halirutan Sep 19 '12 at 3:36
Great. Thanks. I confused my /. with Replace, whereas it actually is ReplaceAll. – VF1 Sep 19 '12 at 3:37
+1 For a good read of the motivation behind the question. – DavidC Sep 19 '12 at 3:41
@DavidCarraher that's funny. I upvoted your other answer seconds ago because you extended your explanation ;-) – halirutan Sep 19 '12 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 Sep 19 '12 at 3:05
@VF1 Not at all. This sort of issue is easy to stumble on. – DavidC Sep 19 '12 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. – DavidC Sep 19 '12 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]}}. – rcollyer Sep 19 '12 at 3:32
Good catch. I must have copied the answer from some other place. – DavidC Sep 19 '12 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 Sep 19 '12 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 Sep 19 '12 at 3:19
So it's not at certain depth, but instead, at certain places? – George Wolfe Sep 19 '12 at 3:20
You could think of it that way. I only want parts that are one-dimensional lists to be affected. – VF1 Sep 19 '12 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 Sep 19 '12 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. – Mr.Wizard Sep 19 '12 at 4:39