I use two string formulas in my details section to determine which countries to suppress. Formula1 refers to the Company field, and I suppress certain values:
{Countries}
In Formula2 i call a function with a parameter like #Formula1:
function(#formula1)
But the problem is that the function still takes the suppressed countries into account. Is this normal behavior, or do I need something extra like whileprintingdata to narrow my results.
When i debug the function the first time the function is called the incoming string is country1 even though I specified it to suppress that one. How can I ensure only unsuppressed values are passed to the function?
You could write additional logic into Formula2 to perform the same check that Formula1 does. Something like:
IF ({Countries} = 10) THEN
function("Company A")
ELSE IF ({Countries} = 14) THEN
function("Company B")
// ELSE IF....
ok got it your edit makes it clear...
what you can do is use the same supress condition in your formula 2 and pass the value to function.
some thing like below:
if your supress condition is
if {Countries} in (10,11,12)
then true
else false
then use the same in function 2 as
if {Countries} <> (10,11,12)
then function(formula1)
else any message as you wish
Related
I am having a couple of issues to put this in a functional format.
select from tableName where i=fby[(last;i);([]column_one;column_two)]
This is what I got:
?[tableName;fby;enlist(=;`i;(enlist;last;`i);(+:;(!;enlist`column_one`column_two;(enlist;`column_one;`column_two))));0b;()]
but I get a type error.
Any suggestions?
Consider using the following function, adjust from the buildQuery function given in the whitepaper on Parse Trees. This is a pretty useful tool for quickly developing in q, this version is an improvement on that given in the linked whitepaper, having been extended to handle updates by reference (i.e., update x:3 from `tab)
\c 30 200
tidy:{ssr/[;("\"~~";"~~\"");("";"")] $[","=first x;1_x;x]};
strBrk:{y,(";" sv x),z};
//replace k representation with equivalent q keyword
kreplace:{[x] $[`=qval:.q?x;x;"~~",string[qval],"~~"]};
funcK:{$[0=t:type x;.z.s each x;t<100h;x;kreplace x]};
//replace eg ,`FD`ABC`DEF with "enlist`FD`ABC`DEF"
ereplace:{"~~enlist",(.Q.s1 first x),"~~"};
ereptest:{((0=type x) & (1=count x) & (11=type first x)) | ((11=type x)&(1=count x))};
funcEn:{$[ereptest x;ereplace x;0=type x;.z.s each x;x]};
basic:{tidy .Q.s1 funcK funcEn x};
addbraks:{"(",x,")"};
//where clause needs to be a list of where clauses, so if only one whereclause need to enlist.
stringify:{$[(0=type x) & 1=count x;"enlist ";""],basic x};
//if a dictionary apply to both, keys and values
ab:{$[(0=count x) | -1=type x;.Q.s1 x;99=type x;(addbraks stringify key x),"!",stringify value x;stringify x]};
inner:{[x]
idxs:2 3 4 5 6 inter ainds:til count x;
x:#[x;idxs;'[ab;eval]];
if[6 in idxs;x[6]:ssr/[;("hopen";"hclose");("iasc";"idesc")] x[6]];
//for select statements within select statements
//This line has been adjusted
x[1]:$[-11=type x 1;x 1;$[11h=type x 1;[idxs,:1;"`",string first x 1];[idxs,:1;.z.s x 1]]];
x:#[x;ainds except idxs;string];
x[0],strBrk[1_x;"[";"]"]
};
buildSelect:{[x]
inner parse x
};
We can use this to create the functional query that will work
q)n:1000
q)tab:([]sym:n?`3;col1:n?100.0;col2:n?10.0)
q)buildSelect "select from tab where i=fby[(last;i);([]col1;col2)]"
"?[tab;enlist (=;`i;(fby;(enlist;last;`i);(flip;(lsq;enlist`col1`col2;(enlist;`col1;`col2)))));0b;()]"
So we have the following as the functional form
?[tab;enlist (=;`i;(fby;(enlist;last;`i);(flip;(lsq;enlist`col1`col2;(enlist;`col1;`col2)))));0b;()]
// Applying this
q)?[tab;enlist (=;`i;(fby;(enlist;last;`i);(flip;(lsq;enlist`col1`col2;(enlist;`col1;`col2)))));0b;()]
sym col1 col2
----------------------
bah 18.70281 3.927524
jjb 35.95293 5.170911
ihm 48.09078 5.159796
...
Glad you were able to fix your problem with converting your query to functional form.
Generally it is the case that when you use parse with a fby in your statement, q will convert this function into its k definition. Usually you should just be able to replace this k code with the q function itself (i.e. change (k){stuff} to fby) and this should run properly when turning the query into functional form.
Additionally, if you check out https://code.kx.com/v2/wp/parse-trees/ it goes into more detail about parse trees and functional form. Additionally, it contains a script called buildQuery which will return the functional form of the query of interest as a string which can be quite handy and save time when a functional form is complex.
I actually got it myself ->
?[tableName;((=;`i;(fby;(enlist;last;`i);(+:;(!;enlist`column_one`column_two;(enlist;`column_one;`column_two)))));(in;`venue;enlist`venueone`venuetwo));0b;()]
The issues was a () missing from the statement. Works fine now.
**if someone wants to add a more detailed explanation on how manual parse trees are built and how the generic (k){} function can be replaced with the actual function in q feel free to add your answer and I'll accept and upvote it
here is the sample that i want to ask:
FOR EACH table-name.... NO LOCK BY (IF TRUE THEN sort-this ELSE sort-that + sort-that2).
END.
This would result in an error.
if it is just
FOR EACH .. NO LOCK BY (IF TRUE THEN sort-this ELSE sort-that).
END.
then there is no error. Progress would accept the code
What is need is if condition is true then sort by one field else sort by two or more fields
If you are in a modern enough version of Progress, then you could construct a dynamic query. This will be more efficient in terms of run time as well as getting you round your problem as having IF statements in your query predicate will make index selection hard.
QueryString = "for each table no-lock...".
if true then
QueryString = QueryString + " by sort-this".
else
QueryString = QueryString + " by sort-that by sort-other".
create query QueryHandle.
QueryHandle:add-buffer(buffer table:handle).
QueryHandle:query-prepare(QueryString).
QueryHandle:query-open.
do while QueryHandle:get-next():
/*some stuff*/
end.
QueryHandle:query-close.
delete object QueryHandle.
As per the previous reply, this is not supported.
Typically, you'd have to prepare the result into a temp-table first, using a logical field in the temp-table for the result of your IF THEN ELSE expression.
Unfortunately, this syntax is not supported. As per the documentation, BREAK/BY expects an expression following it but not a statement.
The method should return words multiple times. The method should take 2 paramters word and n = number times word should be printed. But I want the second parameter to be optional. I can call the method with single parameter and it should return the same word. If second parameter used then it should return the same word that many times.
def repeat(word,n)
n.times {word}
end
p repeat("abc", 2) <- this works
but what if I only want
p repeat("abc")
Old question, but I had a similar problem today and solved it like this:
def repeat(word, n=1)
n.times {word}
end
If you call it like this:
p repeat("abc") it will only repeat it once, but you can pass in a value for n it will repeat however many times you want. If you want it to print out the actual word, this slight modification to the OP's method will do that.
def repeat(word, n=1)
n.times {puts word}
end
and call it with repeat("abc") or repeat("abc", 2)
I need help in retrieving query results within when clause of drl file in drools.
Example rule file having query:
query getUsersForCard (Long ipCardNumber)
$listOfUsers : UsersList()
$listOfUserCards : User(cardNumber == ipCardNumber, $cardNum : cardNumber) from $listOfUsers.user_list
end
rule "matchUser"
when
getUsersForCard("4444333322221112L";)
then
System.out.println( "$$$card number in VisaMessage matched with card number in UsersList$$$" );
end
How to obtain $cardNum aftergetUsersForCard query call so that then clause gets printed? I don't want to retrieve $cardNum in Java code from Working Memory but rather should be able to do it within the drl file itself.
Help is much appreciated!
Going back to the original question: "Can query results be accessed from the LHS of a rule?", the answer is: "Yes, using unification (Sections 8.8.3.3.6 and 8.9)".
A query in Drools can have input and output parameters. Input parameters are those that have a value (are bound) when the query is invoked. Those parameters that don't have a value (unbound) are considered output parameters.
The first thing to do is to rewrite your query using unification:
query getUsersForCard (Long ipCardNumber, Long $cardNum)
$listOfUsers : UsersList()
$listOfUserCards : User(
cardNumber == ipCardNumber,
$cardNum := cardNumber
) from $listOfUsers.user_list
end
The important thing to notice is the := (unification) sign being used. This operator is basically saying: "If the variable has a value, then I'll act as an == operator. Otherwise I'll act as a variable binding.
When invoking your query from a rule, you need to make sure you don't provide any value for the second parameter in the query. Given that you are already using positional arguments, that's easy to do:
rule "matchUser"
when
getUsersForCard("4444333322221112L", $cardNum;)
then
System.out.println( "$$$card number in VisaMessage matched with card number in UsersList$$$: "+$cardNum );
end
When the query is invoked, $cardNum will not have a value and it will set by the query because of the unification mechanism.
Hope it helps,
You cannot retrieve query results within the when clause the way I think you think it can be done. But there is no need to do so, simply write the rule as
rule "match card number"
when
$listOfUsers: UsersList()
$user: User(cardNumber == "4444333322221112L") from $listOfUsers
then ... end
Assuming the text of the println indicates what you really want to do, you might use
rule "match card number"
when
VisaMessage( $cardNo: cardNumber )
$listOfUsers: UsersList()
$user: User(cardNumber == $cardNo) from $listOfUsers
then ... end
I'm trying to so some normal variable expansion in a string and, when it's in a function, it comes out out-of-order.
function MakeMessage99($startValue, $endValue) { "Ranges from $startValue to $endValue" }
MakeMessage99(1, 100)
This returns Ranges from 1 100 to then it should return Ranges from 1 to 100
Functions in powershell shouldn't use parenthesis to enclose parameters. Instead:
PS C:\> MakeMessage99 1 100
Ranges from 1 to 100
Where MakeMessage is your function, "1" is a parameter in the first position, and "100" is a parameter in the second position. According to about_Functions_Advanced_Parameters:
By default, all function parameters are positional. Windows PowerShell assigns position numbers to parameters in the order in which the parameters are declared in the function.
Powershell has several ways to check input going in. You could cast the input as a numeric type. There are also baked-in validation methods for parameters that may prevent this sort of error in the future. If you really want an integer, a simple cast would cause an array to be invalid input. For example:
function MakeMessage99 {
Param
(
[int]$startValue,
[int]$endValue
)
"Ranges from $startValue to $endValue"
}
You could also explore range validation (such as [ValidateRange(0,100)]), pattern validation (such as [ValidatePattern("[0-9][0-9][0-9][0-9]")] to validate a four-digit number) or other validation attributes listed in the link above.
This is a common pitfall in PowerShell. When you invoke...
MakeMessage99(1, 100)
...you're actually passing an array containing the values 1 and 100 as the first parameter. To pass 1 as the first parameter and 100 as the second parameter, use...
MakeMessage99 1 100