Tool of Thought

APL for the Practical Man

"Don't chase tech. Chase fun." - Gunpei Yokoi

Variation on Iota

November 12, 2025

We just encountered the following real-life problem, which oddly, if memory serves, we have never encountered before: Given a list of items, find the location of each item in a list of lists of items. An example will make it clear:

      a← 'abc' 'de' '' 'fgha'
      w←'hafxd'
      a {...} w
3 0 3 4 1

So, it's just like Index Of (), only we are looking a level deeper on the left for the existence of an item on the right. We are not concerned where in the sublist on the left the item on the right is found. It is simply the location of the sublist (where the item is found) within the main list.

Our first inclination is to flatten out a, and then lookup items in w in the new flat array:

      ⊃,/a
abcdefgha
      (⊃,/a)⍳w
7 0 5 9 3

Then we need to map these indices, which index into the flattened array, back to indices apropriate for the original nested array, which are given by:

        ⍳≢a
0 1 2 3

We can do this by counting the items in each sublist:

      ≢¨a
3 2 0 4

And then replicating:

      (≢¨a)/⍳≢a
0 0 0 1 1 3 3 3 3

Now we can map the first set of indices into the second set of indices to get the desired indices:

      (⊂(⊃,/a)⍳w)⌷(≢¨a)/⍳≢a
INDEX ERROR

Oops, we need one more index for things not found:

      (1,⍨≢¨a)/⍳1+≢a
0 0 0 1 1 3 3 3 3 4

And now, with a little code golf to remove a set of parentheses:

     (⊂w⍳⍨⊃,/a)⌷(1,⍨≢¨a)/⍳1+≢a
3 0 3 4 1

Bingo, we are done. Let's make it a dfn:

      liota←{(⊂⍵⍳⍨⊃,/⍺)⌷(1,⍨≢¨⍺)/⍳1+≢⍺}

Now let's try a completely different approach. Consider the outer product of membership ():

      w∘.∊a
0 0 0 1
1 0 0 1
0 0 0 1
0 0 0 0
0 1 0 0

If we look for the first occurance of a 1 in each row we get our answer:

      (↓w∘.∊a)⍳¨1
3 0 3 4 1

And a little golf:

      1⍳⍨¨↓w∘.∊a
3 0 3 4 1

For what it is worth, we can get rid of nesting the Boolean matrix and the each operator by using the rank operator , un-golfing in the process:

      1⍳⍨(⍤1)w∘.∊a
3 0 3 4 1

Maybe we can go tacit. It looks like we have a function between a and w, and then a function on the result, that is:

   g a f w

Which can be rewritten as an atop:

    a (g f) w

Where:

      f←∘.∊⍨
      g←⍳∘1⍤1

Let's see if it works:

      a (g f) w
3 0 3 4 1

Oh yeah! Now we can just combine into one function:

    liota2←⍳∘1⍤1∘.∊⍨ 
    a liota2 w
3 0 3 4 1

We can guess that while the tacit version is short and sweet, it's going to be a dog in terms of time and space due to the outer product when both arguments get large, and indeed the flat version is almost infinitely faster. That being said, in our use case, neither argument will ever be very large.

Update

Josh David provides a much nicer flat array solution using interval index that is both shorter and faster:

      {1+(+\≢¨⍺)⍸⍵⍳⍨⊃,/⍺}

It's amazing the various uses of .