Search code examples
matrixpositionfindkdbq-lang

General position find function in q


I need something like Position function for Mathematica (http://reference.wolfram.com/mathematica/ref/Position.html) but in Q. My solution for rectangular matrix is following:

q) colrow:{sz:count x; $[(count first x) = 1; enlist y; (floor y % sz;  y mod sz)]}
q) position:{flip colrow[x;(where raze x = y)]}

It works straightforward for rectangular matrices and lists:

q) t:(1 -1  1;  / matrix test
     -1  3  4; 
      1 -1  1);

q) pos1:position[t;-1]   / try to find all positions of -1
q) pos1
 0 1
 1 0
 2 1

q) t ./: pos1   / here get items
 -1 -1 -1

q) l:1 0 3 0 2 3 4 1 0  / list test
q) pos2:position[l;0]   / try to find all positions of 0

q) pos2
 1
 3
 8

q) l ./: pos2 / get items
 0 0 0 

This works but it'd be good to have more general solution for arbitrary lists and not only rectangular matrices. For instance code above won't work correctly for arguments like:

position[(1 2 3; 1 2; 1 2 1 4); 1]

May be someone has generic solution for that ?


Solution

  • How's this look? I think it should work for all two-dimensional lists, ragged or rectangular, and also for vectors. (I haven't worked out a version for arbitrary dimensions yet.)

    q)position:{{$[type x;enlist each where x;raze flip each flip(til count x;raze each .z.s each x)]}x=y}
    q)t
    1  -1 1
    -1 3  4
    1  -1 1
    q)l
    1 0 3 0 2 3 4 1 0
    q)r
    1 2 3
    1 2
    1 2 1 4
    q)pos1:position[t;-1]
    q)pos2:position[l;0]
    q)pos3:position[r;1]
    q)pos1
    0 1
    1 0
    2 1
    q)pos2
    1
    3
    8
    q)pos3
    0 0
    1 0
    2 0
    2 2
    q)t ./:pos1
    -1 -1 -1
    q)l ./:pos2
    0 0 0
    q)r ./:pos3
    1 1 1 1
    q)
    

    EDIT:

    Here's a version that works for all dimensions except 1 (and 0 of course):

    q)position2:{{$[type x;where x;raze each raze flip each flip(til count x;.z.s each x)]}x=y}
    q)r2:(r;r)
    q)0N!r2;
    ((1 2 3;1 2;1 2 1 4);(1 2 3;1 2;1 2 1 4))
    q)pos4:position2[r2;1]
    q)0N!pos4;
    (0 0 0;0 1 0;0 2 0;0 2 2;1 0 0;1 1 0;1 2 0;1 2 2)
    q)r2 ./:pos4
    1 1 1 1 1 1 1 1
    q)r ./:position2[r;1]
    1 1 1 1
    q)t ./:position2[t;-1]
    -1 -1 -1
    q)
    

    On vectors, though, it returns an address vector, not an address matrix, so it has to be used with @, not .:

    q)0N!position2[l;0];
    1 3 8
    q)l ./:position2[l;0]
    'type
    q)l position2[l;0]
    0 0 0
    q)
    

    If you really need it to work the same way on vectors as on higher-dimensional structures, the simplest solution is probably just to special-case them directly:

    q)position3:{$[type x;enlist each where@;{$[type x;where x;raze each raze flip each flip(til count x;.z.s each x)]}]x=y}
    q)position3[l;0]
    1
    3
    8
    q)l ./:position3[l;0]
    0 0 0
    q)r2 ./:position3[r2;1]
    1 1 1 1 1 1 1 1
    q)r ./:position3[r;1]
    1 1 1 1
    q)t ./:position3[t;-1]
    -1 -1 -1
    q)