Nested Crystal if statement not working - if-statement

Morning Guys,
I have the following formula in a Crystal Sub-report and it isn't returning any values. I know data exists for over a dozen records, I've used the check function and no syntax errors are given and I've followed the example as given in the following question 'Crystal Report: How to evaluate multiple IF statements in one formula?'.
The formula is as follows;
(if {?Pm-Enquiry.EnquiryId} <1000 and
{DocumentType.DocumentSubtype} = "DocumentSubtype1" then
left ({DocumentSubtype1Table.Field1},3) else
left ({DocumentSubtype1Table.Field1},4)
);
(if {?Pm-Enquiry.EnquiryId} <1000 and
{DocumentType.DocumentSubtype} = "DocumentSubtype2" then
left ({DocumentSubtype2Table.Field1},3) else
left ({DocumentSubtype2Table.Field1},4)
);
(if {?Pm-Enquiry.EnquiryId} <1000 and
{DocumentType.DocumentSubtype} = "DocumentSubtype1" then
left ({DocumentSubtype3Table.Field1},3) else
left ({DocumentSubtype3Table.Field1},4)
);
The formula should determine if the ID is less than or greater than three digits and retrieve the first 3/4 digits from the DocumentSubtypeTable. Unfortunately the ID has been amalgamated with the date in this field making this formula necessary.
Any help you guys could give would be really appreciated.

Perhaps since you are not returning a value.
Try the following (define a variable, fill your result in it, then return it.):
StringVar StrValue;
(if {?Pm-Enquiry.EnquiryId} <1000 and {DocumentType.DocumentSubtype} = "DocumentSubtype1" then
StrValue := left ({DocumentSubtype1Table.Field1},3) else
StrValue := left ({DocumentSubtype1Table.Field1},4)
);
(if {?Pm-Enquiry.EnquiryId} <1000 and {DocumentType.DocumentSubtype} = "DocumentSubtype2" then
StrValue := left ({DocumentSubtype2Table.Field1},3) else
StrValue := left ({DocumentSubtype2Table.Field1},4)
);
(if {?Pm-Enquiry.EnquiryId} <1000 and {DocumentType.DocumentSubtype} = "DocumentSubtype1" then
StrValue := left ({DocumentSubtype3Table.Field1},3) else
StrValue := left ({DocumentSubtype3Table.Field1},4)
);
StrValue;

Related

Calculation in one field

i'm new to sql and pl/sql. To practice I was giving an assignment to make a calculator. That part works. But they also want the possibility to type the calculation in the text field and then it needs to work. For example 4+4 (then the = button or enter on your keyboard) or 4+6-3=.
My calculator with buttons works, but not if I type a calculation in the text field. Can anyone help me with this?
This is the code I have in my total:
declare
l_operator varchar2(1) := :P3_OPERATOR;
l_value1 number := :P3_VALUE1;
l_value2 number := :P3_VALUE2;
l_result number := nvl(:P3_VALUE1,0);
begin
case l_operator
when '+' then
l_result := l_value1 + l_value2;
when '-' then
l_result := l_value1 - l_value2;
when '*' then
l_result := l_value1 * l_value2;
when '/' then
l_result := l_value1 / l_value2;
else
null;
end case;
:P3_OPERATOR := null;
:P3_VALUE2 := null;
:P3_VALUE1 := l_result;
:P3_NUMBERFIELD := l_result;
end;
with this for als extra for the +, -, * and \ .
:P12_OPERATOR := '*';
:P12_NUMBERFIELD := :P12_OPERATOR;
and this is the code for all my number buttons:
begin
if :P12_OPERATOR is null then
:P12_VALUE1 := :P12_VALUE1 || 4;
:P12_NUMBERFIELD := :P12_VALUE1;
elsif :P12_OPERATOR is not null then
:P12_VALUE2 := :P12_VALUE2 || 4;
:P12_NUMBERFIELD := :P12_VALUE2;
end if;
end;
This is not a typical way to use SQL or PL/SQL (or APEX which it looks like you are also using)!
You could evaluate any expression typed in with code like this:
begin
execute immediate 'select ' || :P3_NUMBERFIELD || ' from dual' into l_result;
exception
when others then
l_result := 'Invalid input';
end;
The exception part is to stop the calculator going wrong if the user types in nonsense like "hello world" instead of an arithmetic expression. The user would need to type in an expression like 4+4 without typing the equals sign, and then press a button to invoke the process to calculate the result.

Changing the State of Lexing.lexbuf

