1
0
mirror of https://github.com/sasjs/core.git synced 2025-12-15 00:04:35 +00:00

Compare commits

...

5 Commits

Author SHA1 Message Date
Allan Bowe
8eb4f0844c Merge pull request #124 from sasjs/updates
feat: mf_islibds() macro to test if a library.dataset reference is valid
2021-12-22 17:28:22 +02:00
munja
f90dc069dc feat: update to makedata to respect primary keys (and enable joins to other tables) 2021-12-22 15:12:10 +00:00
munja
436b430389 feat: mf_islibds() macro to test if a library.dataset reference is syntactically valid 2021-12-22 11:23:57 +00:00
Allan Bowe
6667b91ced Merge pull request #123 from sasjs/words
feat: new wordsinstr1andstr2() macro and associated tests
2021-12-22 00:07:39 +02:00
munja
bce56d8105 feat: new wordsinstr1andstr2() macro and associated tests 2021-12-21 21:54:48 +00:00
7 changed files with 339 additions and 36 deletions

144
all.sas
View File

@@ -1532,6 +1532,45 @@ Usage:
%else %do;1%end; %else %do;1%end;
%mend mf_isint;/** %mend mf_isint;/**
@file
@brief Checks whether a string follows correct library.dataset format
@details Many macros in the core library accept a library.dataset parameter
referred to as 'libds'. This macro validates the structure of that parameter,
eg:
@li 8 character libref?
@li 32 character dataset?
@li contains a period?
It does NOT check whether the dataset exists, or if the library is assigned.
Usage:
%put %mf_islibds(work.something)=1;
%put %mf_islibds(nolib)=0;
%put %mf_islibds(badlibref.ds)=0;
%put %mf_islibds(w.t.f)=0;
@param [in] libds The string to be checked
@return output Returns 1 if libds is valid, 0 if it is not
<h4> Related Macros </h4>
@li mf_islibds.test.sas
@li mp_validatecol.sas
@version 9.2
**/
%macro mf_islibds(libds
)/*/STORE SOURCE*/;
%local regex;
%let regex=%sysfunc(prxparse(%str(/^[_a-z]\w{0,7}\.[_a-z]\w{0,31}$/i)));
%sysfunc(prxmatch(&regex,&libds))
%mend mf_islibds;/**
@file @file
@brief Returns physical location of various SAS items @brief Returns physical location of various SAS items
@details Returns location of the PlatformObjectFramework tools @details Returns location of the PlatformObjectFramework tools
@@ -1805,6 +1844,60 @@ Usage:
%exit_success: %exit_success:
%mend mf_verifymacvars; %mend mf_verifymacvars;
/**
@file
@brief Returns words that are in both string 1 and string 2
@details Compares two space separated strings and returns the words that are
in both.
Usage:
%put %mf_wordsInStr1andStr2(
Str1=blah sss blaaah brah bram boo
,Str2= blah blaaah brah ssss
);
returns:
> blah blaaah brah
@param str1= string containing words to extract
@param str2= used to compare with the extract string
@warning CASE SENSITIVE!
@version 9.2
@author Allan Bowe
**/
%macro mf_wordsInStr1andStr2(
Str1= /* string containing words to extract */
,Str2= /* used to compare with the extract string */
)/*/STORE SOURCE*/;
%local count_base count_extr i i2 extr_word base_word match outvar;
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
%put %str(WARN)ING: empty string provided!;
%put base string (str1)= &str1;
%put compare string (str2) = &str2;
%return;
%end;
%let count_base=%sysfunc(countw(&Str2));
%let count_extr=%sysfunc(countw(&Str1));
%do i=1 %to &count_extr;
%let extr_word=%scan(&Str1,&i,%str( ));
%let match=0;
%do i2=1 %to &count_base;
%let base_word=%scan(&Str2,&i2,%str( ));
%if &extr_word=&base_word %then %let match=1;
%end;
%if &match=1 %then %let outvar=&outvar &extr_word;
%end;
&outvar
%mend mf_wordsInStr1andStr2;
/** /**
@file @file
@brief Returns words that are in string 1 but not in string 2 @brief Returns words that are in string 1 but not in string 2
@@ -7586,8 +7679,6 @@ lock &libds clear;
according to the variable types and formats. according to the variable types and formats.
TODO: TODO:
@li Respect PKs
@li Respect NOT NULLs
@li Consider dates, datetimes, times, integers etc @li Consider dates, datetimes, times, integers etc
Usage: Usage:
@@ -7603,12 +7694,15 @@ lock &libds clear;
); );
%mp_makedata(work.example) %mp_makedata(work.example)
@param [in] libds The empty table in which to create data @param [in] libds The empty table (libref.dataset) in which to create data
@param [out] obs= (500) The number of records to create. @param [out] obs= (500) The maximum number of records to create. The table
is sorted with nodup on the primary key, so the actual number of records may
be lower than this.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvarlen.sas @li mf_getvarlen.sas
@li mf_islibds.sas
@li mf_nobs.sas @li mf_nobs.sas
@li mp_getcols.sas @li mp_getcols.sas
@li mp_getpk.sas @li mp_getpk.sas
@@ -7620,45 +7714,59 @@ lock &libds clear;
%macro mp_makedata(libds %macro mp_makedata(libds
,obs=500 ,obs=500
,seed=1
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local ds1 c1 n1 i col charvars numvars; %local ds1 ds2 lib ds pk_fields i col charvars numvars ispk;
%if %mf_nobs(&libds)>0 %then %do; %if %mf_islibds(&libds)=0 %then %do;
%put &sysmacroname: Invalid libds (&libds) - should be library.dataset format;
%return;
%end;
%else %if %mf_nobs(&libds)>0 %then %do;
%put &sysmacroname: &libds has data, it will not be recreated; %put &sysmacroname: &libds has data, it will not be recreated;
%return; %return;
%end; %end;
%local ds1 c1 n1; /* set up temporary vars */
%let ds1=%mf_getuniquename(prefix=mp_makedata); %let ds1=%mf_getuniquename(prefix=mp_makedatads1);
%let c1=%mf_getuniquename(prefix=mp_makedatacol); %let ds2=%mf_getuniquename(prefix=mp_makedatads2);
%let n1=%mf_getuniquename(prefix=mp_makedatacol); %let lib=%scan(&libds,1,.);
data &ds1; %let ds=%scan(&libds,2,.);
/* grab the primary key vars */
%mp_getpk(&lib,ds=&ds,outds=&ds1)
proc sql noprint;
select pk_fields into: pk_fields from &ds1;
data &ds2;
if 0 then set &libds; if 0 then set &libds;
do _n_=1 to &obs; do _n_=1 to &obs;
&c1=repeat(uuidgen(),10);
&n1=ranuni(1)*5000000;
drop &c1 &n1;
%let charvars=%mf_getvarlist(&libds,typefilter=C); %let charvars=%mf_getvarlist(&libds,typefilter=C);
%if &charvars ^= %then %do i=1 %to %sysfunc(countw(&charvars)); %if &charvars ^= %then %do i=1 %to %sysfunc(countw(&charvars));
%let col=%scan(&charvars,&i); %let col=%scan(&charvars,&i);
&col=subpad(&c1,1,%mf_getvarlen(&libds,&col)); /* create random value based on observation number and colum length */
&col=substr(put(md5(_n_),$hex32.),1,%mf_getvarlen(&libds,&col));
%end; %end;
%let numvars=%mf_getvarlist(&libds,typefilter=N); %let numvars=%mf_getvarlist(&libds,typefilter=N);
%if &numvars ^= %then %do i=1 %to %sysfunc(countw(&numvars)); %if &numvars ^= %then %do i=1 %to %sysfunc(countw(&numvars));
%let col=%scan(&numvars,&i); %let col=%scan(&numvars,&i);
&col=&n1; &col=_n_;
%end; %end;
output; output;
end; end;
run; run;
proc sort data=&ds2 nodupkey;
by &pk_fields;
run;
proc append base=&libds data=&ds1; proc append base=&libds data=&ds2;
run; run;
proc sql; proc sql;
drop table &ds1; drop table &ds1, &ds2;
%mend mp_makedata;/** %mend mp_makedata;/**
@file @file

40
base/mf_islibds.sas Normal file
View File

@@ -0,0 +1,40 @@
/**
@file
@brief Checks whether a string follows correct library.dataset format
@details Many macros in the core library accept a library.dataset parameter
referred to as 'libds'. This macro validates the structure of that parameter,
eg:
@li 8 character libref?
@li 32 character dataset?
@li contains a period?
It does NOT check whether the dataset exists, or if the library is assigned.
Usage:
%put %mf_islibds(work.something)=1;
%put %mf_islibds(nolib)=0;
%put %mf_islibds(badlibref.ds)=0;
%put %mf_islibds(w.t.f)=0;
@param [in] libds The string to be checked
@return output Returns 1 if libds is valid, 0 if it is not
<h4> Related Macros </h4>
@li mf_islibds.test.sas
@li mp_validatecol.sas
@version 9.2
**/
%macro mf_islibds(libds
)/*/STORE SOURCE*/;
%local regex;
%let regex=%sysfunc(prxparse(%str(/^[_a-z]\w{0,7}\.[_a-z]\w{0,31}$/i)));
%sysfunc(prxmatch(&regex,&libds))
%mend mf_islibds;

