Assigning values to a list of variable names

15

12

As part of a calculation I need to do something like this

Evaluate[{aaa, bbb, ccc}[[ index]]] = {1, 2, 3, 4, 5}

so if index is 1 then {1, 2, 3, 4, 5} will be stored into the variable aaa.
But if I re-evaluate this it does not work because aaa is now a list and not a variable. I tried various options with Hold[] etc but did not manage to solve this.

BlueMac

Posted 2012-04-03T17:12:05.087

Reputation: 701

1

By the way please also see: Elegant manipulation of the variables list

– Mr.Wizard – 2014-08-27T06:52:49.273

Related: (1), (2), (3), (4), (5)

– Mr.Wizard – 2012-06-06T23:49:57.540

All: please comment if you believe this question or any of the "Related" ones should be merged. – Mr.Wizard – 2012-06-06T23:54:38.327

Answers

23

This is a fairly natural question and I feel it is worthy of attention. I am going to answer in two parts. First, I am going to show a method that is more appropriate for Mathematica programming and which I recommend you use instead. Then I will show how to force the action you are attempting.


Better Alternatives

The common way to accomplish programmatically selected assignments is to use indexed variables. This allows you to assemble a "variable" from inert parts. For example, one would use a single variable var and simply make assignments (SeedRandom[1] for a consistent result):

SeedRandom[1]

Do[
  var[i] = RandomInteger[9],
  {i, {1, 2, 3, 2, 3, 1, 3}}
]

Or recall them:

var /@ {1, 2, 3}
{0, 7, 8}

If you desire a certain name be attached to a value you can index with Strings.

names = {"aaa", "bbb", "ccc"};

i = 1;

var[ names[[i]] ] = Sqrt[2]; (* dummy first assignment *)

var[ names[[i]] ] = {1, 2, 3, 4, 5};

var["aaa"]
{1, 2, 3, 4, 5}

In passing, depending on your application you may find Rules applicable.

Associations

Mathematica 10 introduced Associations which are like self-contained "indexed variables." Use is similar but you need to start with an (optionally empty) Association before you make assignments. Example:

SeedRandom[1]

asc = <||>;

Do[asc[i] = RandomInteger[9], {i, {1, 2, 3, 2, 3, 1, 3}}]

asc
<|1 -> 0, 2 -> 7, 3 -> 8|>

Values may be recalled using Map, Replace, or Lookup; for a comparison see:

For some ideas of when and why one might use associations over "indexed variables" see:


Forcing the behavior

Suppose you need the behavior you asked for to keep a large program working without extensive modification.

Method #1

This works because Part preserves the head of the expression, here Unevaluated.

Ignore the syntax highlighting in Unevaluated: this is a nonstandard but safe use.

This could easily use the same syntax as Method #2: assign[symbols_, idx_, val_] :=

ClearAll[aaa, bbb, ccc, assign]
assign[idx_, val_] := (# = val) & @ symbols[[1, {idx}]]

symbols = Hold @ Unevaluated[aaa, bbb, ccc];

assign[1, "dummy"];
assign[1, Range@5];

aaa
{1, 2, 3, 4, 5}

Method #2

This uses the injector pattern in preference to Unevaluated.

ClearAll[aaa, bbb, ccc, f1, assign]
assign[symbols_, idx_, val_] := symbols[[{idx}]] /. _[x_] :> (x = val)

symbols = Hold[aaa, bbb, ccc];

assign[symbols, 1, "dummy"];
assign[symbols, 1, Range@5];

aaa
{1, 2, 3, 4, 5}

Mr.Wizard

Posted 2012-04-03T17:12:05.087

Reputation: 259 163

Cool! For Association Lists I would add ClearAll[assign]; SetAttributes[assign, HoldFirst]; assign[association_, key_, val_] := Hold[association[key]] /. _[x_] :> (x = val) – tchronis – 2017-07-19T19:24:36.313

1@tchronis You should not need the injector pattern here; a straightforward assignment will work: SetAttributes[assign, HoldFirst]; assign[association_, key_, val_] := association[key] = val. Much of the time I would just write out (asc[#] = #2) & however if I needed to apply that across a set of keys and values. – Mr.Wizard – 2017-07-20T06:39:26.270

you are right. I overcomplicated things here. When the keys are themselves global variables it can be confusing (that was my case). Thank you. – tchronis – 2017-07-20T07:35:56.027

3

Keep data out of your variable names

Use functional programming where, whenever you use aaa, it is an argument to a function. If you want to pass something different in, call the function.

If you really want to do this, use one of the HoldAll-like attributes here.

ninjagecko

Posted 2012-04-03T17:12:05.087

Reputation: 131

2

I would not recommend it, but you could do something like

varlist = "var1,var2,var3";
index   = 2;
ToExpression[StringSplit[varlist, ","][[index]] ~~ "={1, 2, 3, 4, 5}"];
var2
(* -> {1, 2, 3, 4, 5} *)

Dr. belisarius

Posted 2012-04-03T17:12:05.087

Reputation: 112 848