7
$\begingroup$

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)?

$\endgroup$
1
  • $\begingroup$ 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. $\endgroup$
    – DavidC
    Sep 19, 2012 at 4:12

4 Answers 4

9
$\begingroup$

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}]
$\endgroup$
5
  • $\begingroup$ 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? $\endgroup$
    – VF1
    Sep 19, 2012 at 3:33
  • $\begingroup$ 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! $\endgroup$
    – halirutan
    Sep 19, 2012 at 3:36
  • $\begingroup$ Great. Thanks. I confused my /. with Replace, whereas it actually is ReplaceAll. $\endgroup$
    – VF1
    Sep 19, 2012 at 3:37
  • $\begingroup$ +1 For a good read of the motivation behind the question. $\endgroup$
    – DavidC
    Sep 19, 2012 at 3:41
  • $\begingroup$ @DavidCarraher that's funny. I upvoted your other answer seconds ago because you extended your explanation ;-) $\endgroup$
    – halirutan
    Sep 19, 2012 at 3:45
4
$\begingroup$

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]:

treeform

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]}}

$\endgroup$
5
  • $\begingroup$ 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? $\endgroup$
    – VF1
    Sep 19, 2012 at 3:05
  • $\begingroup$ @VF1 Not at all. This sort of issue is easy to stumble on. $\endgroup$
    – DavidC
    Sep 19, 2012 at 3:08
  • $\begingroup$ 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. $\endgroup$
    – DavidC
    Sep 19, 2012 at 3:10
  • $\begingroup$ 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]}}. $\endgroup$
    – rcollyer
    Sep 19, 2012 at 3:32
  • $\begingroup$ Good catch. I must have copied the answer from some other place. $\endgroup$
    – DavidC
    Sep 19, 2012 at 3:35
3
$\begingroup$

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.

$\endgroup$
4
  • $\begingroup$ 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?" $\endgroup$
    – VF1
    Sep 19, 2012 at 3:14
  • $\begingroup$ 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)? $\endgroup$
    – VF1
    Sep 19, 2012 at 3:19
  • $\begingroup$ So it's not at certain depth, but instead, at certain places? $\endgroup$ Sep 19, 2012 at 3:20
  • $\begingroup$ You could think of it that way. I only want parts that are one-dimensional lists to be affected. $\endgroup$
    – VF1
    Sep 19, 2012 at 3:26
3
$\begingroup$

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.

$\endgroup$
2
  • $\begingroup$ 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? $\endgroup$
    – VF1
    Sep 19, 2012 at 3:35
  • $\begingroup$ @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. $\endgroup$
    – Mr.Wizard
    Sep 19, 2012 at 4:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.