View File

@@ -0,0 +1,54 @@
/**
@file
@brief Returns words that are in both string 1 and string 2
@details Compares two space separated strings and returns the words that are
in both.
Usage:
%put %mf_wordsInStr1andStr2(
Str1=blah sss blaaah brah bram boo
,Str2= blah blaaah brah ssss
);
returns:
> blah blaaah brah
@param str1= string containing words to extract
@param str2= used to compare with the extract string
@warning CASE SENSITIVE!
@version 9.2
@author Allan Bowe
**/
%macro mf_wordsInStr1andStr2(
Str1= /* string containing words to extract */
,Str2= /* used to compare with the extract string */
)/*/STORE SOURCE*/;
%local count_base count_extr i i2 extr_word base_word match outvar;
%if %length(&str1)=0 or %length(&str2)=0 %then %do;
%put %str(WARN)ING: empty string provided!;
%put base string (str1)= &str1;
%put compare string (str2) = &str2;
%return;
%end;
%let count_base=%sysfunc(countw(&Str2));
%let count_extr=%sysfunc(countw(&Str1));
%do i=1 %to &count_extr;
%let extr_word=%scan(&Str1,&i,%str( ));
%let match=0;
%do i2=1 %to &count_base;
%let base_word=%scan(&Str2,&i2,%str( ));
%if &extr_word=&base_word %then %let match=1;
%end;
%if &match=1 %then %let outvar=&outvar &extr_word;
%end;
&outvar
%mend mf_wordsInStr1andStr2;

