# Comparing Positions of Elements in a List

How can I check if Position[i] > Position[j] for two arbitrary elements in a given list?

The problem is that Position[i] returns {{k}} instead of just k, and I don't know how to compare {{k}} and {{l}}.

• say X contains a position. X[[1,1]] will be the scalar value. You can also goof it with things like Max[X], etc. that will strip the braces.
– ciao
Apr 29, 2014 at 4:16

Here's a simple example re: my comment:

afterQ[list_, ele1_, ele2_] := Max[Position[list, ele1]] > Max[Position[list, ele2]]

list = Range[10];
afterQ[list, 3, 4]
afterQ[list, 4, 3]
afterQ[list, 4, 99]
afterQ[list, 99, 4]

(*
False
True
True
False
*)


So, False if element 1 is not after element 2, True if it is, True if element 1 exists in the list and element 2 does not, False if element 1 is not in the list at all.

You'll want to think about ambiguities here: what if an element is both before and after? Or what if one does not even exist in the list? The above treats the last positions as the arbiter, you may want to do things differently.

I think Simon's method is definitely the way to go provided:

• You are only concerned with first appearances
• You know that both elements are in fact present
• You will not be scanning the list many times

If you are not certain that both elements are present you should scan the list for both of them. You can limit the number of indexes returned by Position using its fourth parameter:

list = Characters @ "xdslkridiatjxzyoedem";

Position[list, #, 1, 1] & /@ {"i", "j"}

{{{7}}, {{12}}}


From this uniform expression you have many options for making a comparison, but the most direct is likely OrderereQ:

OrderedQ @ {{{7}}, {{12}}}

OrderedQ @ {{{12}}, {{7}}}

True

False


Perhaps you wish to allow for the possibility that one or both of the elements are missing from the list. You could check like this:

f1 = If[# ~MatchQ~ {{{_}} ..}, OrderedQ @ #, $Failed] &; f1 @ {{{7}}, {{12}}} f1 @ {{{12}}, {{7}}} f1 @ {{}, {{12}}}  True False$Failed


I wrote this function to allow a generalization to more than two elements. Combining it with the Position call:

elemOrderedQ[a_List, elem__] :=
Position[a, #, 1, 1] & /@ {elem} //
If[# ~MatchQ~ {{{_}} ..}, OrderedQ @ #, $Failed] &  Now: elemOrderedQ[list, "d", "s", "t"] elemOrderedQ[list, "x", "y", "z"] elemOrderedQ[list, "s", "t", "u"]  True False$Failed


## Look-up tables (rule lists)

If you are going to be using this operation on the same list many times (with different elements) you should build a look-up table of elements and positions.

Single positions:

rls = Dispatch @ Thread[# -> Range@Length@#] & @ list

 Dispatch[{x->1,d->2,s->3,l->4,k->5,r->6,i->7,d->8,i->9,a->10,t->11,j->12,x->13,z->14,
y->15,o->16,e->17,d->18,e->19,m->20},-DispatchTables-]


(If there are many duplicates the rule list should be filtered or building the Dispatch table will be slow. That can be done with e.g. GatherBy[rules, First][[All, 1]] where rules is the raw Rules list. Application of the rules will be fast either way.)

Now making comparisons of (first) positions in this list is as simple as this:

"i" > "j" /. rls
"i" > "x" /. rls

False

True


It is also very fast. If you have need to compare all positions you can do this:

rls2 = Dispatch @ Reap[MapIndexed[Sow[#2[[1]], #] &, list];, _, Rule][[2]]

Dispatch[{x->{1,13},d->{2,8,18},s->{3},l->{4},k->{5},r->{6},i->{7,9},a->{10},t->{11},
j->{12},z->{14},y->{15},o->{16},e->{17,19},m->{20}},-DispatchTables-]


With this you can easily pose queries such as:
Are all appearances of "x" before the first appearance of "e"?

Last["x"] < First["e"] /. rls2 // Quiet

True


I used Quiet here to suppress messages from e.g. Last["x"]. I could also use Unevaluated:

Unevaluated[Last["x"] < First["e"]] /. rls2

True


An alternative to Position is to scan through the list with Cases and see if j appears first:

jbeforei[list_, i_, j_] := Cases[list, i | j, 1, 1] == {j}

jbeforei[Range[10], 8, 5]
jbeforei[Range[10], 2, 5]

(*
True
False
*)

• Lovely method, assuming both elements are present. What is your reason for using a -1 levelspec? Apr 30, 2014 at 1:31
• @Mr.Wizard, no good reason - just habit and tiredness! Apr 30, 2014 at 8:37

Ordering can tell you the position of the smallest and largest values and be used to sort list, e.g.

list = RandomSample[Range[10], 10]


Example:

list={7, 4, 10, 8, 6, 5, 1, 9, 3, 2}


then

ord=Ordering@list


produces:

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


i.e. the smallest element of list is at positon 7 and larges at position 3.

You can sort (in addition to Sort) using:

list[[ord]]


You can get the position of the smallest 3 elements:

Ordering[list, 3]


or largest 3 elements:

Ordering[list, -3]


Using Mr. Wizard's data:

list = Characters @ "xdslkridiatjxzyoedem";


we can use PositionIndex to our advantage

pin = PositionIndex[list][[All, 1]]

"i" > "j" /. pin


False

"i" > "x" /. pin


True