@sasjs/utils 2.49.0 → 2.49.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fs/internal/helper.js +19 -24
- package/fs/internal/helper.js.map +1 -1
- package/macros/mf_abort.sas +27 -0
- package/macros/mf_dedup.sas +53 -0
- package/macros/mf_deletefile.sas +31 -0
- package/macros/mf_existds.sas +30 -0
- package/macros/mf_existfeature.sas +59 -0
- package/macros/mf_existfileref.sas +33 -0
- package/macros/mf_existfunction.sas +37 -0
- package/macros/mf_existvar.sas +39 -0
- package/macros/mf_existvarlist.sas +56 -0
- package/macros/mf_fmtdttm.sas +42 -0
- package/macros/mf_getapploc.sas +78 -0
- package/macros/mf_getattrc.sas +34 -0
- package/macros/mf_getattrn.sas +34 -0
- package/macros/mf_getengine.sas +54 -0
- package/macros/mf_getfilesize.sas +64 -0
- package/macros/mf_getfmtlist.sas +61 -0
- package/macros/mf_getfmtname.sas +44 -0
- package/macros/mf_getkeyvalue.sas +32 -0
- package/macros/mf_getplatform.sas +75 -0
- package/macros/mf_getquotedstr.sas +57 -0
- package/macros/mf_getschema.sas +43 -0
- package/macros/mf_getuniquefileref.sas +60 -0
- package/macros/mf_getuniquelibref.sas +57 -0
- package/macros/mf_getuniquename.sas +22 -0
- package/macros/mf_getuser.sas +44 -0
- package/macros/mf_getvalue.sas +36 -0
- package/macros/mf_getvarcount.sas +49 -0
- package/macros/mf_getvarformat.sas +72 -0
- package/macros/mf_getvarlen.sas +56 -0
- package/macros/mf_getvarlist.sas +74 -0
- package/macros/mf_getvarnum.sas +58 -0
- package/macros/mf_getvartype.sas +52 -0
- package/macros/mf_getxengine.sas +43 -0
- package/macros/mf_increment.sas +29 -0
- package/macros/mf_isblank.sas +27 -0
- package/macros/mf_isdir.sas +34 -0
- package/macros/mf_isint.sas +36 -0
- package/macros/mf_islibds.sas +40 -0
- package/macros/mf_loc.sas +29 -0
- package/macros/mf_mkdir.sas +67 -0
- package/macros/mf_mval.sas +19 -0
- package/macros/mf_nobs.sas +26 -0
- package/macros/mf_trimstr.sas +50 -0
- package/macros/mf_uid.sas +21 -0
- package/macros/mf_verifymacvars.sas +72 -0
- package/macros/mf_wordsinstr1andstr2.sas +53 -0
- package/macros/mf_wordsinstr1butnotstr2.sas +54 -0
- package/macros/mf_writefile.sas +67 -0
- package/macros/mp_abort.sas +301 -0
- package/macros/mp_appendfile.sas +57 -0
- package/macros/mp_applyformats.sas +181 -0
- package/macros/mp_assert.sas +56 -0
- package/macros/mp_assertcols.sas +145 -0
- package/macros/mp_assertcolvals.sas +168 -0
- package/macros/mp_assertdsobs.sas +121 -0
- package/macros/mp_assertscope.sas +147 -0
- package/macros/mp_base64copy.sas +117 -0
- package/macros/mp_binarycopy.sas +79 -0
- package/macros/mp_chop.sas +194 -0
- package/macros/mp_cleancsv.sas +78 -0
- package/macros/mp_cntlout.sas +85 -0
- package/macros/mp_copyfolder.sas +84 -0
- package/macros/mp_coretable.sas +73 -0
- package/macros/mp_createconstraints.sas +70 -0
- package/macros/mp_createwebservice.sas +32 -0
- package/macros/mp_csv2ds.sas +140 -0
- package/macros/mp_deleteconstraints.sas +52 -0
- package/macros/mp_deletefolder.sas +70 -0
- package/macros/mp_dictionary.sas +52 -0
- package/macros/mp_dirlist.sas +263 -0
- package/macros/mp_distinctfmtvalues.sas +50 -0
- package/macros/mp_dropmembers.sas +46 -0
- package/macros/mp_ds2cards.sas +291 -0
- package/macros/mp_ds2csv.sas +212 -0
- package/macros/mp_ds2ddl.sas +30 -0
- package/macros/mp_ds2fmtds.sas +105 -0
- package/macros/mp_ds2inserts.sas +180 -0
- package/macros/mp_ds2md.sas +103 -0
- package/macros/mp_ds2squeeze.sas +120 -0
- package/macros/mp_dsmeta.sas +110 -0
- package/macros/mp_filtercheck.sas +251 -0
- package/macros/mp_filtergenerate.sas +105 -0
- package/macros/mp_filterstore.sas +254 -0
- package/macros/mp_filtervalidate.sas +105 -0
- package/macros/mp_getcols.sas +67 -0
- package/macros/mp_getconstraints.sas +115 -0
- package/macros/mp_getdbml.sas +335 -0
- package/macros/mp_getddl.sas +400 -0
- package/macros/mp_getformats.sas +124 -0
- package/macros/mp_getmaxvarlengths.sas +130 -0
- package/macros/mp_getpk.sas +281 -0
- package/macros/mp_gsubfile.sas +58 -0
- package/macros/mp_guesspk.sas +332 -0
- package/macros/mp_hashdataset.sas +91 -0
- package/macros/mp_hashdirectory.sas +162 -0
- package/macros/mp_include.sas +106 -0
- package/macros/mp_init.sas +76 -0
- package/macros/mp_jsonout.sas +406 -0
- package/macros/mp_lib2cards.sas +78 -0
- package/macros/mp_lib2inserts.sas +77 -0
- package/macros/mp_loadformat.sas +303 -0
- package/macros/mp_lockanytable.sas +243 -0
- package/macros/mp_lockfilecheck.sas +103 -0
- package/macros/mp_makedata.sas +107 -0
- package/macros/mp_md5.sas +58 -0
- package/macros/mp_perflog.sas +43 -0
- package/macros/mp_prevobs.sas +88 -0
- package/macros/mp_recursivejoin.sas +90 -0
- package/macros/mp_replace.sas +151 -0
- package/macros/mp_reseterror.sas +27 -0
- package/macros/mp_resetoption.sas +39 -0
- package/macros/mp_retainedkey.sas +247 -0
- package/macros/mp_runddl.sas +49 -0
- package/macros/mp_searchcols.sas +103 -0
- package/macros/mp_searchdata.sas +131 -0
- package/macros/mp_setkeyvalue.sas +55 -0
- package/macros/mp_sortinplace.sas +122 -0
- package/macros/mp_stackdiffs.sas +592 -0
- package/macros/mp_storediffs.sas +230 -0
- package/macros/mp_stprequests.sas +74 -0
- package/macros/mp_streamfile.sas +212 -0
- package/macros/mp_testjob.sas +92 -0
- package/macros/mp_testservice.sas +39 -0
- package/macros/mp_testwritespeedlibrary.sas +59 -0
- package/macros/mp_tree.sas +71 -0
- package/macros/mp_unzip.sas +90 -0
- package/macros/mp_updatevarlength.sas +93 -0
- package/macros/mp_validatecol.sas +97 -0
- package/macros/mp_wait4file.sas +35 -0
- package/macros/mp_webin.sas +59 -0
- package/macros/mp_zip.sas +85 -0
- package/package.json +4 -1
- package/scripts/copySASjsCore.ts +36 -0
- package/utils/index.d.ts +1 -2
- package/utils/index.js +2 -3
- package/utils/index.js.map +1 -1
- package/utils/utils.d.ts +1 -0
- package/utils/utils.js +7 -1
- package/utils/utils.js.map +1 -1
- package/utils/getNodeModulePath.d.ts +0 -2
- package/utils/getNodeModulePath.js +0 -83
- package/utils/getNodeModulePath.js.map +0 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Create sample data based on the structure of an empty table
|
|
4
|
+
@details Many SAS projects involve sensitive datasets. One way to _ensure_
|
|
5
|
+
the data is anonymised, is never to receive it in the first place! Often
|
|
6
|
+
consultants are provided with empty tables, and expected to create complex
|
|
7
|
+
ETL flows.
|
|
8
|
+
|
|
9
|
+
This macro can help by taking an empty table, and populating it with data
|
|
10
|
+
according to the variable types and formats.
|
|
11
|
+
|
|
12
|
+
TODO:
|
|
13
|
+
@li Consider dates, datetimes, times, integers etc
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
|
|
17
|
+
proc sql;
|
|
18
|
+
create table work.example(
|
|
19
|
+
TX_FROM float format=datetime19.,
|
|
20
|
+
DD_TYPE char(16),
|
|
21
|
+
DD_SOURCE char(2048),
|
|
22
|
+
DD_SHORTDESC char(256),
|
|
23
|
+
constraint pk primary key(tx_from, dd_type,dd_source),
|
|
24
|
+
constraint nnn not null(DD_SHORTDESC)
|
|
25
|
+
);
|
|
26
|
+
%mp_makedata(work.example)
|
|
27
|
+
|
|
28
|
+
@param [in] libds The empty table (libref.dataset) in which to create data
|
|
29
|
+
@param [out] obs= (500) The maximum number of records to create. The table
|
|
30
|
+
is sorted with nodup on the primary key, so the actual number of records may
|
|
31
|
+
be lower than this.
|
|
32
|
+
|
|
33
|
+
<h4> SAS Macros </h4>
|
|
34
|
+
@li mf_getuniquename.sas
|
|
35
|
+
@li mf_getvarlen.sas
|
|
36
|
+
@li mf_getvarlist.sas
|
|
37
|
+
@li mf_islibds.sas
|
|
38
|
+
@li mf_nobs.sas
|
|
39
|
+
@li mp_getcols.sas
|
|
40
|
+
@li mp_getpk.sas
|
|
41
|
+
|
|
42
|
+
<h4> Related Macros </h4>
|
|
43
|
+
@li mp_makedata.test.sas
|
|
44
|
+
|
|
45
|
+
@version 9.2
|
|
46
|
+
@author Allan Bowe
|
|
47
|
+
|
|
48
|
+
**/
|
|
49
|
+
|
|
50
|
+
%macro mp_makedata(libds
|
|
51
|
+
,obs=500
|
|
52
|
+
,seed=1
|
|
53
|
+
)/*/STORE SOURCE*/;
|
|
54
|
+
|
|
55
|
+
%local ds1 ds2 lib ds pk_fields i col charvars numvars ispk;
|
|
56
|
+
|
|
57
|
+
%if %mf_islibds(&libds)=0 %then %do;
|
|
58
|
+
%put &sysmacroname: Invalid libds (&libds) - should be library.dataset format;
|
|
59
|
+
%return;
|
|
60
|
+
%end;
|
|
61
|
+
%else %if %mf_nobs(&libds)>0 %then %do;
|
|
62
|
+
%put &sysmacroname: &libds has data, it will not be recreated;
|
|
63
|
+
%return;
|
|
64
|
+
%end;
|
|
65
|
+
|
|
66
|
+
/* set up temporary vars */
|
|
67
|
+
%let ds1=%mf_getuniquename(prefix=mp_makedatads1);
|
|
68
|
+
%let ds2=%mf_getuniquename(prefix=mp_makedatads2);
|
|
69
|
+
%let lib=%scan(&libds,1,.);
|
|
70
|
+
%let ds=%scan(&libds,2,.);
|
|
71
|
+
|
|
72
|
+
/* grab the primary key vars */
|
|
73
|
+
%mp_getpk(&lib,ds=&ds,outds=&ds1)
|
|
74
|
+
|
|
75
|
+
proc sql noprint;
|
|
76
|
+
select coalescec(pk_fields,'_all_') into: pk_fields from &ds1;
|
|
77
|
+
|
|
78
|
+
data &ds2;
|
|
79
|
+
if 0 then set &libds;
|
|
80
|
+
do _n_=1 to &obs;
|
|
81
|
+
%let charvars=%mf_getvarlist(&libds,typefilter=C);
|
|
82
|
+
%if &charvars ^= %then %do i=1 %to %sysfunc(countw(&charvars));
|
|
83
|
+
%let col=%scan(&charvars,&i);
|
|
84
|
+
/* create random value based on observation number and colum length */
|
|
85
|
+
&col=repeat(put(md5(cats(_n_)),$hex32.),%mf_getvarlen(&libds,&col)/32);
|
|
86
|
+
%end;
|
|
87
|
+
|
|
88
|
+
%let numvars=%mf_getvarlist(&libds,typefilter=N);
|
|
89
|
+
%if &numvars ^= %then %do i=1 %to %sysfunc(countw(&numvars));
|
|
90
|
+
%let col=%scan(&numvars,&i);
|
|
91
|
+
&col=_n_;
|
|
92
|
+
%end;
|
|
93
|
+
output;
|
|
94
|
+
end;
|
|
95
|
+
stop;
|
|
96
|
+
run;
|
|
97
|
+
proc sort data=&ds2 nodupkey;
|
|
98
|
+
by &pk_fields;
|
|
99
|
+
run;
|
|
100
|
+
|
|
101
|
+
proc append base=&libds data=&ds2;
|
|
102
|
+
run;
|
|
103
|
+
|
|
104
|
+
proc sql;
|
|
105
|
+
drop table &ds1, &ds2;
|
|
106
|
+
|
|
107
|
+
%mend mp_makedata;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Generates an md5 expression for hashing a set of variables
|
|
4
|
+
@details This is the same algorithm used to hash records in
|
|
5
|
+
[Data Controller for SAS](https://datacontroller.io) (free for up
|
|
6
|
+
to 5 users).
|
|
7
|
+
|
|
8
|
+
It is not designed to be efficient - it is designed to be effective,
|
|
9
|
+
given the range of edge cases (large floating points, special missing
|
|
10
|
+
numerics, thousands of columns, very wide columns).
|
|
11
|
+
|
|
12
|
+
It can be used only in data step, eg as follows:
|
|
13
|
+
|
|
14
|
+
data _null_;
|
|
15
|
+
set sashelp.class;
|
|
16
|
+
hashvar=%mp_md5(cvars=name sex, nvars=age height weight);
|
|
17
|
+
put hashvar=;
|
|
18
|
+
run;
|
|
19
|
+
|
|
20
|
+
Unfortunately it will not run in SQL - it fails with the following message:
|
|
21
|
+
|
|
22
|
+
> The width value for HEX is out of bounds. It should be between 1 and 16
|
|
23
|
+
|
|
24
|
+
The macro will also cause errors if the data contains (non-special) missings
|
|
25
|
+
and the (undocumented) `options dsoptions=nonote2err;` is in effect.
|
|
26
|
+
|
|
27
|
+
This can be avoided in two ways:
|
|
28
|
+
|
|
29
|
+
@li Global option: `options dsoptions=nonote2err;`
|
|
30
|
+
@li Data step option: `data YOURLIB.YOURDATASET /nonote2err;`
|
|
31
|
+
|
|
32
|
+
@param cvars= Space seperated list of character variables
|
|
33
|
+
@param nvars= Space seperated list of numeric variables
|
|
34
|
+
|
|
35
|
+
<h4> Related Programs </h4>
|
|
36
|
+
@li mp_init.sas
|
|
37
|
+
|
|
38
|
+
@version 9.2
|
|
39
|
+
@author Allan Bowe
|
|
40
|
+
**/
|
|
41
|
+
|
|
42
|
+
%macro mp_md5(cvars=,nvars=);
|
|
43
|
+
%local i var sep;
|
|
44
|
+
put(md5(
|
|
45
|
+
%do i=1 %to %sysfunc(countw(&cvars));
|
|
46
|
+
%let var=%scan(&cvars,&i,%str( ));
|
|
47
|
+
&sep put(md5(trim(&var)),$hex32.)
|
|
48
|
+
%let sep=!!;
|
|
49
|
+
%end;
|
|
50
|
+
%do i=1 %to %sysfunc(countw(&nvars));
|
|
51
|
+
%let var=%scan(&nvars,&i,%str( ));
|
|
52
|
+
/* multiply by 1 to strip precision errors (eg 0 != 0) */
|
|
53
|
+
/* but ONLY if not missing, else will lose any special missing values */
|
|
54
|
+
&sep put(md5(trim(put(ifn(missing(&var),&var,&var*1),binary64.))),$hex32.)
|
|
55
|
+
%let sep=!!;
|
|
56
|
+
%end;
|
|
57
|
+
),$hex32.)
|
|
58
|
+
%mend mp_md5;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Logs a message in a dataset every time it is invoked
|
|
4
|
+
@details If the dataset does not exist, it is created.
|
|
5
|
+
Usage:
|
|
6
|
+
|
|
7
|
+
%mp_perflog(started)
|
|
8
|
+
%mp_perflog()
|
|
9
|
+
%mp_perflog(startanew,libds=work.newdataset)
|
|
10
|
+
%mp_perflog(finished,libds=work.newdataset)
|
|
11
|
+
%mp_perflog(finished)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@param label Provide label to go into the control dataset
|
|
15
|
+
@param libds= Provide a dataset in which to store performance stats. Default
|
|
16
|
+
name is <code>work.mp_perflog</code>;
|
|
17
|
+
|
|
18
|
+
@version 9.2
|
|
19
|
+
@author Allan Bowe
|
|
20
|
+
@source https://github.com/sasjs/core
|
|
21
|
+
|
|
22
|
+
**/
|
|
23
|
+
|
|
24
|
+
%macro mp_perflog(label,libds=work.mp_perflog
|
|
25
|
+
)/*/STORE SOURCE*/;
|
|
26
|
+
|
|
27
|
+
%if not (%mf_existds(&libds)) %then %do;
|
|
28
|
+
data &libds;
|
|
29
|
+
length sysjobid $10 label $256 dttm 8.;
|
|
30
|
+
format dttm datetime19.3;
|
|
31
|
+
call missing(of _all_);
|
|
32
|
+
stop;
|
|
33
|
+
run;
|
|
34
|
+
%end;
|
|
35
|
+
|
|
36
|
+
proc sql;
|
|
37
|
+
insert into &libds
|
|
38
|
+
set sysjobid="&sysjobid"
|
|
39
|
+
,label=symget('label')
|
|
40
|
+
,dttm=%sysfunc(datetime());
|
|
41
|
+
quit;
|
|
42
|
+
|
|
43
|
+
%mend mp_perflog;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Enables previous observations to be re-instated
|
|
4
|
+
@details Remembers the last X observations by storing them in a hash table.
|
|
5
|
+
Is a convenience over the use of lag() or retain, when an entire observation
|
|
6
|
+
needs to be restored.
|
|
7
|
+
|
|
8
|
+
This macro will also restore automatic variables (such as _n_ and _error_).
|
|
9
|
+
|
|
10
|
+
Example Usage:
|
|
11
|
+
|
|
12
|
+
data example;
|
|
13
|
+
set sashelp.class;
|
|
14
|
+
calc_var=_n_*3;
|
|
15
|
+
%* initialise hash and save from PDV ;
|
|
16
|
+
%mp_prevobs(INIT,history=2)
|
|
17
|
+
if _n_ =10 then do;
|
|
18
|
+
%* fetch previous but 1 record;
|
|
19
|
+
%mp_prevobs(FETCH,-2)
|
|
20
|
+
put _n_= name= age= calc_var=;
|
|
21
|
+
%* fetch previous record;
|
|
22
|
+
%mp_prevobs(FETCH,-1)
|
|
23
|
+
put _n_= name= age= calc_var=;
|
|
24
|
+
%* reinstate current record ;
|
|
25
|
+
%mp_prevobs(FETCH,0)
|
|
26
|
+
put _n_= name= age= calc_var=;
|
|
27
|
+
end;
|
|
28
|
+
run;
|
|
29
|
+
|
|
30
|
+
Result:
|
|
31
|
+
|
|
32
|
+
<img src="https://imgur.com/PSjHoET.png" alt="mp_prevobs sas" width="400"/>
|
|
33
|
+
|
|
34
|
+
Credit is made to `data _null_` for authoring this very helpful paper:
|
|
35
|
+
https://www.lexjansen.com/pharmasug/2008/cc/CC08.pdf
|
|
36
|
+
|
|
37
|
+
@param action Either FETCH a current or previous record, or INITialise.
|
|
38
|
+
@param record The relative (to current) position of the previous observation
|
|
39
|
+
to return.
|
|
40
|
+
@param history= The number of records to retain in the hash table. Default=5
|
|
41
|
+
@param prefix= the prefix to give to the variables used to store the hash name
|
|
42
|
+
and index. Default=mp_prevobs
|
|
43
|
+
|
|
44
|
+
@version 9.2
|
|
45
|
+
@author Allan Bowe
|
|
46
|
+
|
|
47
|
+
**/
|
|
48
|
+
|
|
49
|
+
%macro mp_prevobs(action,record,history=5,prefix=mp_prevobs
|
|
50
|
+
)/*/STORE SOURCE*/;
|
|
51
|
+
%let action=%upcase(&action);
|
|
52
|
+
%let prefix=%upcase(&prefix);
|
|
53
|
+
%let record=%eval((&record+0) * -1);
|
|
54
|
+
|
|
55
|
+
%if &action=INIT %then %do;
|
|
56
|
+
|
|
57
|
+
if _n_ eq 1 then do;
|
|
58
|
+
attrib &prefix._VAR length=$64;
|
|
59
|
+
dcl hash &prefix._HASH(ordered:'Y');
|
|
60
|
+
&prefix._KEY=0;
|
|
61
|
+
&prefix._HASH.defineKey("&prefix._KEY");
|
|
62
|
+
do while(1);
|
|
63
|
+
call vnext(&prefix._VAR);
|
|
64
|
+
if &prefix._VAR='' then leave;
|
|
65
|
+
if &prefix._VAR eq "&prefix._VAR" then continue;
|
|
66
|
+
else if &prefix._VAR eq "&prefix._KEY" then continue;
|
|
67
|
+
&prefix._HASH.defineData(&prefix._VAR);
|
|
68
|
+
end;
|
|
69
|
+
&prefix._HASH.defineDone();
|
|
70
|
+
end;
|
|
71
|
+
/* this part has to happen before FETCHing */
|
|
72
|
+
&prefix._KEY+1;
|
|
73
|
+
&prefix._rc=&prefix._HASH.add();
|
|
74
|
+
if &prefix._rc then putlog 'adding' &prefix._rc=;
|
|
75
|
+
%if &history>0 %then %do;
|
|
76
|
+
if &prefix._key>&history+1 then
|
|
77
|
+
&prefix._HASH.remove(key: &prefix._KEY - &history - 1);
|
|
78
|
+
if &prefix._rc then putlog 'removing' &prefix._rc=;
|
|
79
|
+
%end;
|
|
80
|
+
%end;
|
|
81
|
+
%else %if &action=FETCH %then %do;
|
|
82
|
+
if &record>&prefix._key then putlog "Not enough records in &Prefix._hash yet";
|
|
83
|
+
else &prefix._rc=&prefix._HASH.find(key: &prefix._KEY - &record);
|
|
84
|
+
if &prefix._rc then putlog &prefix._rc= " when fetching " &prefix._KEY=
|
|
85
|
+
"with record &record and " _n_=;
|
|
86
|
+
%end;
|
|
87
|
+
|
|
88
|
+
%mend mp_prevobs;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Returns all children from a hierarchy table for a specified parent
|
|
4
|
+
@details Where data stores hierarchies in a simple parent / child mapping,
|
|
5
|
+
it is not always straightforward to extract all the children for a
|
|
6
|
+
particular parent. This problem is known as a recursive self join. This
|
|
7
|
+
macro will extract all the descendents for a parent.
|
|
8
|
+
Usage:
|
|
9
|
+
|
|
10
|
+
data have;
|
|
11
|
+
p=1;c=2;output;
|
|
12
|
+
p=2;c=3;output;
|
|
13
|
+
p=2;c=4;output;
|
|
14
|
+
p=3;c=5;output;
|
|
15
|
+
p=6;c=7;output;
|
|
16
|
+
p=8;c=9;output;
|
|
17
|
+
run;
|
|
18
|
+
|
|
19
|
+
%mp_recursivejoin(base_ds=have
|
|
20
|
+
,outds=want
|
|
21
|
+
,matchval=1
|
|
22
|
+
,parentvar=p
|
|
23
|
+
,childvar=c
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
@param base_ds= base table containing hierarchy (not modified)
|
|
27
|
+
@param outds= the output dataset to create with the generated hierarchy
|
|
28
|
+
@param matchval= the ultimate parent from which to filter
|
|
29
|
+
@param parentvar= name of the parent variable
|
|
30
|
+
@param childvar= name of the child variable (should be same type as parent)
|
|
31
|
+
@param mdebug= set to 1 to prevent temp tables being dropped
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@returns outds contains the following variables:
|
|
35
|
+
- level (0 = top level)
|
|
36
|
+
- &parentvar
|
|
37
|
+
- &childvar (null if none found)
|
|
38
|
+
|
|
39
|
+
@version 9.2
|
|
40
|
+
@author Allan Bowe
|
|
41
|
+
|
|
42
|
+
**/
|
|
43
|
+
|
|
44
|
+
%macro mp_recursivejoin(base_ds=
|
|
45
|
+
,outds=
|
|
46
|
+
,matchval=
|
|
47
|
+
,parentvar=
|
|
48
|
+
,childvar=
|
|
49
|
+
,iter= /* reserved for internal / recursive use by the macro itself */
|
|
50
|
+
,maxiter=500 /* avoid infinite loop */
|
|
51
|
+
,mDebug=0);
|
|
52
|
+
|
|
53
|
+
%if &iter= %then %do;
|
|
54
|
+
proc sql;
|
|
55
|
+
create table &outds as
|
|
56
|
+
select 0 as level,&parentvar, &childvar
|
|
57
|
+
from &base_ds
|
|
58
|
+
where &parentvar=&matchval;
|
|
59
|
+
%if &sqlobs.=0 %then %do;
|
|
60
|
+
%put NOTE: &sysmacroname: No match for &parentvar=&matchval;
|
|
61
|
+
%return;
|
|
62
|
+
%end;
|
|
63
|
+
%let iter=1;
|
|
64
|
+
%end;
|
|
65
|
+
%else %if &iter>&maxiter %then %return;
|
|
66
|
+
|
|
67
|
+
proc sql;
|
|
68
|
+
create table _data_ as
|
|
69
|
+
select &iter as level
|
|
70
|
+
,curr.&childvar as &parentvar
|
|
71
|
+
,base_ds.&childvar as &childvar
|
|
72
|
+
from &outds curr
|
|
73
|
+
left join &base_ds base_ds
|
|
74
|
+
on curr.&childvar=base_ds.&parentvar
|
|
75
|
+
where curr.level=%eval(&iter.-1)
|
|
76
|
+
& curr.&childvar is not null;
|
|
77
|
+
%local append_ds; %let append_ds=&syslast;
|
|
78
|
+
%local obs; %let obs=&sqlobs;
|
|
79
|
+
insert into &outds select distinct * from &append_ds;
|
|
80
|
+
%if &mdebug=0 %then drop table &append_ds;;
|
|
81
|
+
|
|
82
|
+
%if &obs %then %do;
|
|
83
|
+
%mp_recursivejoin(iter=%eval(&iter.+1)
|
|
84
|
+
,outds=&outds,parentvar=&parentvar
|
|
85
|
+
,childvar=&childvar
|
|
86
|
+
,base_ds=&base_ds
|
|
87
|
+
)
|
|
88
|
+
%end;
|
|
89
|
+
|
|
90
|
+
%mend mp_recursivejoin;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Performs a text substitution on a file
|
|
4
|
+
@details Performs a find and replace on a file, either in place or to a new
|
|
5
|
+
file. Can be used on files where lines are longer than 32767.
|
|
6
|
+
|
|
7
|
+
Works by reading in the file byte by byte, then marking the beginning and end
|
|
8
|
+
of each matched string, before finally doing the replace.
|
|
9
|
+
|
|
10
|
+
Full credit for this highly efficient and syntactically satisfying SAS logic
|
|
11
|
+
goes to [Bartosz Jabłoński](https://www.linkedin.com/in/yabwon), founder of
|
|
12
|
+
the [SAS Packages](https://github.com/yabwon/SAS_PACKAGES) framework.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
|
|
16
|
+
%let file="%sysfunc(pathname(work))/file.txt";
|
|
17
|
+
%let str=replace/me;
|
|
18
|
+
%let rep=with/this;
|
|
19
|
+
data _null_;
|
|
20
|
+
file &file;
|
|
21
|
+
put 'blahblah';
|
|
22
|
+
put "blahblah&str.blah";
|
|
23
|
+
put 'blahblahblah';
|
|
24
|
+
run;
|
|
25
|
+
%mp_replace(&file, findvar=str, replacevar=rep)
|
|
26
|
+
data _null_;
|
|
27
|
+
infile &file;
|
|
28
|
+
input;
|
|
29
|
+
list;
|
|
30
|
+
run;
|
|
31
|
+
|
|
32
|
+
Note - if you are running a version of SAS that will allow the io package in
|
|
33
|
+
LUA, you can also use this macro: mp_gsubfile.sas
|
|
34
|
+
|
|
35
|
+
@param infile The QUOTED path to the file on which to perform the substitution
|
|
36
|
+
@param findvar= Macro variable NAME containing the string to search for
|
|
37
|
+
@param replacevar= Macro variable NAME containing the replacement string
|
|
38
|
+
@param outfile= (0) Optional QUOTED path to the adjusted output file (to
|
|
39
|
+
avoid overwriting the first file).
|
|
40
|
+
|
|
41
|
+
<h4> SAS Macros </h4>
|
|
42
|
+
@li mf_getuniquefileref.sas
|
|
43
|
+
@li mf_getuniquename.sas
|
|
44
|
+
|
|
45
|
+
<h4> Related Macros </h4>
|
|
46
|
+
@li mp_chop.sas
|
|
47
|
+
@li mp_gsubfile.sas
|
|
48
|
+
@li mp_replace.test.sas
|
|
49
|
+
|
|
50
|
+
@version 9.4
|
|
51
|
+
@author Bartosz Jabłoński
|
|
52
|
+
@author Allan Bowe
|
|
53
|
+
**/
|
|
54
|
+
|
|
55
|
+
%macro mp_replace(infile,
|
|
56
|
+
findvar=,
|
|
57
|
+
replacevar=,
|
|
58
|
+
outfile=0
|
|
59
|
+
)/*/STORE SOURCE*/;
|
|
60
|
+
|
|
61
|
+
%local inref dttm ds1;
|
|
62
|
+
%let inref=%mf_getuniquefileref();
|
|
63
|
+
%let outref=%mf_getuniquefileref();
|
|
64
|
+
%if &outfile=0 %then %let outfile=&infile;
|
|
65
|
+
%let ds1=%mf_getuniquename(prefix=allchars);
|
|
66
|
+
%let ds2=%mf_getuniquename(prefix=startmark);
|
|
67
|
+
|
|
68
|
+
/* START */
|
|
69
|
+
%let dttm=%sysfunc(datetime());
|
|
70
|
+
|
|
71
|
+
filename &inref &infile lrecl=1 recfm=n;
|
|
72
|
+
|
|
73
|
+
data &ds1;
|
|
74
|
+
infile &inref;
|
|
75
|
+
input sourcechar $char1. @@;
|
|
76
|
+
format sourcechar hex2.;
|
|
77
|
+
run;
|
|
78
|
+
|
|
79
|
+
data &ds2;
|
|
80
|
+
/* set find string to length in bytes to cover trailing spaces */
|
|
81
|
+
length string $ %length(%superq(&findvar));
|
|
82
|
+
string =symget("&findvar");
|
|
83
|
+
drop string;
|
|
84
|
+
|
|
85
|
+
firstchar=char(string,1);
|
|
86
|
+
findlen=lengthm(string); /* <- for trailing bytes */
|
|
87
|
+
|
|
88
|
+
do _N_=1 to nobs;
|
|
89
|
+
set &ds1 nobs=nobs point=_N_;
|
|
90
|
+
if sourcechar=firstchar then do;
|
|
91
|
+
pos=1;
|
|
92
|
+
s=0;
|
|
93
|
+
do point=_N_ to min(_N_ + findlen -1,nobs);
|
|
94
|
+
set &ds1 point=point;
|
|
95
|
+
if sourcechar=char(string, pos) then s + 1;
|
|
96
|
+
else goto _leave_;
|
|
97
|
+
pos+1;
|
|
98
|
+
end;
|
|
99
|
+
_leave_:
|
|
100
|
+
if s=findlen then do;
|
|
101
|
+
START =_N_;
|
|
102
|
+
_N_ =_N_+ s - 1;
|
|
103
|
+
STOP =_N_;
|
|
104
|
+
output;
|
|
105
|
+
end;
|
|
106
|
+
end;
|
|
107
|
+
end;
|
|
108
|
+
stop;
|
|
109
|
+
keep START STOP;
|
|
110
|
+
run;
|
|
111
|
+
|
|
112
|
+
data &ds1;
|
|
113
|
+
declare hash HS(dataset:"&ds2(keep=start)");
|
|
114
|
+
HS.defineKey("start");
|
|
115
|
+
HS.defineDone();
|
|
116
|
+
declare hash HE(dataset:"&ds2(keep=stop)");
|
|
117
|
+
HE.defineKey("stop");
|
|
118
|
+
HE.defineDone();
|
|
119
|
+
do until(eof);
|
|
120
|
+
set &ds1 end=eof curobs =n;
|
|
121
|
+
start = ^HS.check(key:n);
|
|
122
|
+
stop = ^HE.check(key:n);
|
|
123
|
+
length strt $ 1;
|
|
124
|
+
strt =put(start,best. -L);
|
|
125
|
+
retain out 1;
|
|
126
|
+
if out then output;
|
|
127
|
+
if start then out=0;
|
|
128
|
+
if stop then out=1;
|
|
129
|
+
end;
|
|
130
|
+
stop;
|
|
131
|
+
keep sourcechar strt;
|
|
132
|
+
run;
|
|
133
|
+
|
|
134
|
+
filename &outref &outfile recfm=n;
|
|
135
|
+
|
|
136
|
+
data _null_;
|
|
137
|
+
length replace $ %length(%superq(&replacevar));
|
|
138
|
+
replace=symget("&replacevar");
|
|
139
|
+
file &outref;
|
|
140
|
+
do until(eof);
|
|
141
|
+
set &ds1 end=eof;
|
|
142
|
+
if strt ="1" then put replace char.;
|
|
143
|
+
else put sourcechar char1.;
|
|
144
|
+
end;
|
|
145
|
+
stop;
|
|
146
|
+
run;
|
|
147
|
+
|
|
148
|
+
/* END */
|
|
149
|
+
%put &sysmacroname took %sysevalf(%sysfunc(datetime())-&dttm) seconds to run;
|
|
150
|
+
|
|
151
|
+
%mend mp_replace;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Reset when an err condition occurs
|
|
4
|
+
@details When building apps, sometimes an operation must be attempted that
|
|
5
|
+
can cause an err condition. There is no try catch in SAS! So the err state
|
|
6
|
+
must be caught and reset.
|
|
7
|
+
|
|
8
|
+
This macro attempts to do that reset.
|
|
9
|
+
|
|
10
|
+
@version 9.2
|
|
11
|
+
@author Allan Bowe
|
|
12
|
+
|
|
13
|
+
**/
|
|
14
|
+
|
|
15
|
+
%macro mp_reseterror(
|
|
16
|
+
)/*/STORE SOURCE*/;
|
|
17
|
+
|
|
18
|
+
options obs=max replace nosyntaxcheck;
|
|
19
|
+
%let syscc=0;
|
|
20
|
+
|
|
21
|
+
%if "&sysprocessmode " = "SAS Stored Process Server " %then %do;
|
|
22
|
+
data _null_;
|
|
23
|
+
rc=stpsrvset('program error', 0);
|
|
24
|
+
run;
|
|
25
|
+
%end;
|
|
26
|
+
|
|
27
|
+
%mend mp_reseterror;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
@file
|
|
3
|
+
@brief Reset an option to original value
|
|
4
|
+
@details Inspired by the SAS Jedi -
|
|
5
|
+
https://blogs.sas.com/content/sastraining/2012/08/14/jedi-sas-tricks-reset-sas-system-options
|
|
6
|
+
|
|
7
|
+
Called as follows:
|
|
8
|
+
|
|
9
|
+
options obs=30 ps=max;
|
|
10
|
+
%mp_resetoption(OBS)
|
|
11
|
+
%mp_resetoption(PS)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@param [in] option the option to reset
|
|
15
|
+
|
|
16
|
+
@version 9.2
|
|
17
|
+
@author Allan Bowe
|
|
18
|
+
|
|
19
|
+
**/
|
|
20
|
+
|
|
21
|
+
%macro mp_resetoption(option /* the option to reset */
|
|
22
|
+
)/*/STORE SOURCE*/;
|
|
23
|
+
|
|
24
|
+
%if "%substr(&sysver,1,1)" ne "4" and "%substr(&sysver,1,1)" ne "5" %then %do;
|
|
25
|
+
data _null_;
|
|
26
|
+
length code $1500;
|
|
27
|
+
startup=getoption("&option",'startupvalue');
|
|
28
|
+
current=getoption("&option");
|
|
29
|
+
if startup ne current then do;
|
|
30
|
+
code =cat('OPTIONS ',getoption("&option",'keyword','startupvalue'),';');
|
|
31
|
+
putlog "NOTE: Resetting system option: " code ;
|
|
32
|
+
call execute(code );
|
|
33
|
+
end;
|
|
34
|
+
run;
|
|
35
|
+
%end;
|
|
36
|
+
%else %do;
|
|
37
|
+
%put &sysmacroname: reset option feature unavailable on &sysvlong;
|
|
38
|
+
%end;
|
|
39
|
+
%mend mp_resetoption;
|