12

I need to use If inside of a Table loop, e.g. Table[If[i< 3, i], {i, 5}] will give {1, 2, Null, Null, Null}

But I want the result to be {1,2}.

Any fix for this?

EDIT:
What if we consider Table[If[i< 3, f[i]], {i, 5}] which gives {f[1], f[2], Null, Null, Null}

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Osiris Xu
  • 713
  • 1
  • 5
  • 13
  • Somewhat related question: http://stackoverflow.com/q/6313505/618728 – Mr.Wizard Jan 16 '12 at 10:16
  • Related question: http://mathematica.stackexchange.com/questions/3700/how-to-avoid-returning-a-null-if-there-is-no-else-condition-in-an-if-contruct – anderstood Aug 10 '16 at 18:42

5 Answers5

33

Concisely:

Table[If[i < 3, i, ## &[]], {i, 5}]

This works because the function ## & does not immediately evaluate.

## & is a "vanishing" function.

{1, 2, ## &[], 3, 4}
----> {1, 2, 3, 4}

See SlotSequence for more information.

Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • 1
    @Leonid thanks for showing or reminding me of that some months ago. – Mr.Wizard Jan 16 '12 at 10:18
  • 3
    I think this was your invention. I normally use `Sequence@@{}`, which is not as elegant (and may also be a tiny bit slower). – Leonid Shifrin Jan 16 '12 at 10:21
  • @Leonid; I believe that's correct, however I had been using `Unevaluated@Sequence[]` inside `If` before that. (ps do you know how to search comments, apart from Google? I'd like to find that exchange.) – Mr.Wizard Jan 16 '12 at 10:23
15

If you need to remove it from an existing list, you can use

DeleteCases[list, Null]

or

list /. Null -> Sequence[]

(a bit more advanced).


Regarding your Table example above, first note that the second comma in If is unnecessary (and is even highlighted in pink):

list = Table[If[i < 3, i], {i, 5}]

To filter the table elements by a condition, you might want to use something similar to

list = Select[Table[i, {i, 5}], # < 3 &]

instead.


Finally, if you need to generate the list without ever adding rejected elements to it (to save memory), I suggest using Reap and Sow:

Reap@Do[If[i < 3, Sow[i]], {i, 5}]
list = %[[2, 1]]

I haven't actually verified the memory usage of this compared to a plain Table, and note that if you generate only numbers, which can be stored in a packed array, the Table construct may be more memory efficient. On the other hand if you generate a truly huge number of generic expressions, the majority of which will be rejected in If, Sow / Reap may be better.

Szabolcs
  • 24,728
  • 9
  • 85
  • 174
  • +1. I actually implemented your last suggestion with `Reap-Sow` in full generality in the answer I linked to, in my answer (just may be syntax for condition could be friendlier). – Leonid Shifrin Jan 16 '12 at 08:51
6

As an alternative, you may use the variation of Table from this answer, which has been designed specifically for conditional table-building. Here is how it will look:

In[12]:= tableGenAltMD[i,{i,5},#<3&]
Out[12]= {1,2}

The last argument is a function representing the condition. It actually would be nice to also have syntax where one could use i (and / or other iterator variables) directly, and such a syntax is probably not difficult to add.

Community
  • 1
  • 1
Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
  • Please consider illustrating why this may be preferred to the simplistic answer I gave below. – Mr.Wizard Jan 16 '12 at 09:30
  • 1
    @Mr. Wizard For a very simple reason: this is a general function (my version). It handles multi-dimensional case as well, with a uniform syntax. It is still easier to read, and would be even easier when I add syntax to replace function by an expression for a condition. Should it have been a built-in function, or just simply WRI - approved one, there would be no question about using anything else. Imagine that you'd not have say `Cases`, and someone gives you one (implemented on the top-level). Would you not use it (if implementation is ok)? A lot of it is just about habits, conventions, etc. – Leonid Shifrin Jan 16 '12 at 09:56
  • Perhaps then you should include that in your answer? ;-) – Mr.Wizard Jan 16 '12 at 10:04
  • @Mr.Wizard Don't think so :). There is enough information in it so that those who need it will find it. – Leonid Shifrin Jan 16 '12 at 10:06
  • Unless you are going to be obstinate and delete your preceding comment, at least it is now on this page. +1 – Mr.Wizard Jan 16 '12 at 10:09
  • @Mr.Wizard I will keep the comment :). Thanks for the upvote. – Leonid Shifrin Jan 16 '12 at 10:12
4

In the previous anwser, the part ## &[] can be replaced by the built-in symbol Nothing

Table[If[i < 3, i, Nothing], {i, 5}]

gives

{1, 2}
datahaki
  • 600
  • 7
  • 23
3

If you use Sequence[] instead of Null, then you could do

Table[If[i < 3, i, Hold[Sequence[]]] // ReleaseHold, {i, 5}]

I wished for a long time that If would have Attribute SequenceHold. I think I once suggested this to WRI, but there are probably (good?) reasons for If to not hat this attribute. One can try, if one dares to change built-in Symbols (which one should probably not do):

Unprotect[If];
SetAttributes[If, SequenceHold];

Then Sequence[] in If would just work:

Table[If[i < 3, i, Sequence[]], {i, 5}]
Rolf Mertig
  • 1,360
  • 8
  • 22
  • Rolf, please see the answer I just added. I almost edited your question instead of posting it, but I realized that I would essentially replace your answer with mine, so I did not. – Mr.Wizard Jan 16 '12 at 09:28
  • Mr. Wizard: very nice! I did not know about ##&@[] being a Sequence[] equivalent. – Rolf Mertig Jan 17 '12 at 08:31