Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficient way to remove empty lists from lists without evaluating held expressions?

In previous thread an efficient way to remove empty lists ({}) from lists was suggested:

Replace[expr, x_List :> DeleteCases[x, {}], {0, Infinity}]

Using the Trott-Strzebonski in-place evaluation technique this method can be generalized for working also with held expressions:

f1[expr_] := 
 Replace[expr, 
  x_List :> With[{eval = DeleteCases[x, {}]}, eval /; True], {0, Infinity}]

This solution is more efficient than the one based on ReplaceRepeated:

f2[expr_] := expr //. {left___, {}, right___} :> {left, right}

But it has one disadvantage: it evaluates held expressions if they are wrapped by List:

In[20]:= f1[Hold[{{}, 1 + 1}]]

Out[20]= Hold[{2}]

So my question is: what is the most efficient way to remove all empty lists ({}) from lists without evaluating held expressions? The empty List[] object should be removed only if it is an element of another List itself.


Here are some timings:

In[76]:= expr = Tuples[Tuples[{{}, {}}, 3], 4];
First@Timing[#[expr]] & /@ {f1, f2, f3}
pl = Plot3D[Sin[x y], {x, 0, Pi}, {y, 0, Pi}]; 
First@Timing[#[pl]] & /@ {f1, f2, f3}

Out[77]= {0.581, 0.901, 5.027}

Out[78]= {0.12, 0.21, 0.18}

Definitions:

Clear[f1, f2, f3];
f3[expr_] := 
  FixedPoint[
   Function[e, Replace[e, {a___, {}, b___} :> {a, b}, {0, Infinity}]], expr];
f1[expr_] := 
  Replace[expr, 
   x_List :> With[{eval = DeleteCases[x, {}]}, eval /; True], {0, Infinity}];
f2[expr_] := expr //. {left___, {}, right___} :> {left, right};
like image 946
Alexey Popkov Avatar asked Jul 10 '11 13:07

Alexey Popkov


People also ask

How do you remove an empty list from a list in Python?

Short answer: You can remove all empty lists from a list of lists by using the list comprehension statement [x for x in list if x] to filter the list.

How do you ignore an empty list in Python?

Using filter() method Just filter out the None and empty element form list. If None is used as the first argument to filter() , it filters out every value in the given list, which is False in a boolean context. This includes empty lists.


2 Answers

How about:

Clear[f3];
f3[expr_] := 
 FixedPoint[
  Function[e, 
   Replace[e, {a___, {}, b___} :> {a, b}, {0, Infinity}]],
   expr]

It seems to live up to the specs:

In[275]:= f3[{a, {}, {b, {}}, c[d, {}]}]

Out[275]= {a, {b}, c[d, {}]}

In[276]:= f3[Hold[{{}, 1 + 1, {}}]]

Out[276]= Hold[{1 + 1}]
like image 114
Sasha Avatar answered Sep 26 '22 01:09

Sasha


You can combine the solutions you mentioned with a minimal performance hit and maintain the code unevaluated by using a technique from this post, with a modification that the custom holding wrapper will be made private by using Module:

ClearAll[removeEmptyListsHeld];
removeEmptyListsHeld[expr_Hold] :=
  Module[{myHold},
     SetAttributes[myHold, HoldAllComplete];
     Replace[MapAll[myHold, expr, Heads -> True],
        x : myHold[List][___] :> 
           With[{eval = DeleteCases[x, myHold[myHold[List][]]]}, 
             eval /; True], 
       {0, Infinity}]//. myHold[x_] :> x];

The above function assumes that the input expression is wrapped in Hold. Examples:

In[53]:= expr = Tuples[Tuples[{{}, {}}, 3], 4];
First@Timing[#[expr]] & /@ {f1, f2, f3, removeEmptyListsHeld[Hold[#]] &}

Out[54]= {0.235, 0.218, 1.75, 0.328}


In[56]:= removeEmptyListsHeld[Hold[{{},1+1,{}}]]
Out[56]= Hold[{1+1}]
like image 29
Leonid Shifrin Avatar answered Sep 25 '22 01:09

Leonid Shifrin