9
$\begingroup$

Why does this work:

For[i = 1, i <= Dimensions[list][[1]], i++, list = Insert[list, i, {i, 3}]]

But this doesn't:

insertColumns[list_] :=  
  For[i = 1, i <= Dimensions[list][[1]], i++, 
    list = Insert[list, i, {i, 3}]]

I get a message saying that something in the part assignment is not a symbol. But this:

insertColumns[list_] := 
  For[i = 1, i <= Dimensions[list][[1]], i++,
    Return[FullForm[list[[i]]]]]

Returns this result:

List[1,"Red","Name","Random"]

I am very new to Mathematica, so please be kind :)

$\endgroup$
5
  • $\begingroup$ Try insertColumns[list_ List] to indicate a list is input. $\endgroup$ – Fred Kline Apr 16 '16 at 2:11
  • $\begingroup$ That seems to get rid of the error messages, but just returns insertColumns[{the list of things I gave it}] $\endgroup$ – Texas Red Apr 16 '16 at 3:38
  • $\begingroup$ What is the input you're using? To reproduce the error you should let us know what list you actually used. $\endgroup$ – Jens Apr 16 '16 at 3:39
  • $\begingroup$ {{1, "Red", "Name", "Random"}, {1, "Red", "Tim", "asdfg"}} So when I pass this to the function, it just returns: insertColumns[{{1, "Red", "Name", "Random"}, {1, "Red", "Tim", "asdfg"}}] $\endgroup$ – Texas Red Apr 16 '16 at 3:46
  • $\begingroup$ With a slight modification it works - see my answer. $\endgroup$ – Jens Apr 16 '16 at 3:50
7
$\begingroup$

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.

$\endgroup$
3
  • $\begingroup$ could you please explain what you mean by un-Mathematica-like? I'd rather be working with the language than fighting it. $\endgroup$ – Texas Red Apr 16 '16 at 18:18
  • $\begingroup$ Also, could you clarify why my original code doesn't work inside the function, but this does? $\endgroup$ – Texas Red Apr 16 '16 at 18:44
  • $\begingroup$ OK, I've added an explanation. $\endgroup$ – Jens Apr 16 '16 at 20:33
5
$\begingroup$

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}}}
$\endgroup$

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.