View File

@@ -10,8 +10,6 @@
according to the variable types and formats. according to the variable types and formats.
TODO: TODO:
@li Respect PKs
@li Respect NOT NULLs
@li Consider dates, datetimes, times, integers etc @li Consider dates, datetimes, times, integers etc
Usage: Usage:
@@ -27,12 +25,15 @@
); );
%mp_makedata(work.example) %mp_makedata(work.example)
@param [in] libds The empty table in which to create data @param [in] libds The empty table (libref.dataset) in which to create data
@param [out] obs= (500) The number of records to create. @param [out] obs= (500) The maximum number of records to create. The table
is sorted with nodup on the primary key, so the actual number of records may
be lower than this.
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getuniquename.sas @li mf_getuniquename.sas
@li mf_getvarlen.sas @li mf_getvarlen.sas
@li mf_islibds.sas
@li mf_nobs.sas @li mf_nobs.sas
@li mp_getcols.sas @li mp_getcols.sas
@li mp_getpk.sas @li mp_getpk.sas
@@ -44,44 +45,58 @@
%macro mp_makedata(libds %macro mp_makedata(libds
,obs=500 ,obs=500
,seed=1
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
%local ds1 c1 n1 i col charvars numvars; %local ds1 ds2 lib ds pk_fields i col charvars numvars ispk;
%if %mf_nobs(&libds)>0 %then %do; %if %mf_islibds(&libds)=0 %then %do;
%put &sysmacroname: Invalid libds (&libds) - should be library.dataset format;
%return;
%end;
%else %if %mf_nobs(&libds)>0 %then %do;
%put &sysmacroname: &libds has data, it will not be recreated; %put &sysmacroname: &libds has data, it will not be recreated;
%return; %return;
%end; %end;
%local ds1 c1 n1; /* set up temporary vars */
%let ds1=%mf_getuniquename(prefix=mp_makedata); %let ds1=%mf_getuniquename(prefix=mp_makedatads1);
%let c1=%mf_getuniquename(prefix=mp_makedatacol); %let ds2=%mf_getuniquename(prefix=mp_makedatads2);
%let n1=%mf_getuniquename(prefix=mp_makedatacol); %let lib=%scan(&libds,1,.);
data &ds1; %let ds=%scan(&libds,2,.);
/* grab the primary key vars */
%mp_getpk(&lib,ds=&ds,outds=&ds1)
proc sql noprint;
select pk_fields into: pk_fields from &ds1;
data &ds2;
if 0 then set &libds; if 0 then set &libds;
do _n_=1 to &obs; do _n_=1 to &obs;
&c1=repeat(uuidgen(),10);
&n1=ranuni(1)*5000000;
drop &c1 &n1;
%let charvars=%mf_getvarlist(&libds,typefilter=C); %let charvars=%mf_getvarlist(&libds,typefilter=C);
%if &charvars ^= %then %do i=1 %to %sysfunc(countw(&charvars)); %if &charvars ^= %then %do i=1 %to %sysfunc(countw(&charvars));
%let col=%scan(&charvars,&i); %let col=%scan(&charvars,&i);
&col=subpad(&c1,1,%mf_getvarlen(&libds,&col)); /* create random value based on observation number and colum length */
&col=substr(put(md5(_n_),$hex32.),1,%mf_getvarlen(&libds,&col));
%end; %end;
%let numvars=%mf_getvarlist(&libds,typefilter=N); %let numvars=%mf_getvarlist(&libds,typefilter=N);
%if &numvars ^= %then %do i=1 %to %sysfunc(countw(&numvars)); %if &numvars ^= %then %do i=1 %to %sysfunc(countw(&numvars));
%let col=%scan(&numvars,&i); %let col=%scan(&numvars,&i);
&col=&n1; &col=_n_;
%end; %end;
output; output;
end; end;
run; run;
proc sort data=&ds2 nodupkey;
by &pk_fields;
run;
proc append base=&libds data=&ds1; proc append base=&libds data=&ds2;
run; run;
proc sql; proc sql;
drop table &ds1; drop table &ds1, &ds2;
%mend mp_makedata; %mend mp_makedata;

View File

@@ -0,0 +1,46 @@
/**
@file
@brief Testing mf_islibds macro
%put %mf_islibds(work.something)=1;
%put %mf_islibds(nolib)=0;
%put %mf_islibds(badlibref.ds)=0;
%put %mf_islibds(w.t.f)=0;
<h4> SAS Macros </h4>
@li mf_islibds.sas
@li mp_assert.sas
**/
%mp_assert(
iftrue=(
%mf_islibds(work.something)=1
),
desc=%str(Checking mf_islibds(work.something)=1),
outds=work.test_results
)
%mp_assert(
iftrue=(
%mf_islibds(nolib)=0
),
desc=%str(Checking mf_islibds(nolib)=0),
outds=work.test_results
)
%mp_assert(
iftrue=(
%mf_islibds(badlibref.ds)=0
),
desc=%str(Checking mf_islibds(badlibref.ds)=0),
outds=work.test_results
)
%mp_assert(
iftrue=(
%mf_islibds(w.t.f)=0
),
desc=%str(Checking mf_islibds(w.t.f)=0),
outds=work.test_results
)

View File

@@ -0,0 +1,20 @@
/**
@file
@brief Testing mf_wordsinstr1andstr2 macro
<h4> SAS Macros </h4>
@li mf_wordsinstr1andstr2.sas
@li mp_assert.sas
**/
%let x=%mf_wordsinstr1andstr2(str1=xx DOLLAR x $CHAR xxx W MONNAME
,str2=DOLLAR $CHAR W MONNAME xxxxxx
);
%mp_assert(
iftrue=(
"&x"="DOLLAR $CHAR W MONNAME"
),
desc=Checking basic string,
outds=work.test_results
)

View File

@@ -0,0 +1,20 @@
/**
@file
@brief Testing mf_wordsinstr1butnotstr2 macro
<h4> SAS Macros </h4>
@li mf_wordsinstr1butnotstr2.sas
@li mp_assert.sas
**/
%let x=%mf_wordsinstr1butnotstr2(str1=xx DOLLAR x $CHAR xxx W MONNAME
,str2=ff xx x xxx xxxxxx
);
%mp_assert(
iftrue=(
"&x"="DOLLAR $CHAR W MONNAME"
),
desc=Checking basic string,
outds=work.test_results
)