I have exampleList, a list of lists of lists:
exampleList = {
{{"I", "A", "i"}, {"a", "a", "b", "b", "X", "X"}},
{{"II", "A", "ii", "X"}, {"d", "d", "X", "X", "e", "e"}},
{{"III", "A", "iii"}, {"c", "b", "a"}}
};
I wish create a function to find the absolute positions (indices) in exampleList of "X" that appear in the second sublist of each sublist. In other words, the desired result for exampleList is as follows:
(* desired result *)
{{1, 2, 5}, {1, 2, 6}, {2, 2, 3}, {2, 2, 4}}
(Importantly, note that {2, 1, 4} is not in the desired result. There is an "X" at position {2, 1, 4}, but it's in the first sublist, not the second, of the list element {{"II", "A", "ii", "X"}, {"d", "d", "X", "X", "e", "e"}}, so {2, 1, 4} is not in the final desired output.)
I could simply find the positions of all of the "X" elements, and then delete positions referring to the first sublist. For example:
Position[exampleList, "X"]
DeleteCases[%, {_, 1, _}]
(* {{1, 2, 5}, {1, 2, 6}, {2, 1, 4}, {2, 2, 3}, {2, 2, 4}} *)
(* {{1, 2, 5}, {1, 2, 6}, {2, 2, 3}, {2, 2, 4}} *)
An alternative way to call Position is to use PatternTest as its second argument instead of the string pattern. Still we have to subsequently delete elements matching the pattern {_, 1, _}:
Position[exampleList, _?(# == "X" &)]
DeleteCases[%, {_, 1, _}]
(* {{1, 2, 5}, {1, 2, 6}, {2, 1, 4}, {2, 2, 3}, {2, 2, 4}} *)
(* {{1, 2, 5}, {1, 2, 6}, {2, 2, 3}, {2, 2, 4}} *)
This two-function approach (i.e., using Position and DeleteCases) works, but it feels like there should be a more direct way of obtaining the desired result -- without having to use DeleteCases to delete the element(s) having 1 as the second (sub)element. How can I do this?
Using a level specification of {3} gives the same result as before:
Position[exampleList, "X", {3}]
(* OUTPUT: *)
(* {{1, 2, 5}, {1, 2, 6}, {2, 1, 4}, {2, 2, 3}, {2, 2, 4}} *)
One idea I have is to use MemberQ. MemberQ is a predicate function, not a pattern, so I think I need to use PatternTest to make a pattern. I try the following, which generates error messages:
Position[exampleList, _?(MemberQ[#[[2]], "X"] &), Heads -> False]
(* ... Part: Part specification "I"[[2]] is longer than depth of object. *)
(* ... Part: Part specification "A"[[2]] is longer than depth of object. *)
(* ... Part: Part specification "I"[[2]] is longer than depth of object. *)
(* ... General: Further output of Part::partd will be suppressed
during this calculation. *)
I'm guessing that the problem lies in the level specification. As I have it written, the kernel will look for matches at all levels (except the heads, because I have Heads -> False). The fact that the kernel is thus attempting to compute "I"[[2]] and "A"[[2]] indicates that it's looking at levels too deep in the hierarchy. I try to use Level to try to identify the level specification I should include in my call to Position:
Level[exampleList, {1}]
Level[exampleList, {2}]
Level[exampleList, {3}]
(* {{{"I", "A", "i"}, {"a", "a", "b", "b", "X", "X"}},
{{"II", "A", "ii", "X"}, {"d", "d", "X", "X", "e", "e"}},
{{"III", "A", "iii"}, {"c", "b", "a"}}} *)
(* {{"I", "A", "i"}, {"a", "a", "b", "b", "X", "X"}, {"II", "A", "ii", "X"},
{"d", "d", "X", "X", "e", "e"}, {"III", "A", "iii"}, {"c", "b", "a"}} *)
(* {"I", "A", "i", "a", "a", "b", "b", "X", "X", "II", "A", "ii", "X",
"d", "d", "X", "X", "e", "e", "III", "A", "iii", "c", "b", "a"} *)
From that result, it appears that level specification {1} is what I need. But that still doesn't give me my desired result:
Position[exampleList, _?(MemberQ[#[[2]], "X"] &), {1}, Heads -> False]
(* {{1}, {2}} *)
That indeed picks out the "first-level" positions, but I wish for the kernel to give me the "full" depth positions of the desired "X" strings.
The solution using Position is probably obvious, but not to me. Could you please help me?
Other futile attempts of mine involve using the resource functions SelectPositions and SelectIndices:
ResourceFunction["SelectPositions"][exampleList, MemberQ[#[[2]], "X"] &]
ResourceFunction["SelectIndices"][exampleList, MemberQ[#[[2]], "X"] &]
(* {{1}, {2}} *)
(* {1, 2} *)
But, again, this is not at the "depth" at which I would like.
Positionfollowed by some sort of selector to filter the results. $\endgroup$