6
$\begingroup$

If I have a header and I want this header to be prepended to every list but apparently the Map (\@) function does not work with PrependTo. Here are my code.

data1={{1,1},{1,1}};
data2={{2,2},{2,2}};
data3={{3,3},{3,3}};
data4={{4,4},{4,4}};

header = {"A", "B"};
PrependTo[#, header] & /@ {data1,data2,data3,data4}

I got:

{PrependTo[{{1, 1}, {1, 1}}, {"A", "B"}], 
 PrependTo[{{2, 2}, {2, 2}}, {"A", "B"}], 
 PrependTo[{{3, 3}, {3, 3}}, {"A", "B"}], 
 PrependTo[{{4, 4}, {4, 4}}, {"A", "B"}]}

which is not evaluated. I guess it to do with PrependTo. Any idea to fix this?

The reason I was using PrependTo was I want to do

Export["data.xlsx",{"data1" -> data1, "data2" -> data2, "data3" -> data3, "data4" -> data4}]

i.e., I did want the lists to be updated before exporting.

Many thanks. Leo

$\endgroup$
  • $\begingroup$ Use Prepend instead of PrependTo. The latter tries to reset the value of a symbol given as its argument, and that fails when you try to reset the value of a list, which is not a symbol. See the Details section of the docs for PrependTo. As a side note, you should also get a number of errors during your evaluation, which have a pretty good hint at why your original code does not work. If you don't see them, you might want to check that you haven't silenced those messages somehow. $\endgroup$ – MarcoB Mar 8 '17 at 0:02
  • $\begingroup$ I did try Prepend but how to make it work with /@ (in pure function form) ? $\endgroup$ – Leo1215 Mar 8 '17 at 0:11
7
$\begingroup$

Map does not work with PrependTo because Map does not hold its variables and PrependTo requires a variable so that it can prepend to the value of that variable.

That is, Map does not pass data1 to PrependTo. Instead it resolves data1 to its value, {{1,1},{1,1}}, and then passes this value to PrependTo. However, PrependTo needs a variable (not the value of a variable).

You can get around this by using Prepend as @WReach has suggested.

{data1, data2, data3, data4} = Prepend[#, header] & /@ {data1, data2, data3, data4};

However, if your data* values are sufficiently large that you do not want to do the copy-prepend-assign then there is a solution with GeneralUtilities`HoldMap.

GeneralUtilities`HoldMap[PrependTo[#, header] &, {data1, data2, data3, data4}];

Then

data1
{{"A", "B"}, {1, 1}, {1, 1}}

and similarly for the other variables.

Hope this helps.

$\endgroup$
  • $\begingroup$ This is exactly what I want. Thanks! $\endgroup$ – Leo1215 Mar 8 '17 at 0:25
  • $\begingroup$ It seems I don't have GeneralUtilities`HoldMap in v10.1. I am betting it is based on Unevaluated. Would you mind posting the code so I can see? PrintDefinitions @ HoldMap $\endgroup$ – Mr.Wizard Mar 8 '17 at 1:52
  • $\begingroup$ @Mr.Wizard It was introduced in either 10.3 or 10.4. The code with its helper functions is too large to post in a comment. The first argument must be a Function form and with a bit of metaprogramming it inserts the HoldFirst attribute. There is also HoldScan which produces essentially the same form as in my response. @Edmund: nice find +1. $\endgroup$ – WReach Mar 8 '17 at 2:12
7
$\begingroup$

Use Prepend instead of PrependTo.

Prepend[#, header] & /@ {data1, data2, data3, data4}

(* { {{A,B},{1,1},{1,1}},
     {{A,B},{2,2},{2,2}},
     {{A,B},{3,3},{3,3}},
     {{A,B},{4,4},{4,4}} } *)

The difference is that PrependTo not only creates a new list with a prepended element but it also assigns the result back to the variable in the first argument:

myList = {1, 2, 3};

PrependTo[myList, "abc"]
(* {"abc", 1, 2, 3} *)

myList
(* {"abc", 1, 2, 3} *)

Contrast this to Prepend, which does not update the variable:

myList = {1, 2, 3};

Prepend[myList, "abc"]
(* {"abc", 1, 2, 3} *)

myList
(* {1, 2, 3} *)

The first argument to PrependTo must be an assignable variable:

PrependTo[{1, 2, 3}, "abc"]
(* PrependTo::rvalue:
     {1,2,3} is not a variable with a value, so its value cannot be changed. *)

... but Prepend makes no such demand:

Prepend[{1, 2, 3}, "abc"]
(* {"abc", 1, 2, 3} *)

For this reason, Prepend is more suitable for use with higher-level functions such as Map.

Responding to the Updated Question

If we wish to update the original variables, we need to prevent the evaluation of the individual variables. For example:

Scan[Function[Null, PrependTo[#, {"A", "B"}], HoldFirst], Hold[data1, data2, data3, data4]]

data1
(* {{"A","B"},{1,1},{1,1}} *)

data2
(* {{'A","B"},{2,2},{2,2}} *)

data3
(* {{"A","B'},{3,3},{3,3}} *)

data4
(* {{"A","B"},{4,4},{4,4}} *)

Note the use of Hold to prevent the variable list from being evaluated and the use of the HoldFirst attribute on the pure function for the same purpose.

A Plague Of (and On) Held Expressions

All of this holding seems very complicated for such a simple operation. This is unfortunately a common occurrence when working with held expressions. Since the first argument to PrependTo must be held, our mapped operator must hold that argument as well. Which, in turn, means that the list over which we wish to iterate must be held as well. And so on up the chain.

Held expressions are contagious, and once we want to start iterating over them they spread like the plague. In this simple example, there were two prospects for error: if we had omitted the Hold or HoldFirst. But in more complex programs the chance for so-called "evaluation leaks" to spring up grows quickly.

All of the built-in destructive operators (like PrependTo, Set, SetDelayed etc) require held arguments. In other languages, programs involving destructive operators are harder to reason about on account of their side-effects (I found the bug but... the original values are now overwritten and lost. whoops). But in Mathematica, destructive operators have the additional complication of potential evaluation leaks and hold-contagion.

This is why the Mathematica documentation stresses the transformative and functional programming styles -- precisely to avoid such complexity.

In the case at hand, the desired list can be created directly in a functional style:

Thread[{"data1","data2","data3","data4"} -> Prepend[header] /@ {data1,data2,data3,data4}]

(* { "data1" -> {{"A", "B"}, {1, 1}, {1, 1}}
   , "data2" -> {{"A", "B"}, {2, 2}, {2, 2}}
   , "data3" -> {{"A", "B"}, {3, 3}, {3, 3}}
   , "data4" -> {{"A", "B"}, {4, 4}, {4, 4}}
   } *)

There is no need to overwrite the original variables. (in earlier versions of Mathematica the operator mapped may need to be written as Prepend[#, header]&)

$\endgroup$
  • $\begingroup$ In fact, I do need the lists to be updated for exporting. --- I have updated my questions. Sorry for being unclear. $\endgroup$ – Leo1215 Mar 8 '17 at 0:19
  • $\begingroup$ I have updated my response for the new requirement. $\endgroup$ – WReach Mar 8 '17 at 0:25
  • $\begingroup$ Thanks ! slightly complicated. $\endgroup$ – Leo1215 Mar 8 '17 at 0:29
  • $\begingroup$ That is very clever. An attributed pure function with slots, a held sequence of assigned symbols, and all bundled up in Scan. (+1) Very clever, indeed. $\endgroup$ – Edmund Mar 8 '17 at 0:31
  • $\begingroup$ @Leo1215 Indeed... held expressions breed complexity. I added a discussion on this point. $\endgroup$ – WReach Mar 8 '17 at 2:04
5
$\begingroup$

Unevaluated

You can Map Unevaluated over the held Symbols to keep PrependTo[#, header] & from evaluating each of them too soon.

Unevaluated /@ Hold[data1, data2, data3, data4] // Scan[# ~PrependTo~ header &]

data3
{{"A", "B"}, {3, 3}, {3, 3}}

Automated solutions

A slight modification to a method from WReach gives us a very clean solution:

SetAttributes[vars, HoldAll]
vars /: s : _[_vars, _] := CompoundExpression @@ Thread[Unevaluated @ s, vars, 1];

Now just write:

PrependTo[vars[data1, data2, data3, data4], header];

We could also apply my autoThread function, still under development:

PrependTo[{data1, data2, data3, data4}, header] // autoThread;

Alternative approach

If you wish to avoid all of this consider using indexed objects to store you data:

data[1] = {{1, 1}, {1, 1}};
data[2] = {{2, 2}, {2, 2}};
data[3] = {{3, 3}, {3, 3}};
data[4] = {{4, 4}, {4, 4}};

header = {"A", "B"};

This allows you to use:

PrependTo[data[#], header] & /@ {1, 2, 3, 4};

Or:

Array[PrependTo[data[#], header] &, 4];

Or:

Do[PrependTo[data[i], header], {i, 4}]

Recommended reading:

$\endgroup$
  • $\begingroup$ Very interesting techniques used here. (+1) Learned somethings new. $\endgroup$ – Edmund Mar 8 '17 at 11:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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