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
  • 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, 2020 at 19:27

2 Answers 2

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}
$\endgroup$
2
  • 3
    $\begingroup$ As you are comparing against a string, it is better to use === instead of ==: #[[2]] === "b". $\endgroup$ May 9, 2020 at 12:50
  • $\begingroup$ Thank you @J.M. Edited to change == to ===. $\endgroup$
    – kglr
    May 9, 2020 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}

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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