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]
ListMap4[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.