For loop works at top-level but not in a function
Assuming you use a list
input that is appropriate for use with Insert
as it appears in the function, you would only have to localize list
to avoid further errors:
insertColumns[list_] :=
Module[{l = list},
For[i = 1, i <= Dimensions[list][[1]], i++,
l = Insert[l, i, {i, 3}]]; l]
insertColumns[{{a1, b1, c1}, {a2, b2, c2}, {a3, b3, c3}}]
(* ==> {{a1, b1, 1, c1}, {a2, b2, 2, c2}, {a3, b3, 3, c3}} *)
insertColumns[{{1, "Red", "Name", "Random"}, {1, "Red",
"Tim", "asdfg"}}]
(*
==> {{1, "Red", 1, "Name", "Random"}, {1, "Red", 2, "Tim",
"asdfg"}}
*)
However, this entire approach is of course very un-Mathematica-like.
Explanation
The reason why you have to localize the variable list
is that Mathematica uses what can be called "pass-by-value" in which the argument list
is resolved into the actual list of entries. The dummy variable name list
represents a function slot and is not actually a Symbol
to which you should try to assign things. There is no need to know about this when you just extract values from list
(as with the dimension in your code), but you get into trouble when actually trying to assign new values to the arguments named by the dummy variable list
.
The assignment by Set
(=
) in the For
loop sees a list structure on both sides of the equation, and attempts to Thread
over it to make the assignment element-wise. But since the right-hand side is a modified version of list
in which elements have been added, the dimensions of the lists in Set
don't match. This causes an error.
The localized variable l
is treated as a Symbol
and not a "dummy variable", so Set
doesn't try to match dimensions in that case. This is why the above code avoids the error.
The other issue is one of aesthetics and efficiency. In the For
loop, the entire list is copied in every step. Also, simple iterations are often better done using special constructs that operate on Lists
without explicitly introducing a counter index, as required in For
. There is a lot of relevant information about this in the post Elegant operations on matrix rows and columns. The MapIndexed
approach in @m_goldberg's answer is mentioned there, too.
As Jens says your approach is not very Mathematica-like, i.e., it doesn't make good use of Mathematica's strengths. Here is an implementation that I hope Jens would agree makes better use of those strengths. Although it is more concise than your method, it has more generality; you can pick the column where the index of the list element is inserted.
insertIndex[data_List, col_Integer] /; 0 < col <= Length[data[[1]]] + 1 :=
MapIndexed[Insert[#1, #2[[1]], col] &, data]
To digest this answer, you should look up MapIndexed
, Part
and Function
in the Documentation Center. The article under Part
will explain the [[ ]]
notation and article under Function
will explain the #1
, #2
, and &
stuff.
The rather elaborate formal argument pattern is designed restrict arguments to valid ones -- not perfectly, but usefully.
Testing
First I generate a list of 6 3-element lists.
data[n_] := With[{names = Alphabet[]}, Table[RandomSample[names, 3], n]]
SeedRandom[42]; test = data[6]
{{"n", "u", "e"}, {"c", "b", "g"}, {"a", "t", "e"}, {"d", "i", "m"}, {"a", "h", "i"}, {"w", "z", "g"}}
Then I have insertIndex
insert the index of each sublist at positions 1 to 4
Table[insertIndex[test, col], {col, Length[test[[1]]] + 1}]
{{{1, "n", "u", "e"}, {2, "c", "b", "g"}, {3, "a", "t", "e"}, {4, "d", "i", "m"}, {5, "a", "h", "i"}, {6, "w", "z", "g"}}, {{"n", 1, "u", "e"}, {"c", 2, "b", "g"}, {"a", 3, "t", "e"}, {"d", 4, "i", "m"}, {"a", 5, "h", "i"}, {"w", 6, "z", "g"}}, {{"n", "u", 1, "e"}, {"c", "b", 2, "g"}, {"a", "t", 3, "e"}, {"d", "i", 4, "m"}, {"a", "h", 5, "i"}, {"w", "z", 6, "g"}}, {{"n", "u", "e", 1}, {"c", "b", "g", 2}, {"a", "t", "e", 3}, {"d", "i", "m", 4}, {"a", "h", "i", 5}, {"w", "z", "g", 6}}}