Function which takes a string as parameter - kdb

In kdb I would like to have a function which takes a string as a parameter.
myfunc: {[strParam]
....
}
However when I tried to invoke the function.
q) myfunc["test"]
It complains of length error. It seems that function sees "test" as passing 4 char parameters. How can I make the function expect a string?

A string in kdb is defined as a list of characters and so functions using them have to be able to deal with this.
q)count "test"
4
You can also use a symbol instead casting from a string using `symbol$"test". A symbol is atomic and fixed width so can be useful to use as keys to a dictionary or in a table. Some functions for strings will still work on symbols e.g
q)upper `test
`TEST
while list operation will not work and you will have to turn it back into a string using string `test before using those operations.
When a length error is thrown and you go into the debug mode as shown by the q prompt having two brackets q)), you can use the functions .z.ex to show the failed function and .z.ey to see the failed arguments to narrow down which part is throwing the error.

The error can appear due to multiple reasons.
q)f:{[p] show p} //it works fine with any number of elements
q)f["abc"]
"abc"
f:{[p] enlist[`k]!p} //needs a list of single element
f["abc"]
'length
f[enlist "abc"]
(enlist `k)!enlist "abc"
q)f:{[p] 1 2 3 4!p} //the length of values is different from the length of keys
q)f["abc"]
'length
q)f["abcd"]
(1j, 2j, 3j, 4j)!"abcd"

Related

What is causing the error between these two .bindPopup?

The below popup works properly displaying "Neigh_Name" (a name such as "Main") and "2020 Total Population" (a string comprised of numbers '234273' etc).
layer.bindPopup(feature.properties.Neigh_Name+"<br>"+feature.properties["2020 Total Population"])
However, when using the below..
layer.bindPopup(feature.properties["2020 Total Population"])
I get the error: Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
Can someone explain what is happening in the backend and why these two lines of code differ and the latter ultimately fails? I'm trying to learn why certain things occur to avoid future issues.
Thanks!
Very probably the bindPopup method behaves unexpectedly when passing a number as content (your 2nd line), whereas it happily accepts a string argument (as in your 1st line).
Simply make sure to convert your argument into string, e.g. with "" + feature.properties["2020 Total Population"] or
`${feature.properties["2020 Total Population"]}`

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.
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
value will look for a variable in the global scope that's why you are getting an error. You can directly use local variables like you are doing that in your function.
Your function is mostly correct, just need a slight correction to append condition(I have mentioned that below). However, a better approach would be to use functional select in this case.
Using functional select:
q) t:([]id:`a`b; val:3 4)
q) gd: {?[`t;enlist (=;`val;y);0b;((),x)!(),x]}
q) gd[`id;3] / for single column
Output:
id
-
1
q) gd[`id`val;3] / for multiple columns
In case your condition column is of type symbol, then enlist your condition value like:
q) gd: {?[`t;enlist (=;`id;y);0b;((),x)!(),x]}
q) gd[`id;enlist `a]
You can use parse to get a functional form of qsql queries:
q) parse " select id,val from t where id=`a"
?
`t
,,(=;`id;,`a)
0b
`id`val!`id`val
Using String concat(your function):
q)getData:{[requiredColumns;condition] value "select ",(", " sv string[requiredColumns])," from t where id=", .Q.s1 condition}
q) getData[enlist `id;`a] / for single column
q) getData[`id`val;`a] / for multi columns

(kdb+/q) append to dictionary

I am trying to programmatically construct arguments to functional select call having the form:
?[ `t; () ; groupBy; ()]
The problematic part is groupBy, which should be a dictionary.
Suppose the objective is to arrive at the parse tree in the form:
parse "select by sym,month:`date$dt.month from t"
I start constructing the by part with:
groupBy: enlist[`sym]!enlist(`sym)
Then I try to append the month part of the group by statement (note that periodicity is parameterised):
per: `month / monthly periodicity
groupBy,: {enlist[x]!enlist[ $[x = `day;
`dt;
$[x=`month;
((parse "select by month:`date$dt.month from x")#3)#`month
;` sv (`dt,x)]
]]
}[per]
However, that throws type error. What goes wrong?
I think it doesn't like the compound assignment
groupBy,:{...}
Try
groupBy:groupBy,{...}
The difference is that in the first case it's trying to directly alter the data in memory without creating a copy, whereas in the second case it is creating a copy of the data in memory and then re-assigning it to that variable. Perhaps the compound assignment only works when the types are uniform

search a name in dataset error :Undefined function 'eq' for input arguments of type 'cell'

I load a file which has some columns with data.The first line contains ,CITY,YEAR2000 .
The first column has name of cities.The other columns contain number data.
I am trying to search for a specific city using:
data(data.CITY=='Athens',3:end)
where
data = dataset('File','cities.txt','Delimiter',',')
but I receive an error
Undefined function 'eq' for input arguments of type 'cell'.
--------UPDATE-----------------------------
Ok, use :
data(find(strncmp(data.CITY,'Athens',length('Athens'))),3:end)
Have you tried with using strncmp tangled with find?
I would use it this way
find(strncmp(data.CITY,'ATHENS',length('ATHENS')))
EDIT
Other opportunities to exploit would encompass strfind
strfind(data.CITY,'ATHENS')
EDIT 2
You could also try with
data(ismember(data.CITY,'ATHENS'),3:end)
This should lead you to the results you expect (at least I guess so).
EDIT 3
Given your last request I would go for this solution:
inp = input('Name of the CITY: ','s')
Name of the City: ATHENS
data(find(strncmp(data.CITY,inp,length(inp))),3:end)

MATLAB: Referencing an element in a structure

I am trying to reference an element buried within a structure that I did not create (hence I don't know the exact way in which it was built).
Having loaded the structure, if I type:
dataFile.RECORDINGS.eye
I receive the following output:
ans =
2
ans =
2
Both of those variables will always be the same, but they could be at any time 1, 2 or 3. What I'd like to do is check with a switch statement which looks like this:
switch dataFile.RECORDINGS.eye
case {1, 2}
% action A
case 3
% action B
end
Of course, the above throws up an error because 'case' cannot check whether dataFile.RECORDINGS.eye contains a given value since there are two elements stored under that address. So, my question is: how do I reference just one of the elements? I thought it would be as simple as replacing the first line with:
switch dataFile.RECORDINGS.eye(1)
...But, this gives the error:
??? Field reference for multiple structure elements that is followed by more reference blocks is an error.
Similarly, I can't access the element like this:
switch dataFile.RECORDINGS.eye.1
...As I get the following error:
??? Dot name reference on non-scalar structure.
If the values are really always the same, you can try the following to get a scalar that can be used in the switch command:
unique([dataFile.RECORDINGS.eye])
By the way, did you try to index RECORDINGS, i.e.,
dataFile.RECORDINGS(1).eye
dataFile.RECORDINGS(2).eye
Perhaps instead of eye having multiple elements, you have multiple elements of RECORDINGS that each have a single value of eye? You might want dataFile.RECORDINGS(1).eye or dataFile.RECORDINGS(2).eye.