MapThread gives different results from ToExpression when trying to assign variables from a list



I have the following data set:

data = {{"Jakarta","Surabaya","Bandung"},{1,2,3}}

and I'd actually like to assign the numerical values to the categorical data so that Jakarta = 1, Surabaya = 2 and Bandung = 3.

If I use MapThread[Set, {ToExpression[data[[1]]], data[[2]]}]it works fine but when I try to assign the variables individually I keep on getting error messages:

ToExpression[data[[1, 1]]] = data[[2, 1]]
Set::write: "Tag ToExpression in ToExpression[Jakarta] is Protected."

However, ToExpression[data[[1,1]] works fine

May I know why my second approach is not working?

Ruben Garcia

Posted 2012-03-14T04:16:10.467

Reputation: 637



I believe it is because Set has attribute HoldFirst. The FullForm of what you are attempting would look like...


The ToExpression doesn't get a chance to evaluate before trying to assign the value. You can use Evaluate if you insist on doing it this way.

Evaluate[ToExpression[data[[1, 1]]]] = data[[2, 1]]

Andy Ross

Posted 2012-03-14T04:16:10.467

Reputation: 18 640

Thank you for the clarification. I understand the reason now. – Ruben Garcia – 2012-03-22T09:40:36.397


I think avoiding ToExpression is important, so here is a solution using Symbol:

MapThread[With[{var = (Clear[#1]; Symbol[#1])}, var = #2] &, data]

I use Clear to be sure that the symbol has not a previous value defined, which would generate an assigment error.

You can also use Evaluate instead With as @Andy answer:

MapThread[(Clear[#1]; Evaluate[Symbol[#1]] = #2) &, data]


Posted 2012-03-14T04:16:10.467

Reputation: 3 792

Thanks a lot for the suggestion. I implemented it in my code and it works perfectly. – Ruben Garcia – 2012-03-22T09:40:15.850


Since Set attempts to assign to the object on the LHS itself, unless that object has head List, you are attempting to assign a value to ToExpression[data[[1, 1]]] just as the error message informs you.

Also, you will have a problem if your symbol names already have a value when you try your MapThread method. You need a way to get and hold the unevaluated symbol and then pass it to Set. This can be done with ToHeldExpression (or MakeExpression) as follows:

 Set @@ Append[ToHeldExpression@#, #2] &,

This works by building the arguments for Set inside Hold and then passing them to Set with Apply (@@).

Doing this for a single element:

Set @@ Append[ToHeldExpression[ data[[1, 1]] ], data[[2, 1]] ]


Posted 2012-03-14T04:16:10.467

Reputation: 259 163

Thank you for the explanations. I didn't know there was such a command (ToHeldExpression) – Ruben Garcia – 2012-03-22T09:42:23.990