17
$\begingroup$

Suppose I have list

a = Range[10]

{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

in which I want to set some elements to be a list

a[[4 ;; 7]] = {1, 2, 3}; 

{1, 2, 3, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, 8, 9, 10}

Which is fine and dandy unless my part span is the same length as my list:

a[[4 ;; 6]] = {1, 2, 3};

{1, 2, 3, 1, 2, 3, 7, 8, 9, 10}

How can I force the assignment to behave consistently? In my case, I always want the first behaviour. But conceivably someone might always want the second behaviour with errors if the lengths don't match.

$\endgroup$
11
$\begingroup$
(a[[#]] = {1, 2, 3}) & /@ Range[4, 6];

You get:

In[1]:= a

Out[1]= {1, 2, 3, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, 7, 8, 9, 10}

A convenient thing to remember is that if your elements are not sequential it is still easy to set up:

(a[[#]] = {1, 2, 3}) & /@ {1, 3, 10};

In[2]:= a

Out[2]= {{1, 2, 3}, 2, {1, 2, 3}, 4, 5, 6, 7, 8, 9, {1, 2, 3}}
$\endgroup$
  • 2
    $\begingroup$ but this doesn't automate keeping the behaviour of Set consistent as the question asks (although it does do what is desired). then again neither does any other answer, nor anything I can come up with... $\endgroup$ – acl Mar 23 '12 at 1:09
  • $\begingroup$ The in-place modification aspect of this answer works best for my actual problem. But as acl says, the more general question remains. $\endgroup$ – wxffles Mar 23 '12 at 3:12
11
$\begingroup$

Perhaps

a = Range[10]

a[[4 ;; 6]] = Sequence@{1, 2, 3};

Returns

{1, 2, 3, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, 7, 8, 9, 10}

However, as Mr.Wizard noted in a comment, this is a LIIIEEE. If you do Information[a] you see that the Sequence is actually being saved. I find this weird, but anyway, can be solved with an extra

a=a

Also, forcing the lhs and rhs lists to be a different size should work a[[{4,5,6,4}]]={1, 2, 3}

$\endgroup$
  • 4
    $\begingroup$ This actually doesn't work quite seamlessly. Try Information[a]. $\endgroup$ – Mr.Wizard Mar 22 '12 at 23:47
  • $\begingroup$ Oooooops, thanks $\endgroup$ – Rojo Mar 22 '12 at 23:48
  • $\begingroup$ @Mr.Wizard, it's weird that essentially with Set this returns a delayed assignment. Wasn't expecting that, good catch $\endgroup$ – Rojo Mar 22 '12 at 23:52
  • $\begingroup$ take another look. If it doesn't work, delete again. :^) $\endgroup$ – Mr.Wizard Mar 22 '12 at 23:53
  • 1
    $\begingroup$ +1 as this is the only answer in the spirit of the question (or anyway what I took to be the spirit of the question) $\endgroup$ – acl Mar 23 '12 at 1:09
7
$\begingroup$

Here's a way that's in two-steps, but works nevertheless:

a[[4 ;; 6]] = \[FormalF][1, 2, 3];
a = a /. \[FormalF] -> List
Out[1]= {1, 2, 3, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, 7, 8, 9, 10}

Here's another solution using MapAt, but this loses the convenience of indexing with Span.

a = MapAt[{1, 2, 3} &, a, Transpose[{Range[4, 6]}]]
Out[2]= {1, 2, 3, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, 7, 8, 9, 10}

But the standard way to replace elements in a safe manner and for cases where you want different replacements for each, is to use ReplacePart as:

a = ReplacePart[a, Transpose[{Range[4, 6]}] -> {1, 2, 3}]
Out[3]= {1, 2, 3, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, 7, 8, 9, 10}
$\endgroup$
5
$\begingroup$

After reading the answers, this is one way more to do it (maybe too simple):

a[[4 ;; 6]] = ConstantArray[{1, 2, 3}, 3];

Thinking in it, you can do your own simple Set:

Attributes[simpleSet] = {HoldFirst};
simpleSet[lhs_, rhs_] := 
  With[{newrhs = ConstantArray[rhs, Length[lhs]]}, lhs = newrhs];

And use it as Set:

simpleSet[a[[4 ;; 6]], {1, 2, 3}];
{{1, 2, 3}, {1, 2, 3}, {1, 2, 3}}

And it returns the same as Set returns: what it is assigned.

a
{1, 2, 3, {1, 2, 3}, {1, 2, 3}, {1, 2, 3}, 7, 8, 9, 10}
$\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.