I am writing a lexer for Brainfuck with Ocamllex, and to implement its loop, I need to change the state of lexbuf so it can returns to a previous position in the stream.
Background info on Brainfuck (skippable)
in Brainfuck, a loop is accomplished by a pair of square brackets with
the following rule:
[ -> proceed and evaluate the next token
] -> if the current cell's value is not 0, return to the matching [
Thus, the following code evaluates to 15:
+++ [ > +++++ < - ] > .
it reads:
In the first cell, assign 3 (increment 3 times)
Enter loop, move to the next cell
Assign 5 (increment 5 times)
Move back to the first cell, and subtract 1 from its value
Hit the closing square bracket, now the current cell (first) is equals to 2, thus jumps back to [ and proceed into the loop again
Keep going until the first cell is equals to 0, then exit the loop
Move to the second cell and output the value with .
The value in the second cell would have been incremented to 15
(incremented by 5 for 3 times).
Problem:
Basically, I wrote two functions to take care of pushing and popping the last position of the last [ in the header section of brainfuck.mll file, namely push_curr_p and pop_last_p which pushes and pops the lexbuf's current position to a int list ref named loopstack:
{ (* Header *)
let tape = Array.make 100 0
let tape_pos = ref 0
let loopstack = ref []
let push_curr_p (lexbuf: Lexing.lexbuf) =
let p = lexbuf.Lexing.lex_curr_p in
let curr_pos = p.Lexing.pos_cnum in
(* Saving / pushing the position of `[` to loopstack *)
( loopstack := curr_pos :: !loopstack
; lexbuf
)
let pop_last_p (lexbuf: Lx.lexbuf) =
match !loopstack with
| [] -> lexbuf
| hd :: tl ->
(* This is where I attempt to bring lexbuf back *)
( lexbuf.Lexing.lex_curr_p <- { lexbuf.Lexing.lex_curr_p with Lexing.pos_cnum = hd }
; loopstack := tl
; lexbuf
)
}
{ (* Rules *)
rule brainfuck = parse
| '[' { brainfuck (push_curr_p lexbuf) }
| ']' { (* current cell's value must be 0 to exit the loop *)
if tape.(!tape_pos) = 0
then brainfuck lexbuf
(* this needs to bring lexbuf back to the previous `[`
* and proceed with the parsing
*)
else brainfuck (pop_last_p lexbuf)
}
(* ... other rules ... *)
}
The other rules work just fine, but it seems to ignore [ and ]. The problem is obviously at the loopstack and how I get and set lex_curr_p state. Would appreciate any leads.
lex_curr_p is meant to keep track of the current position, so that you can use it in error messages and the like. Setting it to a new value won't make the lexer actually seek back to an earlier position in the file. In fact I'm 99% sure that you can't make the lexer loop like that no matter what you do.
So you can't use ocamllex to implement the whole interpreter like you're trying to do. What you can do (and what ocamllex is designed to do) is to translate the input stream of characters into a stream of tokens.
In other languages that means translating a character stream like var xyz = /* comment */ 123 into a token stream like VAR, ID("xyz"), EQ, INT(123). So lexing helps in three ways: it finds where one token ends and the next begins, it categorizes tokens into different types (identifiers vs. keywords etc.) and discards tokens you don't need (white space and comments). This can simplify further processing a lot.
Lexing Brainfuck is a lot less helpful as all Brainfuck tokens only consist of a single character anyway. So finding out where each token ends and the next begins is a no-op and finding out the type of the token just means comparing the character against '[', '+' etc. So the only useful thing a Brainfuck lexer does is to discard whitespace and comments.
So what your lexer would do is turn the input [,[+-. lala comment ]>] into something like LOOP_START, IN, LOOP_START, INC, DEC, OUT, LOOP_END, MOVE_RIGHT, LOOP_END, where LOOP_START etc. belong to a discriminated union that you (or your parser generator if you use one) defined and made the lexer output.
If you want to use a parser generator, you'd define the token types in the parser's grammar and make the lexer produce values of those types. Then the parser can just parse the token stream.
If you want to do the parsing by hand, you'd call the lexer's token function by hand in a loop to get all the tokens. In order to implement loops, you'd have to store the already-consumed tokens somewhere to be able to loop back. In the end it'd end up being more work than just reading the input into a string, but for a learning exercise I suppose that doesn't matter.
That said, I would recommend going all the way and using a parser generator to create an AST. That way you don't have to create a buffer of tokens for looping and having an AST actually saves you some work (you no longer need a stack to keep track of which [ belongs to which ]).

Replace every nth instance of character in string

I'm a bit new to Go, but I'm trying to replace every nth instance of my string with a comma. So for example, a part of my data looks as follows:
"2017-06-01T09:15:00+0530",1634.05,1635.95,1632.25,1632.25,769,"2017-06-01T09:16:00+0530",1632.25,1634.9,1631.65,1633.5,506,"2017-06-01T09:17:00+0530",1633.5,1639.95,1633.5,1638.4,991,
I want to replace every 6th comma with a '\n' so it looks like
"2017-06-01T09:15:00+0530",1634.05,1635.95,1632.25,1632.25,769"
"2017-06-01T09:16:00+0530",1632.25,1634.9,1631.65,1633.5,506"
"2017-06-01T09:17:00+0530",1633.5,1639.95,1633.5,1638.4,991"
I've looked at the regexp package and that just seems to be a finder. The strings package does have a replace but I don't know how to use it to replace specific indices. I also don't know how to find specific indices without going through the entire string character by character. I was wondering if there is a regEx solution that is more elegant than me writing a helper function.
Strings are immutable so I'm not able to edit them in place.
EDIT: Cast the string into []bytes. This allows me to edit the string in place. Then the rest is a fairly simple for loop, where dat is the data.
If that is your input, you should replace ," strings with \n".You may use strings.Replace() for this. This will leave a last, trailing comma which you can remove with a slicing.
Solution:
in := `"2017-06-01T09:15:00+0530",1634.05,1635.95,1632.25,1632.25,769,"2017-06-01T09:16:00+0530",1632.25,1634.9,1631.65,1633.5,506,"2017-06-01T09:17:00+0530",1633.5,1639.95,1633.5,1638.4,991,`
out := strings.Replace(in, ",\"", "\n\"", -1)
out = out[:len(out)-1]
fmt.Println(out)
Output is (try it on the Go Playground):
"2017-06-01T09:15:00+0530",1634.05,1635.95,1632.25,1632.25,769
"2017-06-01T09:16:00+0530",1632.25,1634.9,1631.65,1633.5,506
"2017-06-01T09:17:00+0530",1633.5,1639.95,1633.5,1638.4,991
If you want flexible.
package main
import (
"fmt"
"strings"
)
func main() {
input := `"2017-06-01T09:15:00+0530",1634.05,1635.95,1632.25,1632.25,769,"2017-06-01T09:16:00+0530",1632.25,1634.9,1631.65,1633.5,506,"2017-06-01T09:17:00+0530",1633.5,1639.95,1633.5,1638.4,991,`
var result []string
for len(input) > 0 {
token := strings.SplitN(input, ",", 7)
s := strings.Join(token[0:6], ",")
result = append(result, s)
input = input[len(s):]
input = strings.Trim(input, ",")
}
fmt.Println(result)
}
https://play.golang.org/p/mm63Hx24ne
So I figured out what I was doing wrong. I initially had the data as a string, but if I cast it to a byte[] then I can update it in place.
This allowed me to use a simple for loop below to solve the issue without relying on any other metric other than nth character instance
for i := 0; i < len(dat); i++ {
if dat[i] == ',' {
count += 1
}
if count%6 == 0 && dat[i] == ',' {
dat[i] = '\n'
count = 0
}

Remove everything but numbers from a cell

I have an excel sheet where i use the follwoing command to get numbers from a cell that contains a form text:
=MID(D2;SEARCH("number";D2)+6;13)
It searches for the string "number" and gets the next 13 characters that comes after it. But some times the results get more than the number due to the fact these texts within the cells do not have a pattern, like the example below:
62999999990
21999999990
11999999990
6299999993) (
17999999999)
21914714753)
58741236714 P
18888888820
How do i avoid taking anything but numbers OR how do i remove everything but numbers from what i get?
You can user this User Defined Function (UDF) that will get only the numbers inside a specific cell.
Code:
Function only_numbers(strSearch As String) As String
Dim i As Integer, tempVal As String
For i = 1 To Len(strSearch)
If IsNumeric(Mid(strSearch, i, 1)) Then
tempVal = tempVal + Mid(strSearch, i, 1)
End If
Next
only_numbers = tempVal
End Function
To use it, you must:
Press ALT + F11
Insert new Module
Paste code inside Module window
Now you can use the formula =only_numbers(A1) at your spreadsheet, by changing A1 to your data location.
Example Images:
Inserting code at module window:
Executing the function
Ps.: if you want to delimit the number of digits to 13, you can change the last line of code from:
only_numbers = tempVal
to
only_numbers = Left(tempVal, 13)
Alternatively you can take a look a this topic to understand how to achieve this using formulas.
If you are going to go to a User Defined Function (aka UDF) then perform all of the actions; don't rely on the preliminary worksheet formula to pass a stripped number and possible suffix text to the UDF.
In a standard code module as,
Function udfJustNumber(str As String, _
Optional delim As String = "number", _
Optional startat As Long = 1, _
Optional digits As Long = 13, _
Optional bCaseSensitive As Boolean = False, _
Optional bNumericReturn As Boolean = True)
Dim c As Long
udfJustNumber = vbNullString
str = Trim(Mid(str, InStr(startat, str, delim, IIf(bCaseSensitive, vbBinaryCompare, vbTextCompare)) + Len(delim), digits))
For c = 1 To Len(str)
Select Case Asc(Mid(str, c, 1))
Case 32
'do nothing- skip over
Case 48 To 57
If bNumericReturn Then
udfJustNumber = Val(udfJustNumber & Mid(str, c, 1))
Else
udfJustNumber = udfJustNumber & Mid(str, c, 1)
End If
Case Else
Exit For
End Select
Next c
End Function
I've used your narrative to add several optional parameters. You can change these if your circumstances change. Most notable is whether to return a true number or text-that-looks-like-a-number with the bNumericReturn option. Note that the returned values are right-aligned as true numbers should be in the following supplied image.
By supplying FALSE to the sixth parameter, the returned content is text-that-looks-like-a-number and is now left-aligned in the worksheet cell.
If you don't want VBA and would like to use Excel Formulas only, try this one:
=SUMPRODUCT(MID(0&MID(D2,SEARCH("number",D2)+6,13),LARGE(INDEX(ISNUMBER(--MID(MID(D2,SEARCH("number",D2)+6,13),ROW($1:$13),1))* ROW($1:$13),0),ROW($1:$13))+1,1)*10^ROW($1:$13)/10)

Correct syntax for multiple execution lines inside an if-else Crystal Report formula

I keep getting an error "A string is required here" in my Crystal Report formula. The part that gets highlighted is the "else" portion, suggesting that is where the error is. I've tried so many different variations of brackets, no brackets, semi-colons, etc... sometimes I get a different error but none of it seems to work.
Local stringVar outputString := "";
if isNull({Evaluation.Strategic}) then
(
outputString := "None"
)
else
(
stringVar array x := split({Evaluation.Strategic},"|");
Local numberVar i;
For i:=1 to Count(x) do
(
outputString := outputString + x[i] + Chr(10)
)
);
outputString;
Can anyone please point out to me the correct syntax to do what I want to do? All the samples I've found online (there are few with multiple lines inside the if-else blocks) suggest this should work.
Thanks
Based on the below 2 sites I really thought you could have multiple statements inside an if-else but nothing I tried was working.
http://www.experts-exchange.com/questions/23735693/Multiple-line-IF-statement-in-Crystal-Reports.html
http://www.kenhamady.com/formulas/form08.shtml
I ended up getting this to work by restructuring my logic to be as shown below. It's a little clunky but it made the CR compiler happy.
//Syntactically correct - this worked:
//-------------------------------------
Local stringVar outputString := "";
stringVar array x := "";
Local numberVar i;
If isNull({Evaluation.Strategic}) Then
(
x := ""
)
Else
(
x := split({Evaluation.Strategic},"|");
);
If x <> "" Then
(
For i:=1 to Count(x) do
(
outputString := outputString + x[i] + Chr(10)
)
)
else
(
For i:=1 to Count(x) do
(
outputString := "None";
);
);
outputString;
Edited: And another site that suggests you can have more than one statement inside an If:
https://msdn.microsoft.com/en-us/library/ms225356(v=vs.80).aspx
IF ([enter first condition here] or [enter second condition here])
THEN ([enter first execution here]; [enter second execution here])
ELSE 0
worked for me (from links in the first answer)
You need to remember your semi-colons at the end of each statement including the after the if
For instance this will not validate -
if (myCondition) Then (
a:= "First"
b:= "Second"
)
if (myOtherCondition) Then (
a:= a + "Other First"
b:= b + "Other Second"
)
However if you add the semi-colons Then it will validate -
if (myCondition) Then (
a:= "First";
b:= "Second";
);
if (myOtherCondition) Then (
a:= a + "Other First";
b:= b + "Other Second";
);
While Crystal does not need the semi-colon after the final statement within a the scope it is probably good practice to add it at the end of every statement as it will help avoid issues like this.