2
$\begingroup$

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.

$\endgroup$
2
  • 2
    $\begingroup$ Cases[Position[exampleList, "X"], {_, 2, _}] $\endgroup$ Commented Aug 20 at 18:10
  • 2
    $\begingroup$ I would push back on your feeling that the composite approach is somehow weak. Writing code is always about composing generic functions together to perform specific tasks. I think it's totally elegant to use Position followed by some sort of selector to filter the results. $\endgroup$ Commented Aug 20 at 20:21

3 Answers 3

1
$\begingroup$

Here is the basic way to do it :

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"}}
   };

collection = {}; 
Table[
 If[Extract[exampleList, {i, j, k}] === "X", 
  AppendTo[collection, {i, j, k}], Null]
 , {i, 1, Length[exampleList]}
 , {j, 2, Length[exampleList[[i]]]}
 , {k, 1, Length[exampleList[[i, j]]]}
 ];
collection  

{{1, 2, 5}, {1, 2, 6}, {2, 2, 3}, {2, 2, 4}}

There are plenty of other approaches, among them :

collection = {};
ReplacePart[exampleList, 
  x : {_, _?(# > 1 &), _} :> 
   If[Extract[exampleList, x] === "X", AppendTo[collection, x], Null]];
collection  

One can also use Sow ... Reap instead of collection = {} ... AppendTo[collection, x]. AppendTo is known to be very slow.

$\endgroup$
1
$\begingroup$

Another way:

Insert[2, 2] /@ Position[exampleList[[All, 2]], "X"]
(*  {{1, 2, 5}, {1, 2, 6}, {2, 2, 3}, {2, 2, 4}}  *)
$\endgroup$
1
$\begingroup$

You can use MapIndexed and Position as follows:

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"}}
};

Flatten[
    MapIndexed[
        Function[{pair, i},
            Map[Function[{i[[1]], 2, #[[1]]}],
                    Position[pair[[2]], "X"]
                ]
        ],
        exampleList
    ],
    1
 ]

{{1, 2, 5}, {1, 2, 6}, {2, 2, 3}, {2, 2, 4}}

$\endgroup$

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.