3
$\begingroup$

I have a list of lists similar to this:

L = {{"a", "b", "c"}, {"x", "c", "y"}, {"i", "j", "h"}, {"x", "b", "z"}}

Each list within L happens to be of length 3. Suppose I need to find the position of the lists that have a particular element (say, "b") at the $n^{th}$ position. How can I do this efficiently?

Currently, I have an approach that works but I don't think it is very efficient inefficient (it creates a new list with the $n^{th}$ elements and then looks for the queried element, I'm sure there's a way to search L directly):

queriedElement = "b";

queriedPosition = 2;

occurencePositions = Flatten@Position[#[[queriedPosition]] & /@ L, queriedElement]//AbsoluteTiming

Which gives the correct answer:

{0.000035, {1, 4}}

Searching for an alternative efficient way because I need to do this in large lists.

Thanks!

$\endgroup$
  • 1
    $\begingroup$ As shown by kglr, Position[L[[All,2]], "b"] is probably close to the 'canonical' solution, and it might be of interest that if you don't Flatten, the result may be used directly with Extract to obtain the full sublists, if desired: Extract[L,%] $\endgroup$ – user1066 May 9 at 19:27
3
$\begingroup$
Flatten @ Position[L, _List?(#[[2]] === "b" &)]
{1, 4}

Making it a function:

posF1 = Flatten @ Position[#, _List?(Function[x, x[[#2]] === #3])] &;

posF1[L, 2, "b"]
 {1, 4}

Alternatively,

posF2 = Flatten @ Position[#[[All, #2]], #3] &;

posF2[L, 2, "b"]
 {1, 4}

Also

posF3 = PositionIndex[#[[All, #2]]]@#3 &;

posF3[L, 2, "b"]
 {1, 4}
| improve this answer | |
$\endgroup$
  • 3
    $\begingroup$ As you are comparing against a string, it is better to use === instead of ==: #[[2]] === "b". $\endgroup$ – J. M.'s technical difficulties May 9 at 12:50
  • $\begingroup$ Thank you @J.M. Edited to change == to ===. $\endgroup$ – kglr May 9 at 13:52
1
$\begingroup$

I tried a solution with Pick.

f[v_,p_,e_]:=Pick[Range[Length[v]], v[[All, p]], e]

where v is your L, p is your queriedPosition and e is your queriedElement.

Here is a timing test of Pick, along with the functions from @kglr.

Block[{element, position, list, posF1, posF2, posF3},
   element = "a";
   position = 1;
   posF1 = Flatten@Position[#, _List?(Function[x, x[[#2]] === #3])] &;
   posF2 = Flatten@Position[#[[All, #2]], #3] &;
   posF3 = PositionIndex[#[[All, #2]]]@#3 &;
   list = RandomChoice[CharacterRange["a", "j"], {2000, 3}];
   First /@ {
      RepeatedTiming[f[list, position, element]],
      RepeatedTiming[posF3[list, position, element]],
      RepeatedTiming[posF2[list, position, element]],
      RepeatedTiming[Flatten@Position[list, _List?(#[[position]] === element &)]],
      RepeatedTiming[posF1[list, position, element]]
   }
]

Sample output:

{0.000125, 0.000200, 0.0002303, 0.00248, 0.0030}

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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