7
$\begingroup$

I would like to use Nothing[] to make some elements of a list invisible. (The intended use is to remove a few unwanted points from a ListPlot.) But sometimes it seems that the operation fails:

x = {a, b, c, d, e} (* Create a list *)
(*{a, b, c, d, e}*)

x[[3]] = Nothing (* Remove an element *)
(*Nothing*)

x
(*{a, b, d, e} (* Third element successfully removed. *)*)

x[[3]]
(*d   (* The third element is now 'd' instead of 'c' *)*)

x[[1]] = Nothing (* Remove the first element *)
(*Nothing*)

x
(*{b, d, e} (* The first element removed successfully. *)*)

x[[3]] = Nothing (* Remove the third element *)
(*Nothing*)

x
(*{b, d, e} (* List is unchanged! *)*)

x[[3]]
(*e*)

Of course, assigning Nothing[] does not actually 'delete' anything.

Definition[x]
(*x = {Nothing, b, Nothing, d, e}*)

The documentation says "Nothing[] is removed during the standard evaluation process." This then makes me believe that assigning Nothing[] does not involve the standard evaluation process.

This is rather annoying, because now the indexes used by standard evaluation and the Nothing[] assignment do not match.

Position[x, d]
(*{{2}} (* Yes, the element 'd' is in the list *)*)

x[[Position[x, d] // First]] = Nothing[] (* Try to remove element 'd' *)
(*Nothing*)

x
(*{d, e} (* The element 'd' was not removed! *)*)

x[[1]] = z; x
(*{z, d, e} (* This is just weird *)*)

I guess I'm posting this more as a warning than as a question. If there are suggestions on how to do it differently, or if I'd missed something in the documentation that explains this behaviour I'd be most interested.

My next step would be to collect the indices of the elements I want to disappear and assign Nothing[] to them in a singe step, rather than trying to remove them piecemeal.

$\endgroup$
7
  • 5
    $\begingroup$ If you want to instantly remove something use e.g. Delete or Drop. $\endgroup$
    – SHuisman
    Commented Mar 4, 2020 at 13:50
  • 2
    $\begingroup$ The question could be rephrased from "a warning about this weirdness" to "why is this behaving weirdly?" $\endgroup$
    – Jason B.
    Commented Mar 4, 2020 at 15:35
  • $\begingroup$ x[[1]] = Nothing changes the first element of x to be Nothing. It does not re-evaluate the entire contents of x and then re-assign the result to x. $\endgroup$
    – Szabolcs
    Commented Mar 4, 2020 at 16:33
  • $\begingroup$ The person who voted to reopen: it's better to explain why it should be reopened. If you make that clear, others will vote too. $\endgroup$
    – Szabolcs
    Commented Mar 4, 2020 at 19:46
  • 1
    $\begingroup$ Nothing is not a function. It is a symbol. Sequence is very different: you must use Sequence[]. It has to be used as a function. If you are using Nothing and not Nothing[] in your code, then please do not keep referring to Nothing[]—they are not the same thing. $\endgroup$
    – Szabolcs
    Commented Mar 5, 2020 at 8:49

1 Answer 1

14
$\begingroup$

I think the best way to understand this behavior is with this example,

In[99]:= x = {a, b, c, d, e};

In[101]:= Length@x

Out[101]= 5

In[102]:= x[[3]] = Nothing;

In[103]:= Length[x]

Out[103]= 4

In[104]:= Block[{Nothing}, Length[x]]

Out[104]= 5

When you say x[[3]] = Nothing, you are not deleting an element from x. x is still a 5-element list. But x is now something that will evaluate to a 4-element list.

Nothing has evaluation rules such that it becomes Sequence[] when inside a List. So when you use x it has to re-evaluate every time,

In[107]:= TracePrint[x, _List]

During evaluation of In[107]:=  {a,b,Nothing,d,e}

During evaluation of In[107]:=  {a,b,d,e}

Out[107]= {a, b, d, e}

In general I would avoid Part modification like this, where you create an expression that isn't finished evaluating.

As mentioned in the comments, to delete a part of a list use Delete. If you want to use Nothing it is necessary to add another evaluation step, like

x[[3]] = Nothing;
x = x

or

x = ReplacePart[x, 3 -> Nothing]

I find Nothing to be very useful. Often I'm compiling a list and I do

elem1 = If[condition1, val1, Nothing];
elem2 = If[condition2, val2, Nothing];
list = {elem1, elem2}

I find it easier to use than Sequence @@ {} in these cases.

$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.