76
$\begingroup$

Say I have a list x={2,4,6,8,10} and I want to find out the positions of the elements that are greater than 7.

Select[x, #>7&] gives the elements themselves, and Position[x,8] gives the position of the elements satisfying one of the possible criteria, but what I am looking for would be a mix of the two returning {4,5}.

Any suggestions?

$\endgroup$
5
  • 2
    $\begingroup$ Using ogerard's processing idea, one can use Pick to enhance performance: x = {2, 4, 6, 8, 10}; Pick[Range[Length[x]], Sign[x - 6], 1]. $\endgroup$ Commented Jan 18, 2012 at 20:46
  • 2
    $\begingroup$ I've noticed a preference for people below to use PatternTest rather than Condition. Are any performance differences or is it just what people are used to? $\endgroup$ Commented Jan 18, 2012 at 22:15
  • $\begingroup$ I prefer Condition for ease of naming elements to work with them. $\endgroup$ Commented Jan 19, 2012 at 1:05
  • 4
    $\begingroup$ This question is a good example of where some effort on WR's part to give useful simple examples in their documentation would make every's life a lot easier. This SHOULD be easy to find in the docs, but it's not. $\endgroup$ Commented May 7, 2016 at 6:13
  • $\begingroup$ just a point-free style {2, 4, 6, 8, 10} // Position[_?(GreaterThan[7])] $\endgroup$ Commented Oct 13, 2023 at 13:02

12 Answers 12

82
$\begingroup$

Position[{2, 4, 6, 8, 10}, _?(# > 7 &)] does the job. Apply Flatten[] if need be.


As noted by Dan in a comment alluding to Brett's answer, using the level-specification argument of Position[] might sometimes be needed, if the numbers are not Reals, Rationals or Integers.

$\endgroup$
4
  • 5
    $\begingroup$ You can also preprocess the list, like this Position[{2, 4, 6, 8, 10} - 6, _?Positive] $\endgroup$ Commented Jan 18, 2012 at 18:15
  • $\begingroup$ @ogerard, post it as an answer. $\endgroup$ Commented Jan 18, 2012 at 18:18
  • 1
    $\begingroup$ This answer is not correct. Just in case people get this far and don't scroll down, check @brett champion's answer below first. $\endgroup$ Commented Apr 11, 2014 at 22:56
  • $\begingroup$ Thank you @Dan, I've already edited it. $\endgroup$ Commented Aug 27, 2017 at 13:39
26
$\begingroup$

As many have pointed out, Position can be used for this, but you may want to take care since Position will quite happily go into things you might not expect it to.

In[17]:= Position[Sin[{2, 4, 6, 8, 10}], _?(# > 0.5 &)] Out[17]= {{1, 1}, {1}, {2, 1}, {3, 1}, {4, 1}, {4}, {5, 1}} 

Note that we got match from the 4 in Sin[4] being bigger than 0.5, even though numerically Sin[4] around -0.75. We can use a level specification to limit the depth at which we're inspecting elements:

In[18]:= Position[Sin[{2, 4, 6, 8, 10}], _?(# > 0.5 &), 1] Out[18]= {{1}, {4}} 
$\endgroup$
1
  • $\begingroup$ Very nice. Thank you. $\endgroup$ Commented Jun 1, 2020 at 0:39
23
$\begingroup$

Position can take very generic patterns. Here we ask for the position of values in the list given that they are larger than 7.

x={2,4,6,8,10}; In[11]:= Position[x,val_/;val>7] Out[11]= {{4},{5}} 
$\endgroup$
18
$\begingroup$

A couple of options come to mind, but I think Position can be manipulated into giving you what you want. The key is to use a PatternTest, as follows

Position[x, _?(#>7&)] // Flatten 

returns

{4, 5}. 

For another variant, MapIndexed can be used directly

MapIndexed[If[#1 > 7, #2, Unevaluated[Sequence[]]] &, {2, 4, 6, 8, 10}] 

which relies on the properties of an empty Sequence in a List. The Unevaluated is necessary to unsure that you don't get Null in the list.

$\endgroup$
1
  • 2
    $\begingroup$ Nice trick with Unevaluated[Sequence[]] to have the kernel contract the list ! $\endgroup$ Commented Jan 18, 2012 at 22:50
12
$\begingroup$

For really big list or a rectangular matrix I would suggest the following approaches:

list = RandomInteger[20, 1000000]; f1[list_,num_] := SparseArray[UnitStep[list-(num + 1)]]["NonzeroPositions"]; f2[list_,num_] := Position[UnitStep[list-(num + 1)], 1]; 

speed comparison relative to accepted answer:

r1 = f1[list,7];//AbsoluteTiming; (* {0.0274773, Null} *) r2 = f2[list,7];//AbsoluteTiming (* {0.201376, Null}; *) r3 = Position[list, _?(# > 7 &)]; // AbsoluteTiming (* accepted answer *) (* {0.73195, Null} *) r1 === r2 === r3 (* True *) 

f1 is ~ 36 times faster than the combination of Position and PatternTest

$\endgroup$
10
$\begingroup$

Position is certainly the best for the job, but for the sake of completeness, you can do this in a way similar to Select:

DeleteCases[MapIndexed[If[#1 > 7, #2] &, {2, 4, 6, 8, 10}], Null]

While inefficient when you select only on content, this form allows to mix criteria on the content and position.

$\endgroup$
8
$\begingroup$

As others have said, Position does the job:

lst = {2, 4, 9, 6, 8, 10}; posns=Position[lst, _?(# > 7 &)] (* {{3}, {5}, {6}} *) 

This is now in the appropriate form for Extract:

Extract[lst, posns] (* {9, 8, 10} *) 
$\endgroup$
7
$\begingroup$
PositionIndex[UnitStep[x - 7]][1] 

is another possibility

$\endgroup$
2
  • $\begingroup$ you just reminded me that i had forgotten about PositionIndex :) +1 ! $\endgroup$ Commented Aug 9, 2017 at 19:33
  • 1
    $\begingroup$ Introduced in version 10. $\endgroup$ Commented Jan 2, 2024 at 7:04
5
$\begingroup$

Here's another, more general (but probably less performant) solution based on PositionIndex:

Catenate@KeySelect[PositionIndex[{2, 4, 6, 8, 10}], # > 7 &] (* {4, 5} *) 
$\endgroup$
3
$\begingroup$

Version 13.2 introduced PositionLargest

list = {2, 4, 6, 8, 10} PositionLargest[list, Length @ Select[# > 7 &] @ list] 

{{5}, {4}}

Compare to:

ReverseSort @ Position[list, _?(# > 7 &)] 

{{5}, {4}}

To see the result in descending order might be desirable or not.

$\endgroup$
2
$\begingroup$

In version 14.2, Select was updated so that it can also return the indices of elements:

Select[{2, 4, 6, 8, 10}, # > 7 & -> "Index"] (* {4, 5} *) 
$\endgroup$
2
  • $\begingroup$ has 14.2 released in your location or is this cloud? $\endgroup$ Commented Jan 13 at 0:10
  • $\begingroup$ @ubpdqn, not yet, only Cloud. $\endgroup$ Commented Jan 13 at 7:43
0
$\begingroup$

Using ReplaceList:

ReplaceList[{2, 4, 6, 8, 10}, {a___, b_ /; b > 7, ___} :> {Length@{a} + 1}] 

{{4}, {5}}

$\endgroup$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.