So let's assume that I have a file which consists of 10 students with 3 fields: Name, Gender, Age. So, theoretically, I would want to create a 10 by 3 array.
But when it comes to COBOL, two-dimensional tables are created by this example:
01 WS-TABLE.
05 WS-A OCCURS 10 TIMES.
10 WS-B PIC A(10).
10 WS-C OCCURS 5 TIMES.
15 WS-D PIC X(6).
In this example, I can not understand what WS-B and WS-D are. If I want to create an array like the one I mentioned (10 by 3), how may I do so?
Thanks
First of all COBOL doesn't have arrays per-se it has tables. There is no way to make a 2-dimensional table. The example you have give is actually the closest you can get (a nested table). If I was faced with the problem you do (a field of 10 student with Name, Gender and Age) I would structure my data like this:
01 WS-TABLE.
05 WS-STUDENT OCCURS 10 TIMES.
10 WS-NAME PIC X(10).
10 WS-GENDER PIC X.
10 WS-AGE PIC 9(3).
In this example I would use a subscript to access the fields I have created for student. So this is what a loop to display them all would look like:
PERFORM VARYING WS-X
FROM 1 BY 1
UNTIL WS-X > 10
DISPLAY "NAME: " WS-NAME(WS-X) " GENDER: " WS-GENDER(WS-X) " AGE: " WS-AGE(WS-X)
END-PERFORM
Related
In my program I keep filling the following array with data obtained from a database table then inspect it to find certain words:
01 PRODUCTS-TABLE.
03 PRODUCT-LINE PIC X(40) OCCURS 50 TIMES.
sometimes it occurs 6 times, sometimes more than 6 times.
I'd like to find the number of lines in the array every time I write data to it , how can I do that ?
I tried this but it based on a fixed length:
INSPECT-PROCESS.
MOVE 0 TO TALLY-1.
INSPECT PRODUCTS-TABLE TALLYING TALLY-1 FOR ALL "PRODUCT"
IF TALLY-1 > 0
MOVE SER-NUMBER TO HITS-SN-OUTPUT
MOVE FILLER-SYM TO FILLER-O
MOVE PRODUCT-LINE(1) TO HITS-PR-OUTPUT
WRITE HITS-REC
PERFORM WRITE-REPORT VARYING CNT1 FROM 2 BY 1 UNTIL CNT1 = 11.
WRITE-REPORT.
MOVE " " TO HITS-SN-OUTPUT
MOVE PRODUCT-LINE(CNT1) TO HITS-TX-OUTPUT
WRITE HITS-REC.
In the first output line it writes the SN and the first product-line then in the following lines it writes all remaining product-line and blank out SN.
Something like:
12345678 first product-line
Second product-line
etc
It’s working, however, it only stops when CNT1 is 11, how can I feed the procedure with a variable CNT1 based on how many lines are actually in PRODUCTS-TABLE each time?
I solved the problem by adding an array line counter (LINE-COUNTER-1) to count (ADD 1 TO LINE-COUNTER-1) how many times I add data to the array and stop writing the report when "WRITE-COUNTER = LINE-COUNTER-1"
INSPECT-PROCESS.
MOVE 0 TO TALLY-1
INSPECT PRODUCTS-TABLE TALLYING TALLY-1 FOR ALL "PRODUCT"
IF TALLY-1 > 0
MOVE HOLD-SER-NUM TO HITS-SN-OUTPUT
MOVE FILLER-SYM TO FILLER-O
MOVE PRODUCT-LINE(1) TO HITS-PR-OUTPUT
WRITE HITS-REC
PERFORM WRITE-REPORT VARYING WRITE-COUNTER FROM 2 BY 1
UNTIL WRITE-COUNTER = LINE-COUNTER-1.
Trying to solve this using arrays.
Here's the problem:
Problem I'm facing: Has to be an easier way to loop through the booking types. When I view the output, it shows the customer number, customer name, and address several times with the same input which it shouldn't be.
Any help would be appreciated.
Here is the program below:
Process Apost.
Identification Division.
Program-ID. BOOKINGARR.
*
* Page 554 No 3. ARRAYS.
* Data in sequence by Client No. Print the average cost of
* trip for each booking type. Use arrays.
*
Environment Division.
Configuration Section.
Source-Computer. IBM-AS400.
Object-Computer. IBM-AS400.
Input-Output Section.
File-Control.
Select Input-File Assign to Database-Bookingpf.
Select Output-File Assign to Printer-Qsysprt.
Data Division.
File Section.
FD Input-File.
01 Input-File-Rec.
Copy DDS-BookingR of Bookingpf.
FD Output-File.
01 Output-File-Rec Pic x(120).
Working-Storage Section.
01 END-OF-FILE PIC X VALUE 'N'.
01 WS-ARRAY.
05 WS-TABLE-ENTRIES OCCURS 4 TIMES.
10 WS-TOTAL-COST PIC 9(7)V99.
10 WS-TRIP-COUNT PIC 999.
10 WS-AVG-COST PIC 9(7)V99 VALUE ZERO.
10 WS-BOOKING-TYPE PIC 9.
01 ARRAY-INDEX PIC 99.
01 EMPTY-POINTER PIC 99.
01 ARRAY-EMPTY PIC XXX.
01 PROGRAM-HEADER.
05 PIC X(2) VALUE SPACES.
05 PIC X(10) VALUE 'CLIENT NO.'.
05 PIC X(3) VALUE SPACES.
05 PIC X(11) VALUE 'CLIENT NAME'.
05 PIC X(6) VALUE SPACES.
05 PIC X(14) VALUE 'CLIENT ADDRESS'.
05 PIC X(4) VALUE SPACES.
05 PIC X(9) VALUE 'BOOK TYPE'.
05 PIC X(4) VALUE SPACES.
05 PIC X(12) VALUE 'AVERAGE COST'.
01 REPORT-LINE.
05 PIC X(2) VALUE SPACES.
05 CLIENTNO-OUT PIC 999.
05 PIC X(10) VALUE SPACES.
05 CLIENTNA-OUT PIC X(16).
05 PIC X(1) VALUE SPACES.
05 CLIENTADD-OUT PIC X(19).
05 PIC X(3) VALUE SPACES.
05 BOOKTYPE-OUT PIC Z.
05 PIC X(8) VALUE SPACES.
05 AVGCOST-OUT PIC $Z,ZZ9.99.
05 PIC X(12) VALUE SPACES.
Procedure Division.
000-MAIN.
OPEN INPUT INPUT-FILE
OUTPUT OUTPUT-FILE.
PERFORM 100-MOVE.
PERFORM 1000-READ.
PERFORM 300-UPDATE-BOOKINGS
UNTIL END-OF-FILE = 'Y'.
WRITE OUTPUT-FILE-REC FROM PROGRAM-HEADER.
PERFORM 600-WRITE-TO-SCREEN
VARYING ARRAY-INDEX FROM 1 BY 1
UNTIL ARRAY-INDEX > 4.
CLOSE INPUT-FILE, OUTPUT-FILE.
STOP RUN.
100-MOVE.
MOVE 1 TO EMPTY-POINTER.
MOVE 'Y' TO ARRAY-EMPTY.
MOVE SPACES TO UPDATE-DONE.
PERFORM 150-ZERO-OUT-ARRAY
VARYING ARRAY-INDEX FROM 1 BY 1 UNTIL
ARRAY-INDEX > 4.
150-ZERO-OUT-ARRAY.
MOVE ZEROS TO WS-BOOKING-TYPE (ARRAY-INDEX).
MOVE ZEROS TO WS-TOTAL-COST (ARRAY-INDEX).
MOVE ZEROS TO WS-TRIP-COUNT (ARRAY-INDEX).
MOVE ZEROS TO WS-AVG-COST (ARRAY-INDEX).
1000-READ.
READ INPUT-FILE AT END MOVE 'Y' TO END-OF-FILE.
300-UPDATE-BOOKINGS.
IF ARRAY-EMPTY = 'Y'
PERFORM 400-ADD-1-TO-COUNT
MOVE 'N' TO ARRAY-EMPTY
ELSE
MOVE 'N' TO UPDATE-DONE
PERFORM 500-GET-BOOKING-AVERAGE
VARYING ARRAY-INDEX FROM 1 BY 1
UNTIL ARRAY-INDEX = EMPTY-POINTER
OR
UPDATE-DONE = 'Y'.
IF UPDATE-DONE = 'N'
PERFORM 400-ADD-1-TO-COUNT.
PERFORM 1000-READ.
400-ADD-1-TO-COUNT.
MOVE BOOKTYPE TO WS-BOOKING-TYPE (EMPTY-POINTER).
ADD 1 TO WS-TRIP-COUNT (EMPTY-POINTER).
MOVE COSTOFTRIP TO WS-TOTAL-COST (EMPTY-POINTER).
MOVE COSTOFTRIP TO WS-AVG-COST (EMPTY-POINTER).
ADD 1 TO EMPTY-POINTER.
500-GET-BOOKING-AVERAGE.
IF BOOKTYPE = WS-BOOKING-TYPE (ARRAY-INDEX)
ADD 1 TO WS-TRIP-COUNT (ARRAY-INDEX)
ADD COSTOFTRIP TO WS-TOTAL-COST (ARRAY-INDEX)
COMPUTE WS-AVG-COST (ARRAY-INDEX) =
WS-TOTAL-COST (ARRAY-INDEX) /
WS-TRIP-COUNT (ARRAY-INDEX)
MOVE 'Y' TO UPDATE-DONE.
600-WRITE-TO-SCREEN.
MOVE CLIENTNO TO CLIENTNO-OUT.
MOVE CLIENTNA TO CLIENTNA-OUT.
MOVE CLIENTADD TO CLIENTADD-OUT.
MOVE WS-BOOKING-TYPE (ARRAY-INDEX) TO BOOKTYPE-OUT.
MOVE WS-AVG-COST (ARRAY-INDEX) TO AVGCOST-OUT.
WRITE OUTPUT-FILE-REC FROM REPORT-LINE
AFTER ADVANCING 1 LINE.
I see you are using Stern & Stern.
The objective:
Print the average cost of a trip for each booking type. Use arrays.
means the output should contain only two columns and four rows, plus any header. For example,
Booking Type Average Cost
------------ ------------
Cruise ZZZZ9.99
Air-Independent ZZZZ9.99
Air-Tour ZZZZ9.99
Other ZZZZ9.99
To achieve that you will need to place the four descriptions in an array and accumulate the total cost and count, for each booking type, also in an array. After processing all the records, calculate the averages and print the results while looping through the array(s).
You tried to do much more than was requested!
The reason behind numbering COBOL paragraphs is so they are easier to find. In a program several thousand lines long it saves significant effort for the maintainer. You have located paragraph 1000 between paragraphs 150 and 300.
Modern COBOL programs usually have full stops at the end of a paragraph or section name and the end of a paragraph or section. Some people make the last line of a paragraph or section a CONTINUE or EXIT statement with a full stop instead of just a full stop on its own.
Modern COBOL programs use explicit scope terminators, particularly on IF statements, instead of full stops.
COBOL is criticized for being verbose; many COBOL programmers make this into a virtue by naming their paragraphs to indicate what is being done by the code contained therein. For example, 100-MOVE might be better named 100-INITIALIZE.
On Stack Overflow, I suggest you familiarize yourself with the "code sample" button, the "{}" rather than marking your paragraphs in bold.
If the point of the exercise is to compute the average cost of each type of booking, I suggest using the booking type as a subscript into an array. For each record you read, add the COSTOFTRIP to WS-TOTAL-COST(BOOKTYPE) and increment WS-TRIP-COUNT(BOOKTYPE). At end of file compute the average cost of a trip for each BOOKTYPE by using a COMPUTE for each element of the array inside a PERFORM VARYING loop.
For extra credit, verify BOOKTYPE is numeric before using it as a subscript and use an in-line PERFORM to compute the average.
I have some data as follow (column A:D contain data, column E is the sum I created):
NO
SE
Date Country ID Value Sum
30-01-2014 SE B-08888 10 10
05-02-2014 SE B-08888 23
06-02-2014 SE B-08888 20
13-05-2014 SE B-08888 17 27
14-05-2014 SE B-08888 10
13-05-2014 NO A-07777 15 35
14-05-2014 NO A-07777 20
I would like to sum all values that are having same country and same ID when: 1) the date is greater than 1/5; and 2) when date is less than 1/5.
I am using the SUMIFS. But the SUMIFS doesn't give correct results when I included the date argument which is less than 1/5.
=SUMIFS($D$5:$D$11;$A$5:$A$11;"<="&DATE(2014;5;1);$B$5:$B$11;A2;$C$5:$C$11;C5) ==> gives incorrect result (=10)
=SUMIFS($D$5:$D$11;$A$5:$A$11;">="&DATE(2014;5;1);$B$5:$B$11;A2;$C$5:$C$11;C8) ==> gives correct result (=27)
Is there a way I can take into account both date conditions (i.e. date greater than and less than 1/5) and make the formula general so I don't have to go through every cell to change reference?
Thank you.
Using your data, the second formula returns 27 for me - so I assume the cell references you have not mentioned are as I have guessed. The first formula for me returns 53 - I suspect the result you want, though have not mentioned.
Something is wrong with your data (not the formulae). The most likely cause is that there is a trailing space in C6 and C7 that is not in C5. Copying C5 down to C9 should fix that. There might however be a data issue in other cells in those two rows.
It might make things easier for you if the formulae were in separate columns.
I have an array of objects with time and value property. Looks something like this.
UPDATE: dataset with epoch times rather than time strings
[{datetime:1383661634, value: 43},{datetime:1383661856, value: 40}, {datetime:1383662133, value: 23}, {datetime:1383662944, value: 23}]
The array is far larger than this. Possibly a 6 digit length. I intend to build a graph to represent this array. Due to obvious reasons, I cannot use every bit of the data to build this graph (value vs time); so I need to normalize it across time.
So here's the main problem - There is no trend in the timestamp for these objects; so I need to dynamically choose slots of time in which I either average out the values or show counts of objects in that slot.
How can I calculate slots that user friendly. i.e per minute, hour, day, eight hours or so. I am looking at having a maximum of 25 slots done out of the array, which I show up on the graph.
I hope this helps get my point through.
You can convert the date/time into epoch and use numpy.histogram to get the ranges:
import random, numpy
l = [ random.randint(0, 1000) for x in range(1000) ]
num_items_bins, bin_ranges = numpy.histogram(l, 25)
print num_items_bins
print bin_ranges
Gives:
[34 38 42 41 43 50 34 29 37 46 31 47 43 29 30 42 38 52 42 44 42 42 51 34 39]
[ 1. 40.96 80.92 120.88 160.84 200.8 240.76 280.72
320.68 360.64 400.6 440.56 480.52 520.48 560.44 600.4
640.36 680.32 720.28 760.24 800.2 840.16 880.12 920.08
960.04 1000. ]
Hard to say without knowing the nature of your values, compressing values for display is a matter of what you can afford to discard and what you can't. Some ideas though:
histogram
candlestick chart
Is this JSON and the DateTimes transmitted as text?
Why not transmit the Date as a long (Int64), and use a method to convert to/from DateTime? Depending on which language you could use these implementations:
DateTime to Long in C#
Date to long using Unix timestamp in Java
That alone would save you a considerable amount of space, since strings are 16-bits per character and the long TimeStamp would be just 64 bits.
I am a new comer to programming in COBOL and I am having difficulty at something that probably should be trivial. I am wanting to find the min and max of what the user enters. When the user hits 0 the max, min, and avg should display. The avg is easy but the min and max had me wondering. If this was JAVA or another lang I'd just do some scenario with comparing MAX INT value. Unfortunately High-Value and Low-Value in COBOL are not Integer values???? So I decided to put the user's entries in a table and then use the intrinsic functions to do what I need. However as soon as I attempt to compute like this:
compute Min-Result = Function Min (Num-Field(ALL))
I get an error that says, "syntax error, unexpected all." I am totally confused at this point on what to do and why I get this error. I am using OpenCOBOL 1.1 Mingw. Here is my complete code. Any help would be greatly appreciated. Anything at all. I also made sure no lines were over 72.
identification division.
program-id. lab1a.
* no envionrment division since there are no files needed, etc.
data division.
working-storage section.
* declaring proper variables to store integer values
01 Max-Result PIC S9(5).
01 Min-Result PIC S9(5).
01 Count-Val PIC 9 Value 0.
01 Running-Tot PIC S9(10)v99.
01 First-Zero PIC 9 Value 1.
01 Final-Format-Avg PIC ZZZZZ9.9999.
01 Avg-Ent PIC S9(5)v9999.
01 Calc-Table.
03 Table-Record Occurs 1 to 500 times
depending on Entered-Num.
05 Num-Field PIC S9(5).
01 Entered-Num PIC S9(5).
procedure division.
000-Main.
perform with test after until Entered-Num = 0
display "Enter a 4-digit number (0 to stop): "
with no advancing
accept Entered-Num
add 1 to Count-Val
add Entered-Num to Running-Tot
display Running-Tot
display Count-Val
move Entered-Num to Num-Field(Count-Val)
* this way every time the user enters a non zero number it will be re-assigned
* to the variable Ending-Num. If they enter zero the if condition is skipped, the
* loop condition is tested at the top and is ended.
end-perform.
subtract 1 from Count-Val
display Count-Val
display " "
display " "
*WATCH FOR TRUNCATION ERROR.....
Divide Running-Tot By Count-Val Giving Avg-Ent
move Avg-Ent to Final-Format-Avg
*******WHY DOES THIS NOT WORK???????***********************
compute Min-Result = Function Min (Num-Field(ALL))
compute Max-Result = Function Max (Num-Field(ALL))
if First-Zero = 0
display "The first number you entered was zero.
& Next time enter a different one."
else
display "The lowest value entered: " Min-Result
display "The highest value entered: " Max-Result
display "The average value entered: "
Final-Format-Avg
end-if
stop run.
ALL is not currently supported for OpenCOBOL intrinsics, which is a feature that is on the the books for implementation.
You have "Entered-Num" as your Occurs Depending On field. Entered-Num by the time you use the function is zero. It should be Count-Val.
That's not the problem, but you asked.
Look at a 2009 OpenCobol Programmer's Guid, I can find no confirmation that ALL is supported.
It will be much simpler/faster to keep a "lowest value" and a "highest value" and compare to/replace as necessary with the entered number.