Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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}}.

share|improve this question
1  
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. –  rasher Apr 29 at 4:16
add comment

4 Answers 4

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.

share|improve this answer
add comment

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
share|improve this answer
add comment

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
*)
share|improve this answer
    
Lovely method, assuming both elements are present. What is your reason for using a -1 levelspec? –  Mr.Wizard Apr 30 at 1:31
    
@Mr.Wizard, no good reason - just habit and tiredness! –  Simon Woods Apr 30 at 8:37
add comment

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]
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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