2

(Mathematica version: 8.0.4)

Given

lst = {{{{1, 2}, 3}, {{4, 5}, 6}}, {{{7, 8, 9, 10, 11}, 13}}};
lst2 = DeleteCases[lst, {x_, y_} /; y > 6, {2}]

gives

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

Note the extra empty {} at the end.

I could not find a way to remove it in the same command using DeleteCases (which I think the right command to use for this), so I had to apply it again on the result

lst2 = DeleteCases[lst2, {}]

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

question: Is there a trick to do the above in one command without getting the empty {} in the result? so that the command is self contained for all cases?

updatet 1

response to Lou suggestion below, of adding an extra { }

Here is an example where I get different results:

lst={{{{1, 2}, 3}, {{4, 5}, 6}}, {{{7, 8, 9, 10, 11}, 13}}}

now using the method of removing empty {} by an extra application of DeleteCases, we get

lst2 = DeleteCases[lst, {x_, y_} /; y >= 6, {2}]
{{{{1, 2}, 3}}, {}}

lst2 = DeleteCases[lst2, {}]
{{{{1, 2}, 3}}}

now using the method of extra { }

lst2 = DeleteCases[lst, {{x_, y_}} /; y >= 6]
{{{{1, 2}, 3}, {{4, 5}, 6}}}

which is the not the same, I should get only {{{{1, 2}, 3}}}

thanks

Arnoud Buzing
  • 15,383
  • 3
  • 20
  • 50
Nasser
  • 12,849
  • 6
  • 52
  • 104

6 Answers6

6

There seems to be no general automatic way to remove empty lists which emerge as a result of DeleteCases or other structural transformation function, as a part of the original structural operation. Their removal must be a separate operation. This question:

efficient-way-to-remove-empty-lists-from-lists

answers how to do that efficiently after the fact

Community
  • 1
  • 1
Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
5

I think that your original solution is a good solution:

$lst = {{{{1, 2}, 3}, {{4, 5}, 6}}, {{{7, 8, 9, 10, 11}, 13}}};

DeleteCases[$lst, {x_, y_} /; y > 6, {2}] // DeleteCases[#, {}] &

It is clear and concise. An alternative would be this:

DeleteCases[$lst, {x_, y_} /; y > 6, {2}] /. {} -> Sequence[]

However, let's persevere and try to find a way to do the job with a single invocation of DeleteCases. We could add an alternative pattern that matches top-level elements that contain only rejected subpairs:

DeleteCases[
  $lst
, (a:{{_, _?NumericQ}..} /; And @@ Map[#[[2]] > 6 &, a, {1}]) |
  ({_, y_?NumericQ} /; y > 6)
, {1, 2}
]

It is inconvenient to write the threshold value (6) twice. We can avoid that:

DeleteCases[
  $lst
, 6 /. n_ :>
    (a:{{_, _?NumericQ}..} /; And @@ Map[#[[2]] > n &, a, {1}]) |
    ({_, y_?NumericQ} /; y > n)
, {1, 2}
]

Alternatively, we could define a local function that matches both top-level elements and individual subpairs:

Module[{test}
, test[elems:{{_, _?NumericQ}..}] := And @@ test /@ elems
; test[{_List, y_?NumericQ}] := y > 6
; DeleteCases[$lst, e_?test, {1, 2}]
]

While these proposals meet the stated requirement to invoke DeleteCases only once, I find them unsatisfactory. My main objection is that they are not as readable as the original solution.

WReach
  • 18,098
  • 3
  • 49
  • 93
2
lst2 = DeleteCases[DeleteCases[lst, {x_, y_} /; y > 6, {2}], {}] 
Peter O.
  • 32,158
  • 14
  • 82
  • 96
tryme
  • 81
  • 3
  • Thanks, but the above is basically what I did. I wanted to see if one can tell `DeleteCases` not to generate an empty `{}` during the process itself. – Nasser Dec 29 '11 at 09:36
1
lst2 = DeleteCases[lst, {{x_, y_}} /; y > 6]

But I suppose you want the first list to be matched to..? Perhaps:

lst2 = DeleteCases[Flatten[lst,1], {x_, y_} /; y >= 6]

it results in {{{1, 2}, 3}}

Lou
  • 322
  • 4
  • 12
  • Thanks, but your method does not work in general. Please see edit (1) showing an example case – Nasser Dec 29 '11 at 10:12
1

The other answers are all very good, and I hesitate to give this reply.

The following is just for fun, nothing more:

lst = {{{{1, 2}, 3}, {{4, 5}, 6}}, {{{7, 8, 9, 10, 11}, 13}}};

Using Cases

Cases[lst, {x_, y_} /; ! y > 6, {2}]

Cases[lst, {x_, y_} /; ! y >= 6, {2}]

giving

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

{{{1, 2}, 3}}

681234
  • 4,214
  • 2
  • 35
  • 42
0

Why not just use an OR clause in DeleteCases and get delete based on the pattern OR a {}

DeleteCases[lst, {x_, y_} /; y > 6 | {}, {2} ]

On my machine that returns the result:

{{{{1, 2}, 3}, {{4, 5}, 6}}, {{{7, 8, 9, 10, 11}, 13}}}

...which is what I think you wanted.

  • thanks, but the answer should come out to be `{{{{1, 2}, 3}, {{4, 5}, 6}}}` your method did nothing to the list. – Nasser Dec 29 '11 at 23:53