Search code examples
kdb

Access locally scoped variables from within a string using parse or value (KDB / Q)


The following lines of Q code all throw an error, because when the statement "local" is parsed, the local variable is not in the correct scope.

{local:1; value "local"}[]
{[local]; value "local"}[1]
{local:1; eval parse "local"}[]
{[local]; eval parse "local"}[1]

Is there a way to reach the local variable from inside the parsed string?

Note: This is a simplification of the actual problem I'm grappling with, which is to write a function that executes a query, accepting a list of columns which it should return. I imagine the finished product looking something like this:

getData:{[requiredColumns, condition]
    value "select ",(", " sv string[requiredColumns])," from myTable where someCol=condition"
    }

The condition parameter in this query is the one that isn’t recognised and I do realise I could append it’s value rather than reference it inside a string, but the real query uses lots of local variables including tables etc, so it’s not as easy as just pulling all the variables out of the string before calling value on it.

I'm new to KDB and Q, so if anyone has a better way to achieve the same effect I'm happy to be schooled on the proper way to achieve this outcome in Q. Would still be interested to know in the variable access thing is possible though.


Solution

  • In the first example, you are right that local is not within the correct scope, as value is looking for the global variable local.

    One way to get around this is to use a namespace, which will define the variable globally, but can only be accessed by calling that namespace. In the modified example below I have defined local in the .ns namespace

    {.ns.local:1; value ".ns.local"}[]
    

    For the problem you are facing with selecting, if requiredColumns is a symbol list of columns you can just use the take operator # to select them.

    getData:{[requiredColumns] requiredColumns#myTable}
    

    For more advanced queries using variables you may have to use functional select form, explained here. This will allow you to include variables in the where and by clause of the select statement

    The same example in functional form would be (no by clause, only select and where):

    getData:{[requiredColumns;condition] requiredColumns:(), requiredColumns;
    ?[myTable;enlist (=;`someCol;condition);0b;requiredColumns!requiredColumns]}
    

    The first line ensures that requiredColumns is a list even if the user enters a single column name