3
$\begingroup$
In[1]  Map[(# + 7) &, {a, 3 b, {c, d}, e}, {2}]
Out[1]  {a, 10 (7 + b), {7 + c, 7 + d}, e}

But I want feature like ListMap :

In[2]  ListMap[(# + 7) &, {a, 3 b, {c, d}, e}, {2}]
Out[2]  {a, 3b, {7 + c, 7 + d}, e}

(* +7 to every level 2 object. But when determining level, ignore all head except List. *)

The concept of level is very closely related to 'Head', more precisely, 'all kinds of Heads'.
Can we restrict kinds of heads temporarily?

For example, can we do somethig like

'of all heads, only List is regarded as an effective head, when dealing with level concept, only for this time'.

There are many built-in symbols containing 'level' as their arguments. Map,Outer,Apply,...Ideally I hope there exist an original method which works for all such built-in symbols.

$\endgroup$
3
  • $\begingroup$ Replace[{a, 3 b, {c, d}, e}, x_List :> x + 7, {1}] $\endgroup$
    – Ben Izd
    Commented Mar 29, 2021 at 15:41
  • 1
    $\begingroup$ Here's a clarifying question: should ListMap have to encounter Lists all the way down? For instance, in {c, f[{a}, b]}, listmapping h over all lists at level 3 naively would produce {c, f[{h[a]}, b]}; but it would have to "go through" the head f to get there. Should listmap be defined such that it can only "go through" List heads when trying to reach the level it's seeking, or not? $\endgroup$
    – thorimur
    Commented Mar 29, 2021 at 23:52
  • $\begingroup$ Thank you @thorimur. My concept about ListMap is : there is no level 2,3 object for {c, f[{h[a]}, b]}. In fact I was constructing ListMap but wasn't considering such case. The first thing to do may be atomization of all the other heads. $\endgroup$
    – imida k
    Commented Mar 30, 2021 at 3:06

2 Answers 2

1
$\begingroup$

The general solution for using different heads and levels:

customMap[fn_, arr_, heads_List, level_] := 
 Replace[arr, Alternatives @@ (Pattern[x, Blank[#]] & /@ heads) -> fn[x], level]

customMap[(# + 3) &, {a, 3 b, {c, d}, e}, {List}, {1}]

(*Out: {a, 3 b, {3 + c, 3 + d}, e} *)

Solution 1

Using customMap:

ListMap[fn_, arr_, level_] := customMap[fn, arr, {List}, level]

ListMap[(# + 3) &, {a, 3 b, {c, d}, e}, {1}]

(*Out: {a, 3 b, {3 + c, 3 + d}, e} *)

Solution 2

Using Replace:

ListMap[fn_, arr_, level_] := Replace[arr, x_List -> fn[x], level]

ListMap[(# + 3) &, {a, 3 b, {c, d}, e}, {1}]

(*Out: {a, 3 b, {3 + c, 3 + d}, e} *)

If you want to apply to all elements of a list at a specific level:

ListMap[fn_, arr_, level_] := Replace[arr, x_List :> fn /@ x, level]


ListMap[{#, x} &, {a, b, c}, {0}]

(*Out: {{a, x}, {b, x}, {c, x}} *)


ListMap[(# + 7) &, {a, 3 b, {c, d}, e}, {1}]

(*Out: {a, 3 b, {7 + c, 7 + d}, e} *)

Also, levels start at 0, you can change the function definition but I think it's better to be consistent with other functions.

$\endgroup$
2
  • $\begingroup$ Thanks for sincere interest but my idea was misrepresented or you misunderstood something. The output of ListMap[{#, x} &, {a, b, c}, {1}] must be {{a,x},{b,x},{c,x}}. But the functions you presented doesn't work correctly. $\endgroup$
    – imida k
    Commented Mar 30, 2021 at 13:37
  • $\begingroup$ @imidak I edited the post. $\endgroup$
    – Ben Izd
    Commented Mar 30, 2021 at 14:31
0
$\begingroup$

Here's a way of checking the heads "on the way down", and only mapping through expressions with head head; I tried to account for Mathematica's usual level specification, but didn't manage to account for negative level specification (which proceeds from the innermost level).

(* Exact levels {n}: *)

headMap[f_, expr_, head_, {0}] := f[expr]

headMap[f_, expr_, head_, {level_Integer}] := 
 Replace[expr, x_head :> Map[headMap[f, #, head, {level - 1}] &, x, {1}]] /; level > 0

(* Range of levels {n1, n2}: *)

headMap[f_, expr_, head_, {n1_Integer, n2 : (_Integer | Infinity)}] :=
 Which[
  n2 == 0, f[expr],
  n1 == 0, 
   Construct[f, Replace[expr, x_head :> Map[headMap[f, #, head, {0, n2 - 1}] &, x, {1}]]],
  True, Replace[expr, x_head :> Map[headMap[f, #, head, {n1, n2} - 1] &, x, {1}]]
 ] /; n2 >= n1 >= 0

(* Levels given by an integer n or Infinity, i.e. levels 1 through n *)

headMap[f_, expr_, head_, 0] := f[expr]

headMap[f_, expr_, head_, Infinity] := headMap[f, expr, head, {1, Infinity}]

headMap[f_, expr_, head_, level : _Integer] := headMap[f, expr, head, {1, level}] /; level > 0

Note: this could be generalized to accept patterns matching heads instead of explicit heads by using, say, x_ :> Map[...] /; MatchQ[Head[x], head] everywhere instead.

And then we can define

ListMap[f_, expr_, level_] := headMap[f, expr, List, level]

Examples:

ListMap[{#, x} &, {a, b, c}, {1}]

(* Output: {{a, x}, {b, x}, {c, x}} *)

ListMap[{#, x} &, {a, 3 b, c}, Infinity]

(* Output: {{a, x}, {3 b, x}, {c, x}} *)

ListMap[f, {e, d, {a, {b, x}, g[c, {d, h}]}}, {3}]

(* Output: {e, d, {a, {f[b], f[x]}, g[c, {d, h}]}} *)

ListMap[f, {e, d, {a, {b, x}, g[c, {d, h}]}}, Infinity]

(* Output: {f[e], f[d], f[{f[a], f[{f[b], f[x]}], f[g[c, {d, h}]]}]} *)

ListMap[f, {e, d, {a, {b, x}, g[c, {d, h}]}}, {2, 3}]

(* Output: {e, d, {f[a], f[{f[b], f[x]}], f[g[c, {d, h}]]}} *)

As you can check, these are the analogues of what Map does with the corresponding level specifications, but only ever "maps through" heads that are List!

$\endgroup$

Your Answer

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

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