1
$\begingroup$

Consider a set of lists:

lista={{1,2},{3,4},{3,5},{4,8}}
listb={{3,5},{7,6},{2,9},{3,7}}
listc={{1,23},{3,23},{34,56}}

I want to do some operation on them and then export the result.

Do[
x=Listplot[n];
Export["path"<>ToString[x]<>.jpg,x,"JPEG"]
,{n,{lista,listb,listc}}
]

However this doesn't work as x get evaluated in the ToString[] function. I tried using Hold[] on x inside the ToString[] but then it doesn't evaluate the name at all. Im looking for a way to use the evaluated form of x in the plotting and only keep the evaluation up to the name of the variable in the ToString[] function. Any ideas how to do this?

$\endgroup$
2
$\begingroup$

These are ordered by closeness to the original attempt - I think something like the third approach is the most natural to work with.

Approach 1

This approach is the closest to the original in terms of how n can be used, but is not very pretty otherwise...

Do[
  Export[
    "path" <> (Hold@n /. OwnValues@n /. Hold[x_] :> ToString[Unevaluated@x]) <> ".jpg",
    ListPlot[n],
    "JPEG"
  ],
  {n, Unevaluated /@ Unevaluated@{lista, listb, listc}}
]

This relies on preventing evaluation of lista,... at two key points:

  • In the iterator specification of Do: Here, we need to wrap every element of the list in Unevaluated. The second Unevaluated is to prevent the list from being evaluated by Map (the Unevaluated/@… part)
  • In the ToString part: We need to evaluate one layer of definitions (i.e. we need to insert the value of n) using Hold[n]/.OwnValues[n]. This gives us something of the form Hold[lista]. Then we pass this to ToString, where we use Unevaluated again. Also, we do this using a replacement rule to have better control over the evaluation.

Approach 2

The main idea is similar, but we initially wrap the variables in Hold to make handling them easier:

Do[
  Export[
    "path" <> (n /. Hold[x_] :> ToString[Unevaluated@x]) <> ".jpg",
    ListPlot[ReleaseHold@n],
    "JPEG"
  ],
  {n, Hold /@ Unevaluated@{lista, listb, listc}}
]

As mentioned above, the main idea is similar to approach 1. The difference is that n is now of the form Hold[lista]. This means that we do not have to be so careful when getting the name of the symbol. We just have to use ReleaseHoldwhenever we actually need to insert the value.

Approach 3

There are better ways to label data than to try and extract variable names (while simultaneously preventing their evaluation): Associations. Most operations are transparent to the keys, but if you need them (e.g. to build the file name), you can access them using functions like KeyValueMap:

lists = <|
  "lista" -> {{1, 2}, {3, 4}, {3, 5}, {4, 8}},
  "listb" -> {{3, 5}, {7, 6}, {2, 9}, {3, 7}},
  "listc" -> {{1, 23}, {3, 23}, {34, 56}}
|>;

KeyValueMap[
  Export["path" <> # <> ".jpg", ListPlot[#2], "JPEG"] &,
  lists
]
$\endgroup$
  • $\begingroup$ Thank you for the break down, approach 1 and 2 is what I was after. I know its not the most elegant or functional way, but I wanted to understand how to hold variables at different points. I used approach 3 in my code before posting this question but I was just curious how it could be done. $\endgroup$ – Giovanni Baez Jun 25 '18 at 18:13
1
$\begingroup$

This helper function converts precisely the expression given to it to a string:

holdToString[s_] := StringTake[ToString[Hold[s]], {6, -2}];
SetAttributes[holdToString, HoldAll];

However, this means a little bit of trickery is required to get the iterated variables to appear at all. After all, this doesn't work:

Table[holdToString[i], {i, {lista, listb, listc}}]
{"i", "i", "i"}

Introducing With to perform the substitution works, however:

Table[With[{iter = i}, holdToString[iter]], {i, {lista, listb, listc}}]
{"lista", "listb", "listc"}

Using this to do what I suspect you're trying to do, an initial approach would be something like this:

Do[
  With[{iter = n},
    x = ListPlot[iter];
    Export["path"<>holdToString[iter]<>".jpg",x,"JPEG"];
    ];,
  {n, {lista,listb,listc}}
];

However, this is fundamentally flawed because replacement with the defined list happens in the iterator variables. To preserve variable names reliably, it'd probably be best to switch to a more function-oriented paradigm, such as:

export[x_] := 
  Export["path" <> holdToString[x] <> ".jpg", ListPlot[x], 
   "JPEG"];
SetAttributes[export, {HoldAll, Listable}];

export[{lista,listb,listc}];
$\endgroup$
  • 1
    $\begingroup$ I'm not sure this approach will work, as the values lista,... have values assigned to them $\endgroup$ – Lukas Lang Jun 25 '18 at 17:48
  • $\begingroup$ @LukasLang Very true, see update. I think this should work efficiently for your scenario. $\endgroup$ – eyorble Jun 25 '18 at 18:00
  • $\begingroup$ Works as intended. $\endgroup$ – Giovanni Baez Jun 25 '18 at 18:08

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.