9

What is the most efficient method for deleting a row of empty list elements? For example, here is a list with one row of empty list elements:

list = {{
    {1, 2, 3},
    {0, 0, 0},
    {4, 5, 6}
    },
   {{}, {}, {}},
   {
    {1, 2, 3},
    {0, 0, 0},
    {4, 5, 6}
    }};

I want to get rid of the whole of row 2. Using DeleteCases[]:

DeleteCases[list, x_ /; x == {}, Infinity]

...which returns:

{{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}, **{}**, {{1, 2, 3}, {0, 0, 0}, {4, 5, 
   6}}}

How can I efficiently delete the 2nd row without using DeleteCases[] twice?

Athanassios
  • 1,291
  • 10
  • 22
awyr_agored
  • 534
  • 3
  • 15

4 Answers4

10

Here is a very simple and concise construct for doing what you ask.

a = 
  {{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}, 
   {{}, {}, {}}, 
   {{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}};
a //. {} -> Nothing
{{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}, 
 {{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}}

It has the advantage of working for { } appearing at any level.

b = 
  {{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}, 
   {{}, {}, {}}, 
   {{}, {{{}, {{}}, {}}}, {}}};
b //. {} -> Nothing
{{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}}

Note that I used the very useful new symbol Nothing. This means the above solution only works for V10.2 or later.

m_goldberg
  • 107,779
  • 16
  • 103
  • 257
  • 1
    Since OP mentions "efficient", I think this will be considerably slower than DeleteCases on large lists... – ciao Sep 21 '15 at 07:30
  • @ciao I am actually having trouble beating by a large margin. DeleteCases by itself doesn't get everything in one pass. – Mr.Wizard Sep 21 '15 at 08:06
  • @Mr.Wizard: Perhaps I've misread the question - can you give an example? The OP says nothing about "arbitrarily nested..." – ciao Sep 21 '15 at 08:17
  • @ciao I mean for the case of arbitrary nesting; using m_goldberg's b if we do DeleteCases[b, {{} ...}, {0, -1}] we still end up with {{{}}} in the output. – Mr.Wizard Sep 21 '15 at 08:19
  • @Mr.Wizard: Ah, well unless Nothing is way faster than Sequence, FixedPoint[DeleteCases[#, {{} ..}, -1] &, list] is faster on the loungebook on v9 for big lists with arbitrary nesting of empties. – ciao Sep 21 '15 at 08:25
  • Hi, Thanks. I missused the term 'efficient'. I should have said if I could solve the problem without having to nest the DeleteCases function. – awyr_agored Sep 22 '15 at 07:07
10

For arbitrarily nested lists one could also use MapAll and the operator form of DeleteCases:

b = (* m_goldberg's example *)
  {{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}, 
   {{}, {}, {}}, 
   {{}, {{{}, {{}}, {}}}, {}}};

DeleteCases[{}] //@ b
{{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}}

It works via the bottom-up standard evaluation order.

This however is somewhat slower than //. in a single test. Using an anonymous function instead of the operator form is a little faster than //. however in the same test, though less clean:

DeleteCases[#, {}] & //@ b
{{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
9

I don't have the same interpretation of OP requirements as others seem to, but here's my take on those interpretations:

DeleteCases[#, Nest[{# ...} &, {}, Depth[#]], Infinity] &@b

Where b is of course the target list.

Unless Nothing performs much faster than the use of Sequence[], this seems to do quite well against what appears to be the fastest "get rid of any empty list anywhere" solution.

Edit: I used this to generate test b:

b = 
 RandomChoice[
  {9, 1, 1, 1, 9} -> 
   {{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}, 
    {{}, {}, {}}, {{}, {{{}, {{}}, {}}}, {}}, {{}}, 
    {{1, 2, 3}, {}, {{{{{{{{{{{{1, 2, 3, {{{}, {1, 2, 3}}}}}}}}}}}}}}},
     {1,2, 3, {}}}}, 1000000];
xyz
  • 605
  • 4
  • 38
  • 117
ciao
  • 25,774
  • 2
  • 58
  • 139
  • Nice example. I spent a little time figuring out how this works. Would I be correct in saying that in this case, the condition in the DeleteCases function is the a representation of the empty list structure? Doing this causes the DeleteFunction to delete empty lists in one go without mapping or nesting DeleteCases? Thanks to all who gave their time to help me. – awyr_agored Sep 22 '15 at 07:18
  • 1
    @awyr_agored: Yes, though what "one go" means under the covers can vary. In any case, it handily outperformed in my tests, glad you find in useful and interesting. – ciao Sep 22 '15 at 07:24
4

This one performs nicely also:

f[{}]=Sequence[];
f[x_]:=x;

f //@ {{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}, {{}, {}, {}}, {{}, {{{}, {{}}, {}}}, {}}};

{{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}}

Here are some interesting relative timings for the different solutions given in this post (1.00 is the best and reference time):

enter image description here

(the test list b is Ciao's random list with parameter n as the given "size":)

SeedRandom[299];
b = RandomChoice[
  {9, 1, 1, 1, 9} -> 
   {{{1, 2, 3}, {0, 0, 0}, {4, 5, 6}}, 
    {{}, {}, {}}, {{}, {{{}, {{}}, {}}}, {}}, {{}}, 
    {{1, 2, 3}, {}, {{{{{{{{{{{{1, 2, 3, {{{}, {1, 2, 3}}}}}}}}}}}}}}},
     {1,2, 3, {}}}}, n];
SquareOne
  • 7,575
  • 1
  • 15
  • 34