Search code examples
kdbq-lang

Understanding how to read each-right and each-left combined in kdb


From q for mortals, i'm struggling to understand how to read this, and understand it logically.

1 2 3,/:\:10 20

I understand the result is a cross product when in full form: raze 1 2 3,/:\:10 20.

But reading from left to right, I'm currently lost at understanding what this yields (in my head)

\:10 20

combined with 1 2 3,/: ??

Help in understanding how to read this clearly (in words or clear logic) would be appreciated.


Solution

  • I found myself saying the following in my head whilst I program the syntax in q. q works from right to left.

    Internal Monologue ->  Join the string on the right onto each of the strings on the left
    code ->                "ABC",\:"-D"
    result ->              "A-D"
                           "B-D" 
                           "C-D"
    

    I think that's an easy way to understand it. 'join' can be replaced with whatever...

    Internal Monologue ->  Does the string on the right match any of the strings on the left
    code ->                ("Cat";"Dog";"CAT";"dog")~\:"CAT"
    result ->              0010b
    

    Each-right is the same concept and combining them is straightforward also;

    Internal Monologue ->  Does each of the strings on the right match each of the strings on the left
    code ->                ("Cat";"Dog";"CAT";"dog")~\:/:("CAT";"Dog")
    result ->              0010b
                           0100b   
    

    So in your example 1 2 3,/:\:10 20 - you're saying 'Join each of the elements on the right to each of the elements on the left'

    Hope this helps!!

    EDIT To add a real world example.... - consider the following table

    q)show tab:([] upper syms:10?`2; names:10?("Robert";"John";"Peter";"Jenny"); amount:10?til 10)
    syms names    amount
    --------------------
    CF   "Peter"  8
    BP   "Robert" 1
    IC   "John"   9
    IN   "John"   5
    NM   "Peter"  4
    OJ   "Jenny"  6
    BJ   "Robert" 6
    KH   "John"   1
    HJ   "Peter"  8
    LH   "John"   5
    q)
    

    I you want to get all records where the name is Robert, you can do; select from tab where names like "Robert"

    But if you want to get the results where the name is either Robert or John, then it is a perfect scenario to use our each-left and each-right.

    Consider the names column - it's a list of strings (a list where each element is a list of chars). What we want to ask is 'does any of the strings in the names column match any of the strings we want to find'... that translates to (namesList)~\:/:(list;of;names;to;find). Here's the steps;

    q)(tab`names)~\:/:("Robert";"John")
    0100001000b
    0011000101b
    

    From that result we want a compiled list of booleans where each element is true of it is true for Robert OR John - for example, if you look at index 1 of both lists, it's 1b for Robert and 0b for John - in our result, the value at index 1 should be 1b. Index 2 should be 1b, index3 should be 1b, index4 should be 0b etc... To do this, we can apply the any function (or max or sum!). The result is then;

    q)any(tab`names)~\:/:("Robert";"John")
    0111001101b
    

    Putting it all together, we get;

    q)select from tab where any names~\:/:("Robert";"John")
    syms names    amount
    --------------------
    BP   "Robert" 1
    IC   "John"   9
    IN   "John"   5
    BJ   "Robert" 6
    KH   "John"   1
    LH   "John"   5
    q)