7

I would like to turn a list which looks like this one:

{{-1, -1, -1}, {-1, 2, 3}, {-1, -1, 2}, {2, 3, 4}}

into one which looks like that one:

{{}, {2, 3}, {2}, {2, 3, 4}}

Is there an efficient way to do this?

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
RMMA
  • 2,710
  • 2
  • 18
  • 33

5 Answers5

14

This :

data = {{-1, -1, -1}, {-1, 2, 3}, {-1, -1, 2}, {2, 3, 4}};
DeleteCases[data, -1, Infinity]

Addressing @Sjoerd's comment, if the list is 2 dimensional one can adjust the level to 2.

data2 = {{-1, -1, -1}, {-1, 2, 3, Exp[-1]}, {-1, -1, 2}, {2, 3, 4}};
DeleteCases[data2, -1, 2]
(* {{}, {2, 3, 1/E}, {2}, {2, 3, 4}} *)
b.gates.you.know.what
  • 20,103
  • 2
  • 43
  • 84
13

Replacing -1 with an empty Sequence should do it:

lst = {{-1, -1, -1}, {-1, 2, 3}, {-1, -1, 2}, {2, 3, 4}};

(* In *)
lst /. {-1 -> Sequence[]}
(* Out *)
{{}, {2, 3}, {2}, {2, 3, 4}}

If there are expressions containing -1 that shouldn't be replaced, and your list is always of depth 2, you can use Replace instead of ReplaceAll:

lst = {{Exp[-1], -1, -1}, {-1, 2, 3}, {-1, -1, 2}, {2, 3, 4}};

Replace[lst, {-1 -> Sequence[]}, {2}]

Comparing this solution with the DeleteCases solution from b.gatessucks, it seems the DeleteCases solution is faster:

AbsoluteTiming[Do[lst /. {-1 -> Sequence[]}, {100000}]] // First
(* Out *)
0.763795

AbsoluteTiming[Do[DeleteCases[lst, -1, Infinity], {100000}]] // First
(* Out *)
0.448700

Some other alternatives based on the answer by yulinlinyu (and comments):

AbsoluteTiming[Do[Select[#, FreeQ[#, -1] &] & /@ lst, {100000}]] // First
(* Out *)
2.153360

AbsoluteTiming[Do[Cases[#, Except[-1]] & /@ lst, {100000}]] // First
(* Out *)
1.045021

If the actual lst is larger, the tests may come out differently, but I would suspect not.

Malte Lenz
  • 2,471
  • 19
  • 21
2

If your (ragged) array is entirely numeric, and your desired operation is to remove all negative values, you could use this:

Pick[#, UnitStep@#, 1] & @ {{-1, -1, -1}, {-1, 2, 3}, {-1, -1, 2}, {2, 3, 4}}
{{}, {2, 3}, {2}, {2, 3, 4}}

This should prove competitively fast as well.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
1

How about this?

Select[#, Positive] & /@ {{-1, -1, -1}, {-1, 2, 3}, {-1, -1, 2}, {2, 
   3, 4}}

Edit 1:

 According to Lenz's and Kguler's advice, the code can be

Select[#, FreeQ[#,-1]&] & /@ {{-1, -1, -1}, {-1, 2, 3}, {-1, -1, 2}, {2, 
       3, 4}}

Or

    Select[#, #!=-1&] & /@ {{-1, -1, -1}, {-1, 2, 3}, {-1, -1, 
   2}, {2, 3, 4}}

Edit 2:

 According to the op's comment below, the codes can be something like this:

 Select[#, FreeQ[#,-1]&] & /@ {{-1, -1, -1}, {-1, 2, 3}, {-1, -1, 2}, {2, 
           3, 4}}/.{}->{0,0}
yulinlinyu
  • 4,815
  • 2
  • 29
  • 36
  • This would also remove elements of other negative numbers, which the title of the question indicates is not the wanted scenario. – Malte Lenz Dec 12 '12 at 10:10
  • thank you for the nice answers. If the result of a sublist is {} it should be turned into {0,0}. How can I do this? – RMMA Dec 12 '12 at 10:14
  • You could add this as a requirement to the original question. Replacing empty lists with ReplaceAll should be a good way though. – Malte Lenz Dec 12 '12 at 10:17
  • 2
    maybe FreeQ[#, -1] & instead of Positive? – kglr Dec 12 '12 at 10:18
1

Translation of python.

Table[If[j != -1, j, ## &[]], {i, list}, {j, i}]
 [[j for j in i if j != -1] for i in list]

A recursive version:

foo[L_] := Table[If[ListQ[i], foo[i], If[i != -1, i, ## &[]]], {i, L}];
foo[list]
chyanog
  • 15,542
  • 3
  • 40
  • 78