2
$\begingroup$

I'm having trouble understanding the difference between Cases and Position.

What I want to do is to identify the elements of a (twice) nested list for which the first list contained in it itself contains a particular integer. The following code correctly identifies these elements using "Cases"

snap = {{{1, 2, 3}, {a, b, c}}, {{2, 3, 4}, {d, e, f}}};

Cases[snap, x_ /; MemberQ[x[[1]], 1]]

This correctly returns: {{{1, 2, 3}, {a, b, c}}}

I would have expected the following code to do essentially the same thing but to return the positions of these elements within "snap" rather than the elements themselves.

Position[snap, x_ /; MemberQ[x[[1]], 1]]

However this generates an error (multiple times before stopping):

"Part specification List[[1]] is longer than depth of object."

Can someone explain why Position doesn't behave just like Cases? And is there an easy way to get the positions of the elements that match my pattern?

Thank you!

$\endgroup$

marked as duplicate by Mr.Wizard list-manipulation Feb 15 '18 at 15:07

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 2
    $\begingroup$ They have different default values of Heads option and default levelspec. $\endgroup$ – Kuba Feb 15 '18 at 12:59
4
$\begingroup$

The main difference between Cases and Position is obviously that the first one gives the matched expression, while the latter one gives the position of the expression. However, I'm sure you knew this. Kuba pointed out that the default level-spec differs as well. I want to point to something different.

I don't like your approach very much. Cases and Position take patterns and what you did is to use a pattern that matches everything and then you place a condition on the pattern. More severely, you use a condition that uses [[1]] which only works, if the match that is tested indeed has a first element!

This is not the case when the heads of your expressions (the heads are the symbol List) are tested. Therefore, to fix your call, you would need to do

Position[snap, x_ /; MemberQ[x[[1]], 1], {1}, Heads -> False]
(* {{1}} *)

If you have simple condition or test-function, you should rather use Select which is more appropriate

Select[snap, MemberQ[First[#], 1] &]
(* {{{1, 2, 3}, {a, b, c}}} *)

Or, we ignore the runtime for the moment, you can easily convert your test function into a real pattern:

Cases[snap, {{___, 1, ___}, _}]
(* {{{1, 2, 3}, {a, b, c}}} *)

Position[snap, {{___, 1, ___}, _}]
(* {{1}} *)
$\endgroup$
  • $\begingroup$ Awesome! Thanks for taking the time to answer my question! $\endgroup$ – Christoph Feb 15 '18 at 14:00
  • $\begingroup$ As a follow-up (I hope I am not getting on your nerves with my lack of understanding): Is there a way to use "Position" in a way similar to your use of "Select"? I.e. how do I, in general, find the Position of elements that satisfy a certain condition. For example, suppose from a list of integers I want to find the positions of elements greater than 2: $\endgroup$ – Christoph Feb 15 '18 at 14:09
  • $\begingroup$ Position[{1, 2, 3}, # > 2 &] $\endgroup$ – Christoph Feb 15 '18 at 14:10
  • $\begingroup$ That doesn't work. My inelegant code does: Position[{1, 2, 3}, x_ /; x > 2] $\endgroup$ – Christoph Feb 15 '18 at 14:10
  • $\begingroup$ Your code works for me, giving {{3}} as output which is correct. Again, you have the problem that x_ catches every expression. Make the pattern specific! Like this: Position[RandomInteger[10, {20}], x_Integer /; x > 2] $\endgroup$ – halirutan Feb 15 '18 at 14:18

Not the answer you're looking for? Browse other questions tagged or ask your own question.