How can I implement a loop that will display subsequent months (01/01/2018, 01/02/2018 etc.) x-times? Additionally, how can I set day,month,year as a variable? By the way, I'm new to Cobol.
This is my code I wrote so far
01 YYYYMMDD Pic 9(8).
01 Integer-Form Pic S9(9).
Move Function Current-Date(1:8) to YYYYMMDD
Compute Integer-Form = Function Integer-of-Date(YYYYMMDD)
Add 12 to Integer-Form
Compute YYYYMMDD = Function Date-of-Integer(Integer-Form)
Display 'Date: ' YYYYMMDD.
EDIT!
PERFORM VARYING Number-Periods FROM 0 BY 1 UNTIL Number-Periods > 36
DISPLAY ws-current-day, "/", ws-current-month, "/", ws-current-year
ADD 1 TO WS-current-month
IF ws-current-month > 12 THEN
COMPUTE ws-current-month = 1
ADD 1 TO WS-current-year
END-IF
END-PERFORM
Best to use redefines and 88 to check month:
01 YYYYMMDD PIC 9(8).
01 YYYYMMDD-R REDEFINES YYYYMMDD.
05 YYYY PIC 9(4).
05 MM PIC 9(2).
88 VALID-MONTH VALUE 1 2 3 4 5 6
7 8 9 10 11 12.
05 DD PIC 9(2).
Then you can use the 88 in the check (within the PERFORM VARYING as you discovered):
IF NOT VALID-MONTH
... increment year
Related
1) Data I have a following dataset in google sheets link. In the sheets sample I have only 4 months of data but normally there would be many, many more to come in the future.
MONTH
DATE
KATEGORIE
DOWNTIME
TIME (min)
9
01/09/2021
01 DURCHLAUF
0
50
9
02/09/2021
01 DURCHLAUF
0
65
9
03/09/2021
01 DURCHLAUF
0
91
9
04/09/2021
01 DURCHLAUF
0
52
9
05/09/2021
01 DURCHLAUF
0
72
9
06/09/2021
01 DURCHLAUF
0
44
9
07/09/2021
01 DURCHLAUF
0
55
9
08/09/2021
01 DURCHLAUF
0
30
9
09/09/2021
01 DURCHLAUF
0
42
2) Expected output table and desired output
I want to create a scorecard for 02 Downtime to show total time for a given month.
If I filter for November, I would like the scorecard to compare vs October. (abs=1180, %=45)
Similarly, if I select December, I want to see the amount vs November (abs=940, %=25)
As a safety measure, if someone selects 2 months simultaneously, then perhaps it should not show any comparison. (unless it's possible to even do 2 vs 2 months, but it's not a necessity.)
3) Chart: Configuration + Setup
I have created a simple scorecard and a pivot table. I filtered out only Downtime.
4) Issue: Attempt at solving + Output and 5) Report: Publicly editable Looker Studio with 1-4.
In my file link you see the mentioned scorecard but I fail to include any comparison that is kind of "dynamic" that changes the month in question.
Added solution to your dashboard page 2
Cross join the datasource with itself
use the following calculated fields for expected output.
Daet01:
CAST(CONCAT(DATE(EXTRACT(YEAR FROM DATE123),EXTRACT(MONTH FROM DATE123),01)) AS DATE)
Previous Month:
TIME (min) (Table 1)-CASE WHEN Daet01 (Table 2) = DATETIME_SUB(Daet01 (Table 1), INTERVAL 1 MONTH) THEN TIME (min) (Table 2) ELSE 0 END
% Difference:
(TIME (min) (Table 1)-CASE WHEN Daet01 (Table 2) = DATETIME_SUB(Daet01 (Table 1), INTERVAL 1 MONTH) THEN TIME (min) (Table 2) ELSE 0 END)/TIME (min) (Table 2)
-
Not very difficult:
#!/bin/bash
hr=(00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23)
for i in ${hr[#]}; do
echo ${hr[i]}
done
But:
user#userver:$ ./stat.sh
00
01
02
03
04
05
06
07
./stat.sh: string 7: 08: value too great for base (error token is "08")
Bash thinks that leading zero means octal number system. What to do?
I think your fundamental problem is that you're using each element of the array to index the array itself.
If you want to just print out the array elements, you should be outputting ${i} rather than ${hr[i]}. The latter is useless in your current code since element zero is 00, element one is 01, and so on.
On the chance that this is a simplified example and you do want to reference a different array based on the content of this one, you have a couple of options:
Realise that the value of an integer and the presentation of it are two distinct things. In other words, use 0, 1, 2, ... but something like printf "%02d" $i if you need to output it (noting this is only ouputting the value in the first array, not the one you're looking up things in).
Exclusively use strings and a string-based associative array rather than an integer-based one, see typeset -A for detail.
Use brace expansion (ranges, repetition).
To create an array:
hours=({00..23})
Loop through it:
for i in "${hours[#]}"; do
echo "$i"
done
Or loop through a brace expansion:
for i in {00..23}; do
echo "$i"
done
Regarding your error, it's because in bash arithmetic, all numbers with leading zeroes are treated as octals, and 08 and 09 are invalid octal numbers. All indexed array subscripts are evaluated as arithmetic expressions. You can fix the problem by using the notation base#number to specify a number system. So for base 10: 10#09, or for i=09, 10#$i. The variable must be prefixed with $, 10#i does not work.
You should be printing your array like this anyway:
Loop through elements:
for i in "${hr[#]}"; do
echo "$i"
done
Loop through indexes:
for i in "${!hr[#]}"; do
echo "index is $i"
echo "element is ${hr[i]}"
done
If you need to do arithmetic on the hours, or any zero padded number, you will lose the zero padding. You can print it again with printf: printf %.2d "$num", where 2 is the minimum width.
When you iterate with:
for i in ${hr[#]}; do
It is iterating the values of the array witch are:
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23
But when within the loop it has:
echo ${hr[i]}
it is using i as the index of the hr array.
In Bash, an index within the brackets of an array like [i] is an arithmetic context. It means while the value of i=08 the leading 0 within the arithmetic context causes the number to be treated as an octal number, and 8 is an invalid octal number.
If you wanted to iterate your array indexes to process its values by index, then you'd start the loop as:
for i in "${!hr[#]}"; do
This one will perfectly work as it iterates the index into the variable i :
#!/usr/bin/env bash
declare -a hr=(00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23)
for i in "${!hr[#]}"; do
printf '%s\n' "${hr[i]}"
done
Now if all you want is iterate the values of the hr array, just do this way:
#!/usr/bin/env bash
declare -a hr=(00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23)
for e in "${hr[#]}"; do
printf '%s\n' "$e"
done
No need to index the array within the loop, since the elements are already expanded into e.
You can specify it's 10-based :
echo ${hr[10#$i]}
I got a problem when analyzing dataset about combining string together.
The data frame looks like the one below:
IP Event
01 check
01 redo
01 view
02 check
02 check
03 review
04 delete
As you can see, the IP contains duplicates. My question is, how can I get the results of combining the Event group by each IP in order.For example, the result I'm looking for is:
IP result
01 check->redo->view
02 check->check
03 review
04 delete
try this:
In [27]: df.groupby('IP').agg('->'.join).reset_index()
Out[27]:
IP Event
0 01 check->redo->view
1 02 check->check
2 03 review
3 04 delete
or
In [26]: df.groupby('IP').agg('->'.join)
Out[26]:
Event
IP
01 check->redo->view
02 check->check
03 review
04 delete
Try this with lambda:
df.groupby("IP")['Event'].apply(lambda x: '->'.join(x)).reset_index()
# IP Event
# 0 1 check->redo->view
# 1 2 check->check
# 2 3 review
# 3 4 delete
I have raw data like this
time ID01 ID02 ID03 ~ IDxx
0 10 11 xx
0.5 20 12 xx
1 29 25 xx
1.5 41 30 xx
2 50 40 xx
3 30 50 xx
4 40 42 xx
. . .
. . .
. . .
I want to make it to this form
x time temp.
01 0 10
01 0.5 20
01 1 29
01 1.5 41
01 2 50
01 3 30
01 4 40
02 0 11
02 0.5 12
02 1 25
02 1.5 30
02 2 40
02 3 50
02 4 42
I used array statement and proc transpose
but I can't repeat time variable beside temp.
It works using arrays. Just write an output within the loop and time will be written in your output datatset, and then sort.
data output;
set input;
array ID(*) ID01-ID03;
do i=1 to 3;
X=put(i,z2.);
temp=ID(i);
output;
end;
keep time X temp;
run;
proc sort data=output;
by X time;
run;
I'll start by admitting this is for my homework and I don't expect anything specific just a tip perhaps. The input file is just one 30 byte field that contains names. The output file is two fields 30 bytes each. I'll list the code to show what I mean by this. The program needs to read the input file putting the names into an array and then print them to the two fields in the output file. It would be simple enough if the out put file was like this:
name 1 name 2
name 3 name 4
etc...
but it's supposed to be:
name 1 name 55
name 2 name 56
....
name 54 name 108
I'm not quite understanding how I can code the program to do this without having 54 lines of code (1 for each line in the output). Well here's the code I have so far:
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT NAMELIST-FILE-IN
ASSIGN TO 'NAMELIST.SEQ'
ORGANIZATION IS LINE SEQUENTIAL.
SELECT NAMELIST-FILE-OUT
ASSIGN TO 'NAMELIST.RPT'
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD NAMELIST-FILE-IN.
01 NAME-IN PIC X(30).
FD NAMELIST-FILE-OUT.
01 NAME-OUT PIC X(60).
WORKING-STORAGE SECTION.
01 ARE-THERE-MORE-RECORDS PIC X(3) VALUE 'YES'.
01 PAGE-CTR PIC 99 VALUE ZERO.
01 SUB PIC 999 VALUE 1.
01 LEFT-NAME PIC 99 VALUE 54.
01 RIGHT-NAME PIC 9(3) VALUE 108.
01 WS-DATE.
05 RUN-YEAR PIC XX.
05 RUN-MONTH PIC XX.
05 RUN-DAY PIC XX.
01 HEADING-LINE.
05 PIC X(15) VALUE SPACES.
05 PIC X(20)
VALUE 'NAME LIST REPORT'.
05 HL-DATE.
10 DAY-HL PIC XX.
10 PIC X VALUE '/'.
10 MONTH-HL PIC XX.
10 PIC X VALUE '/'.
10 YEAR-HL PIC XX.
05 PIC X(3) VALUE SPACES.
05 PIC X(5) VALUE 'PAGE'.
05 PAGE-NUMBER-HL PIC Z9 VALUE 1.
01 DETAIL-LINE.
05 NAME-LEFT PIC X(30).
05 NAME-RIGHT PIC X(30).
01 NAME-ARRAY.
05 NAME-X OCCURS 108 PIC X(30).
PROCEDURE DIVISION.
100-MAIN.
OPEN INPUT NAMELIST-FILE-IN
OPEN OUTPUT NAMELIST-FILE-OUT
ACCEPT WS-DATE FROM DATE.
MOVE RUN-MONTH TO MONTH-HL
MOVE RUN-DAY TO DAY-HL
MOVE RUN-YEAR TO YEAR-HL
PERFORM 200-ACCEPT-INPUT
CLOSE NAMELIST-FILE-IN
CLOSE NAMELIST-FILE-OUT
STOP RUN.
200-ACCEPT-INPUT.
PERFORM UNTIL SUB > 108
READ NAMELIST-FILE-IN
MOVE NAME-IN TO NAME-X (SUB)
ADD 1 TO SUB
END-PERFORM
PERFORM 300-PRINT-ONE-PAGE.
300-PRINT-ONE-PAGE.
WRITE NAME-OUT FROM HEADING-LINE
AFTER ADVANCING PAGE
ADD 1 TO PAGE-CTR
Here's the final code for this program if anyone is interested in seeing it.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT NAMELIST-FILE-IN
ASSIGN TO 'NAMELIST.SEQ'
ORGANIZATION IS LINE SEQUENTIAL.
SELECT NAMELIST-FILE-OUT
ASSIGN TO 'NAMELIST.RPT'
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD NAMELIST-FILE-IN.
01 NAME-IN PIC X(30).
FD NAMELIST-FILE-OUT.
01 NAME-OUT PIC X(80).
WORKING-STORAGE SECTION.
01 ARE-THERE-MORE-RECORDS PIC X(3) VALUE 'YES'.
01 PAGE-CTR PIC 99 VALUE ZERO.
01 SUB PIC 999.
01 SUB2 PIC 999.
01 LEFT-NAME PIC 99 VALUE 54.
01 RIGHT-NAME PIC 9(3) VALUE 108.
01 WS-DATE.
05 RUN-YEAR PIC XX.
05 RUN-MONTH PIC XX.
05 RUN-DAY PIC XX.
01 HEADING-LINE.
05 PIC X(26) VALUE SPACES.
05 PIC X(35)
VALUE 'NAME LIST REPORT'.
05 HL-DATE.
10 DAY-HL PIC XX.
10 PIC X VALUE '/'.
10 MONTH-HL PIC XX.
10 PIC X VALUE '/'.
10 YEAR-HL PIC XX.
05 PIC X(3) VALUE SPACES.
05 PIC X(5) VALUE 'PAGE'.
05 PAGE-NUMBER-HL PIC Z9.
01 DETAIL-LINE.
05 NAME-LEFT PIC X(40).
05 NAME-RIGHT PIC X(40).
01 NAME-ARRAY.
05 NAME-X OCCURS 108 PIC X(30).
PROCEDURE DIVISION.
100-MAIN.
OPEN INPUT NAMELIST-FILE-IN
OPEN OUTPUT NAMELIST-FILE-OUT
ACCEPT WS-DATE FROM DATE.
MOVE RUN-MONTH TO MONTH-HL
MOVE RUN-DAY TO DAY-HL
MOVE RUN-YEAR TO YEAR-HL
PERFORM UNTIL ARE-THERE-MORE-RECORDS = 'NO'
PERFORM 200-ACCEPT-INPUT
END-PERFORM
CLOSE NAMELIST-FILE-IN
CLOSE NAMELIST-FILE-OUT
STOP RUN.
200-ACCEPT-INPUT.
INITIALIZE NAME-ARRAY
MOVE 1 TO SUB
PERFORM UNTIL SUB > 108 OR ARE-THERE-MORE-RECORDS = 'NO'
READ NAMELIST-FILE-IN
AT END
MOVE 'NO' TO ARE-THERE-MORE-RECORDS
MOVE SPACES TO NAME-IN
END-READ
MOVE NAME-IN TO NAME-X (SUB)
ADD 1 TO SUB
END-PERFORM
PERFORM 300-PRINT-ONE-PAGE.
300-PRINT-ONE-PAGE.
ADD 1 TO PAGE-CTR
MOVE PAGE-CTR TO PAGE-NUMBER-HL
WRITE NAME-OUT FROM HEADING-LINE
AFTER ADVANCING PAGE
MOVE SPACES TO DETAIL-LINE
WRITE NAME-OUT FROM DETAIL-LINE
AFTER ADVANCING 1
PERFORM VARYING SUB FROM 1 BY 1 UNTIL SUB > 54
MOVE NAME-X (SUB) TO NAME-LEFT
COMPUTE SUB2 = SUB + 54
MOVE NAME-X (SUB2) TO NAME-RIGHT
WRITE NAME-OUT FROM DETAIL-LINE
AFTER ADVANCING 1
END-PERFORM.
I must apologize, I cannot think of a way to guide you without giving away the answer. I guess this is a spoiler alert.
One possible method you could use would be to add a variable SUB2 to Working-Storage and...
Perform Varying SUB From 1 By 1 Until SUB > 54
Move NAME-X(SUB) to NAME-LEFT
Compute SUB2 = SUB + 54
MOVE NAME-X(SUB2) to NAME-RIGHT
Write NAME-OUT from DETAIL-LINE After Advancing 1 Line
End-Perform
This is kind of kludgy and ties you to an array of 108 elements. You could use a record counter that you increment by 1 for each record read and then compute the values I show hardcoded in the sample above (54 is simply half of 108).
I don't show the page break logic, so perhaps I didn't give the whole show away.
I hope this helps you.
I would have 2 arrays.
One containing the whole file.
01 DETAIL-LINE-ARRAY.
02 DETAIL-LINE OCCURS 54.
05 NAME-LEFT PIC X(30).
05 NAME-RIGHT PIC X(30).
Another like you did with NAME-ARRAY
Then I would populate first the DETAIL-LINE-ARRAY.
I would read twice DETAIL-LINE-ARRAY to fill NAME-ARRAY
Then you can read sequentially NAME-ARRAY
==========================================================================
Another solution:
You can read the file twice. While the first read, you take care only of the left name and populate NAME-ARRAY.
While the second reading, you take care only of the right name and continue to populate NAME-ARRAY.
After the first and second read, you can read your array NAME-ARRAY.
==========================================================================
There is a problem with both last solutions : you have to know how much lines contains your file. Yep, you can have dynamic programming in cobol too :-)
You have to use SORT.
In FILE SECTION add
SD SORT-WORK
01 SORT-RECORD.
05 SR-ORDER PIC 9(09).
05 SR-NAME PIC X(30).
In your PROCEDURE DIVISION add (in pseude-code, you'll need to add some variables in your working storage section.
SORT SORT-WORK
ASCENDING SORT-ORDER
INPUT PROCEDURE IS 1000-INPUT
OUTPUT PROCEDURE IS 2000-OUTPUT
1000-INPUT SECTION.
MOVE 0 TO I.
PERFORM until end-of-file of NAMELIST-FILE-IN
ADD 1 TO I
READ left-name
MOVE I TO SORT-ORDER
MOVE left-name TO SR-NAME
* This operation populates the sort file...
RELEASE SORT-RECORD
END-PERFORM.
PERFORM until end-of-file of NAMELIST-FILE-IN
ADD 1 TO I
READ right-name
MOVE I TO SORT-ORDER
MOVE right-name TO SR-NAME
* This operation populates the sort file...
RELEASE SORT-RECORD
END-PERFORM.
MOVE I TO WS-NB-NAMES.
2000-OUTPUT SECTION.
PERFORM VARYING I FROM 1 BY 1 UNTIL I > WS-NB-NAMES
* This operation returns the "next" record. It begins by the first, second...
RETURN SORT-RECORD
MOVE SR-NAME TO NAME-LEFT
RETURN SORT-RECORD
MOVE SR-NAME TO NAME-RIGHT
WRITE NAMELIST-FILE-OUT FROM DETAIL-LINE
END-PERFORM.
You have some example here for SORT
Have fun !