From b785e1c9e48337004fc9e6b5cb8de840549b0019 Mon Sep 17 00:00:00 2001 From: Bart Jablonski Date: Tue, 7 Nov 2023 12:47:50 +0100 Subject: [PATCH] The macroArray package [ver. 1.2.0] ## The macroArray package [ver. 1.2.0] - New parameters added to the [`%mcDictionary()`](https://github.com/SASPAC/macroarray/blob/main/macroarray.md#mcdictionary-macro) macro which allows to populate dictionary directly from a data set (see the last example in the documentation). - Documentation updated. --- README.md | 2 +- hist/1.2.0/macroarray.md | 2285 +++++++++++++++++++++++++++++++++++++ hist/1.2.0/macroarray.zip | Bin 0 -> 53208 bytes macroarray.md | 97 +- macroarray.zip | Bin 52051 -> 53208 bytes 5 files changed, 2375 insertions(+), 9 deletions(-) create mode 100644 hist/1.2.0/macroarray.md create mode 100644 hist/1.2.0/macroarray.zip diff --git a/README.md b/README.md index 6df0b91..4d064ab 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The **macroArray** package implements an array, a hash table, and a dictionary c ); ``` -SHA256 digest for the latest version of `macroArray`: F*E9C0C58FB36AC40C76A518066B8C6F9942202A9DB2C2D737E95D2BB6E4ECED50 +SHA256 digest for the latest version of `macroArray`: F*8689194590698F9A00B57FB37BE3CA8D7330F16B3E591CEAF49E6BE0B70D61D0 [**Documentation for macroArray**](./macroarray.md "Documentation for macroArray") diff --git a/hist/1.2.0/macroarray.md b/hist/1.2.0/macroarray.md new file mode 100644 index 0000000..dbba1b1 --- /dev/null +++ b/hist/1.2.0/macroarray.md @@ -0,0 +1,2285 @@ +- [The macroArray package](#macroarray) +- [Content description](#content-description) + * [`%appendArray()` macro](#appendarray-macro) + * [`%appendCell()` macro](#appendcell-macro) + * [`%array()` macro](#array-macro) + * [`%concatArrays()` macro](#concatarrays-macro) + * [`%deleteMacArray()` macro](#deletemacarray-macro) + * [`%do_over()` macro](#do-over-macro) + * [`%do_over2()` macro](#do-over2-macro) + * [`%do_over3()` macro](#do-over3-macro) + * [`%make_do_over()` macro](#make-do-over-macro) + * [`%mcHashTable()` macro](#mchashtable-macro) + * [`%mcDictionary()` macro](#mcdictionary-macro) + * [`%QzipArrays()` macro](#qziparrays-macro) + * [`%zipArrays()` macro](#ziparrays-macro) + * [`%sortMacroArray()` macro](#sortmacroarray-macro) + + * [License](#license) + +--- + +# The macroArray package [ver. 1.2.0] ############################################### + +The **macroArray** package implements a macro array facility: +- `%array()`, +- `%do_over()`, +- `%make_do_over()`, +- `%deletemacarray()`, +- `%concatarrays()`, +- `%appendcell()`, +- `%mcHashTable()`, +- `%zipArrays()`, +- `%sortMacroArray()`, +- `%mcDictionary()`, +- etc. + +The set of macros, which emulates classic +data-step-array functionality on the macro +programming level, is provided. + +*Note:* +If you are working with BIG macroarrays do not +forget to verify your session setting for macro +memory limits. Run: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + proc options group = macro; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +to verify the following options: + +| option | description | +|-------------:|:-----------------------------------------------------------------------------------------------| +|`MEXECSIZE=` | specifies the maximum macro size that can be executed in memory. | +|`MSYMTABMAX=` | specifies the maximum amount of memory available to the macro variable symbol table or tables. | +|`MVARSIZE=` | specifies the maximum size for a macro variable that is stored in memory. | + +--- + +Package contains: + 1. macro appendarray + 2. macro appendcell + 3. macro array + 4. macro concatarrays + 5. macro deletemacarray + 6. macro do_over + 7. macro do_over2 + 8. macro do_over3 + 9. macro make_do_over + 10. macro mcdictionary + 11. macro mchashtable + 12. macro qziparrays + 13. macro sortmacroarray + 14. macro ziparrays + +Required SAS Components: + *Base SAS Software* + +*SAS package generated by generatePackage, version 20231107* + +The SHA256 hash digest for package macroArray: +`F*8689194590698F9A00B57FB37BE3CA8D7330F16B3E591CEAF49E6BE0B70D61D0` + +--- +# Content description ############################################################################################ + +## >>> `%appendArray()` macro: <<< ############ + +The `%appendArray()` macro is a macrowrapper +which allows to concatenate two macroarrays +created by `%array()` macro. + +By default values of the second macroarray are *not* removed. + +Dimensions of the first macroarray are extended. + +The `%appendArray()` macro executes like a pure macro code. + +### SYNTAX: ##################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%appendArray( + first + ,second +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `first` - *Required*, a name of a macroarray created by the `%array()` macro. + +2. `second` - *Required*, a name of a macroarray created by the `%array()` macro. + + + + +### EXAMPLES AND USECASES: ###################################################### + +**EXAMPLE 1.** Append macroarrays LL and MM. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(ll[2:4] $ 12, + function = quote(put(today() + 10*_I_, yymmdd10.)), + macarray=Y + ) + %array(mm[10:13] $ 1200, + function = quote(repeat("A",12*_I_)), + macarray=Y + ) + %put *%ll(2)*%ll(3)*%ll(4)*; + + %appendArray(ll, mm); + %put *%ll(2)*%ll(3)*%ll(4)*%ll(5)*%ll(6)**%ll(7)*%ll(8)*; + + %put *%mm(10)**%mm(11)*%mm(12)*%mm(13)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Error handling. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %appendArray(ll, ) + %appendArray(, mm) + + %appendArray(noExistA, noExistB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- + + + +## >>> `%appendCell()` macro: <<< ############## + +The `%appendCell()` macro allows to append +a macrovariable to a macroarray created by the `%array()` macro. + +Dimensions of the macroarray are extended. + +The `%appendCell()` macro executes like a pure macro code. + +### SYNTAX: #################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%appendCell( + first + ,second + ,hilo +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `first` - *Required*, a name of a macroarray created by the `%array()` macro. + +2. `second` - *Required*, a name of a macrovariable to be append to the macroarray. + +3. `hilo` - *Required*, if `H` macrovariable is appended at the end + if `L` macrovariable is appended at the beginning +); + + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Create two macro wrappers. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %* Macro wrapper to append a macrovariable to the end of a macroarray; + %macro appendHC(array,cell); + %appendCell(&array.,&cell.,H) + %mend appendHC; + + %* macro wrapper to append a macrovariable to the beginning of a macroarray; + %macro appendLC(array,cell); + %appendCell(&array.,&cell.,L) + %mend appendLC; + + + %* create macroarrays X and variables W,Y,Z; + + %array(X[2:4] $ ("AAA", "BBB", "CCC"), macarray=Y) + %let W=1; + %let Y=2; + %let Z=3; + %put *%do_over(X)*&=W*&=Y*&=Z*; + + %put BEFORE *%do_over(X)**&=xLBOUND*&=xHBOUND*&=xN*; + %appendCell(X,Y,H) + %put AFTER1 *%do_over(X)**&=xLBOUND*&=xHBOUND*&=xN*; + + %appendLC(X,W) + %put AFTER2 *%do_over(X)**&=xLBOUND*&=xHBOUND*&=xN*; + + %appendHC(X,Z) + %put AFTER3 *%do_over(X)**&=xLBOUND*&=xHBOUND*&=xN*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Error handling +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %appendCell(X,Y,blahblah) + + %appendCell(X,,H) + %appendCell(,Y,H) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** Adding variable below lower bound. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(zero[0:2] $ ("AAA", "BBB", "CCC"), macarray=Y) + %let belowzero=zzz; + + %put BEFORE *%do_over(zero)**&=zeroLBOUND*&=zeroHBOUND*&=zeroN*; + %appendCell(zero,belowzero,L) + %put AFTER *%do_over(zero)**&=zeroLBOUND*&=zeroHBOUND*&=zeroN*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- + + + +## >>> `%array()` macro: <<< ####################### + +The code of a macro was inspired by +*Ted Clay's* and *David Katz's* macro `%array()`. + +The `%array()` macro version provided in the package +is designed to facilitate +the idea of macro array concept, i.e. *a list of +macrovariables with common prefix and numerical suffixes*. +Usually such construction is then resolved by +double ampersand syntax, e.g. `&&perfix&i` or similar one. + +What is new/extension to the `%array()` macro concept are: + +0. The syntax is closer to the data step one. +1. It is a pure macro code (it can be executed in any place + of 4GL code), this includes generating macro arrays out + of datasets. +2. When a macroarrray is created it allows also to generate + a new macro (named the same as the array name) and replace + the double ampersand syntax with more array looking one, + i.e. for array ABC user can have `%ABC(1)`, `%ABC(2)`, or `%ABC(&i)` + constructions. +3. The array macro allows to use data step functions to generate + array's entries. + +The `%array()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%array( + array + <,function=> + <,before=> + <,after=> + <,vnames=N> + <,macarray=N> + <,ds=> + <,vars=> + <,q=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `array` - *Required*, an array name and a declaration/definition of an array,
+ e.g. `myArr[*] x1-x3 (4:6)`
+ or `myBrr[*] $ y1-y3 ("a" "b" "c")`
+ or `myCrr[3] $ ("d d d" "e,e,e" "f;f;f")`
+ or `myDrr p q r s`.
+ Macrovariables created by the macro are *global*. + If an array name is `_` (single underscore) then attached variables + list names are used, a call of the form: + `%array(_[*] p1 q2 r3 s4 (-1 -2 -3 -4))` + will create macrovariables: `p1`, `q2`, `r3`, and `s4` with respective + values: `-1`, `-2`, `-3`, and `-4`.
+ Three additional *global* macrovariables: + `LBOUND`, `HBOUND`, and `N` + are generated with the macroarray. See examples for more use-cases. + +* `function=` - *Optional*, a function or an expression to be applied to all array cells, + `_I_` is as array iterator, e.g. `_I_ + rand("uniform")`. + +* `before=` - *Optional*, a function or an expression to be added before looping through + array, e.g. `call streaminit(123)`. + +* `after=` - *Optional*, a function or an expression to be added after looping through + array, e.g. `call sortn(ABC)`. + +* `vnames=N` - *Optional*, default value `N`, if set to `Y`/`YES` then macroarray is built based + on variables names instead values, e.g. + `%array(myArr[*] x1-x3 (4:6), vnames=Y)` + will use `x1`, `x2`, and `x3` as values instead `4`, `5`, and `6`. + +* `macarray=N` - *Optional*, default value `N`, if set to `Y`/`YES` then a macro, named with the array + name, is compiled to create convenient envelope for multiple ampersands, e.g. + `%array(myArr[*] x1-x3 (4:6), macarray=Y)` + will create `%myArr(J)` macro which will allow to extract "data" + from macroarray like: + `%let x = %myArr(1);` + or when used with second parameter equal `I` (insert) allow to overwrite macroarrays + value: + `%let %myArr(17,i) = 42;` + If set to `M` then for a given array name the macro symbols table is scanned for + macrovariables with prefix like the array name and numeric suffixes, + then the minimum and the maximum index is determined + and all not existing global macrovariables are created and + a macro is generated in the same way as for the `Y` value. + +* `ds=` - *Optional*, use a dataset as a basis for a macroarray data, + if used by default overwrites use of the `array` parameter, honors `macarray=` + argument, dataset options are allowed, e.g. `sashelp.class(obs=5)` + +* `vars=` - *Optional*, a list of variables used to create macroarrays from a dataset, + the list format can be as follows (`<...>` means optional): + `variable1 <... variableN>` + delimiters are hash(`#`) and pipe(`|`), currently only space + is supported as separator, the meaning of `#` and `|` delimiters + will be explained in the following example: + if the `vars = height#h weight weight|w age|` value is provided + then the following macroarrays will be created:
+ 1) macroarray "H" with ALL(`#`) values of variable "height"
+ 2) macroarray "WEIGHT" with ALL(no separator is equivalent to #) + values of variable "weight"
+ 3) macroarray "W" with UNIQUE(|) values of variable "weight" and
+ 4) macroarray "AGE" with UNIQUE(|) values of variable "age". + +* `q=` - *Optional*, indicates (when set to `1`) if the value be surrounded by quotes. + It uses `quote(cats(...))` combo under the hood. Default value is `0`. + Ignored for `macarray=M`. + + +--- + +### EXAMPLES AND USECASES: #################################################### + + +**EXAMPLE 1.** Basic use-case. + Creating macroarray like in the array statement. + Values not variables names are used by default. + Different types of brackets are allowed. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(a[*] x1-x5 (1:5)) + + %array(b{5} (5*17), q=1) + + %* Mind the $ since it is a character array!; + %array(c(3) $ 10 ("a A" "b,B" "c;C")) + + %array(d x1-x5 (5 4 3 2 1)) + %put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Index ranges. + If range starts < 0 then it is shifted to 0. + In case when range is from `1` to `M` + then macrovariable `N` is set to `M` + In case when range is different + the `N` returns number of + elements in the array `(Hbound - Lbound + 1)`. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(d[-2:2] $ ("a" "b" "c" "d" "e")) + %put &=dLBOUND. &=dHBOUND. &=dN.; + %put &=d0. &=d1. &=d2. &=d3. &=d4.; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Functions. + It is possible to assign value of a function + or an expression to a cell of the array, + e.g. `array[_I_] = function(...)`. + You can use an iterator in a function. + As in case of usual arrays it is `_I_`. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(e[-3:3] $, function = "A" ) + %put &=eLBOUND. &=eHBOUND. &=eN.; + %put &=e0. &=e1. &=e2. &=e3. &=e4. &=e5. &=e6.; + + %array(f[-3:3], function = (2**_I_) ) + %put &=fLBOUND. &=fHBOUND. &=fN.; + %put &=f0. &=f1. &=f2. &=f3. &=f4. &=f5. &=f6.; + + %array(g[0:2], function = ranuni(123) ) + %put &=gLBOUND. &=gHBOUND. &=gN.; + %put &=g0. &=g1. &=g2.; + + %* Or something more complex; + %array(gg[0:11] $ 11, function = put(intnx("MONTH", '1jun2018'd, _I_, "E"), yymmn.), q=1) + %put &=ggLBOUND. &=ggHBOUND. &=ggN.; + %put &=gg0 &=gg1 &=gg2 ... &=gg11; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Functions cont. + If there is need for set-up something *before* or *after*: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(h[10:12] + ,function = rand('Uniform') + ,before = call streaminit(123) + ,after = call sortn(of h[*]) + ) + %put &=h10. &=h11. &=h12.; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Fibonacci series. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(i[1:10] (10*0) + ,function = ifn(_I_ < 2, 1, sum(i[max(_I_-2,1)], i[max(_I_-1,2)]) ) ) + %put &=i1 &=i2 &=i3 &=i4 &=i5 &=i6 &=i7 &=i8 &=i9 &=i10; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6a.** Quoted "Uppercas Letters" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(UL[26] $, function = byte(rank("A")+_I_-1) , q=1) + %put &=UL1 &=UL2 ... &=UL25 &=UL26; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6b.** "Lowercase Letters" + Extended by `macarray=Y` option and + the input mode support (with `I`). + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(ll[26] $, function = byte(rank("a")+_I_-1), macarray=Y) + %put *%ll(&llLBOUND.)*%ll(3)*%ll(4)*%ll(5)*...*%ll(25)*%ll(&llHBOUND.)*; + + %* The range handling, warning; + %put *%ll(265)*; + + %* The input mode; + %put *before:*%ll(2)*; + %let %ll(2,I) = bbbbb; + %put *after: *%ll(2)*; + + %* The range handling, error; + %let %ll(265,I) = bbb; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** The use of `vnames=Y` + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(R R1978-R1982) + %put &=R1 &=R2 &=R3 &=R4 &=R5; + + %array(R R1978-R1982 (78:82)) + %put &=R1 &=R2 &=R3 &=R4 &=R5; + + %array(R R1978-R1982 (78:82), vnames=Y) + %put &=R1 &=R2 &=R3 &=R4 &=R5; + + %array(R R1978-R1982, vnames=Y) + %put &=R1 &=R2 &=R3 &=R4 &=R5; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 8.** A "no name" array i.e. the `_[*]` array + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(_[*] x1-x5 (1:5)) + %put _user_; + + %array(_[*] p q r s (4*42)) + %put _user_; + + %* If no variables names than use _1 _2 ... _N; + %array(_[4] (-1 -2 -3 -4)) + %put &=_1 &=_2 &=_3 &=_4; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 9.** Pure macro code can be used in a data step. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data test1; + set sashelp.class; + %array(ds[*] d1-d4 (4*17)) + a1 = &ds1.; + a2 = &ds2.; + a3 = &ds3.; + a4 = &ds4.; + run; + + data test2; + set sashelp.class; + %array(_[*] j k l m (4*17)) + a1 = &j.; + a2 = &k.; + a3 = &l.; + a4 = &m.; + run; + + data test3; + set sashelp.class; + %array(alpha[*] j k l m (101 102 103 104), macarray=Y) + a1 = %alpha(1); + a2 = %alpha(2); + a3 = %alpha(3); + a4 = %alpha(4); + a5 = %alpha(555); + run; + + data test4; + set sashelp.class; + %array(beta[*] j k l m (101 102 103 104), vnames=Y, macarray=Y) + a1 = "%beta(1)"; + a2 = "%beta(2)"; + a3 = "%beta(3)"; + a4 = "%beta(4)"; + a5 = "%beta(555)"; + run; + + data test5; + set sashelp.class; + %array(gamma[4] $ 12 ("101" "102" "103" "104"), macarray=Y) + a1 = "%gamma(1)"; + a2 = "%gamma(2)"; + a3 = "%gamma(3)"; + a4 = "%gamma(4)"; + a5 = "%gamma(555)"; + run; + + data test6; + set sashelp.class; + %array(ds = sashelp.cars, vars = Cylinders|, macarray=Y) + a0 = %Cylinders(0); + a1 = %Cylinders(1); + a2 = %Cylinders(2); + a3 = %Cylinders(3); + a4 = %Cylinders(4); + a5 = %Cylinders(555); + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 10.** Creating an array from a dataset, basic case. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(ds = sashelp.class, vars = height weight age) + %put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 11. Creating an array from a dataset, advanced. + If: `vars = height#h weight weight|w age|` + then create: + 1. macroarray "h" with ALL(#) values of variable "height" + 2. macroarray "weight" with ALL(no separator is equivalent to #) values of variable "weight" + 3. macroarray "w" with UNIQUE(|) values of variable "weight" + 4. macroarray "age" with UNIQUE(|) values of variable "age" + Currently the only separator in VARS is a space. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(ds = sashelp.class, vars = height#h weight weight|w age|, q=1) + %put _user_; + + %array(ds = sashelp.class, vars = height#hght weight weight|wght age|, macarray=Y, q=1) + %put *%hght(&hghtLBOUND.)**%weight(2)**%wght(&wghtHBOUND.)**%age(3)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 12.** Creating an array from a dataset with dataset options + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(ds = sashelp.cars(obs=100 where=(Cylinders=6)), vars = Make| Type| Model, macarray=Y) + %put *%make(&makeLBOUND.)*%Model(2)*%Model(3)*%Model(4)*%type(&typeHBOUND.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 13.** Creating an array and macro from existing list of macrovariables + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let myTest3 = 13; + %let myTest6 = 16; + %let myTest9 = 19; + + %array(myTest, macarray=M, q=1) + %do_over(myTest, phrase = %nrstr(%put *&_I_.*%myTest(&_I_.)*;)) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + + +## >>> `%concatArrays()` macro: <<< ########### + +The `%concatArrays()` macro allows to concatenate +two macroarrays created by the `%array()` macro. + +By default values of the second macroarray are removed. + +Dimensions of the first macroarray are extended. + +The `%concatArrays()` macro executes like a pure macro code. + +### SYNTAX: ##################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%concatArrays( + first + ,second + <,removeSecond=Y> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `first` - *Required*, a name of a macroarray created by the `%array()` macro. + +2. `second` - *Required*, a name of a macroarray created by the `%array()` macro. + +* `removeSecond=Y` - *Optional*, default value `Y`, if set to `Y` then + the second array is removed. + + + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Concatenate macroarrays LL and MM. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(ll[2:4] $ 12, + function = quote(put(today() + 10*_I_, yymmdd10.)), + macarray=Y + ) + %array(mm[10:13] $ 12000, + function = quote(repeat("A",123*_I_)), + macarray=Y + ) + %put *%ll(2)*%ll(3)*%ll(4)*; + + %concatArrays(ll, mm); + %put *%ll(2)*%ll(3)*%ll(4)*%ll(5)*%ll(6)**%ll(7)*%ll(8)*; + + %put *%mm(10)**%mm(11)*%mm(12)*%mm(13)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Error handling. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %concatArrays(ll, ) + %concatArrays(, mm) + + %concatArrays(noExistA, noExistB) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- + + + +## >>> `%deleteMacArray()` macro: <<< ####### + +The `%deleteMacArray()` macro allows to delete +macroarrays created by the `%array()` macro. + +The `%deleteMacArray()` macro executes like a pure macro code. + +### SYNTAX: ##################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%deleteMacArray( + arrs + <,macarray=N> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `arrs` - *Required*, a space separated list of manes + of macroarray created by the `%array()` macro. + +* `macarray=N` - *Optional*, indicator should a macro + associated with macroarray to be deleted? + If `Y` or `YES` then the associated macro is deleted. + + + + +## >>> `%do_over()` macro: <<< ###################### + +The code of the macro was inspired by +*Ted Clay's* and *David Katz's* macro `%do_over()`. + +The `%DO_OVER()` macro allows to iterate over macroarray created with +the `macarray=Y` parameter of the `%ARRAY()` macro. + +The `%do_over()` macro executes like a pure macro code. + +### SYNTAX: ##################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%do_over( + array + <,phrase=%nrstr(%&array(&_I_.))> + <,between=%str( )> + <,which = > +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `array` - *Required*, indicates a macroarray which metadata (Lbound, Hbouns) + are to be used to loop in the `%do_over()` + +* `phrase=` - *Optional*, Default value `%nrstr(%&array(&_I_.))`, + a statement to be called in each iteration + of the internal do_over's loop. Loop iterator is `_I_`, + if you want to use `_I_` or array name + [e.g. `%myArr(&_I_.)`] *enclose it* in the `%NRSTR()` + macro quoting function. + +* `between=` - *Optional*, default value `%str( )` (space), + a statement to be called in between each + iteration of the internal do_over loop. + If macroquoted (e.g. `%str( + )`) then the `%unquote()` + function is automatically applied. + +* `which=` - *Optional*, a _SPACE_ separated list of indexes which + should be used to iterate over selected macroarray. + Possible special characters are `H` and `L` which means + *high* and *low* bound of an array, list could be set with + colons(`:`) in form of `start:end:by` (*no spaces between!*), + if `by` is omitted the default is `1`. If possible use + `1:5` rather `1 2 3 4 5` since the firs works faster. + + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple looping. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(beta[*] j k l m (101 102 103 104), vnames=Y, macarray=Y) + + %put #%do_over(beta)#; + + %put #%do_over(beta, phrase=%nrstr("%beta(&_I_.)"), between=%str(,))#; + + data test1; + %array(beta[*] j k l m (101 102 103 104), vnames=Y, macarray=Y) + %do_over(beta, phrase=%nrstr(a&_I_. = "%beta(&_I_.)";)) + put _all_; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Multiple arrays looping. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(alpha[*] j k l m n, vnames=Y, macarray=Y) + %array( beta[5] $ , function = "a", macarray=Y) + %array(gamma[4] (101 102 103 104), macarray=Y) + + data test2; + call streaminit(123); + %do_over(beta + , phrase = %nrstr(%beta(&_I_.) = %gamma(&_I_.) * rand('Uniform'); output;) + , between = put _all_; + ); + put _all_; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Multiple arrays looping, cont. + Create multiple datasets. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %do_over(beta + , phrase = %nrstr( + data %alpha(&_I_.)2; + call streaminit(123); + %beta(&_I_.)x = %gamma(&_I_.) * rand('Uniform'); + output; + run; + ) + ) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Multiple arrays looping, cont. + Create multiple datasets using a macro. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %macro doit(ds, var=a, val=1); + data &ds.; + call streaminit(123); + &var. = &val. * rand('Uniform'); + output; + run; + %mend doit; + + %do_over(beta + , phrase = %nrstr( + %DOIT(%alpha(&_I_.)1, var = %beta(&_I_.), val = %gamma(&_I_.)) + ) + ) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** `%do_over()` inside `%array()` + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(test[*] x1-x12 (1:12), macarray=Y) + + %put **%test(1)**%test(12)**; + + %put #%do_over(test)#; + + %array(abc[*] x1-x12 (%do_over(test,phrase=%nrstr(%eval(100-%test(&_I_.))))), macarray=Y) + + %put **%abc(1)**%abc(12)**; + + %put #%do_over(abc)#; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** Looping over array with *macroquoted* separator. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(alpha[11] (5:15), macarray=Y) + + %let x = %do_over(alpha + , phrase = %NRSTR(%alpha(&_I_.)) + , between= %str( + ) + ); + %put &=x.; + %put %sysevalf(&x.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** Working with the `WHICH=` optional parameter + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(test[*] x01-x12, vnames= Y, macarray=Y) + + %put #%do_over(test)#; + + %put #%do_over(test, which= 1 3 5)#; + + %put #%do_over(test, which= 1:5)#; + + %put #%do_over(test, which= 1:5:2 7 8)#; + + %put #%do_over(test, which= L:H l:h)#; + + %put #%do_over(test, which= L:3 10:h)#; + + %put #%do_over(test, which= L:H h:l:-1 13 14)#; + + %put #%do_over(test, which= %eval(1+1):%eval(5+1))#; + + %put #%do_over(test, which= L:H h:l:-1 13 14, between=%str(,))#; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- + + + +## >>> `%do_over2()` macro: <<< #################### + +The code of the macro was inspired by +*Ted Clay's* and *David Katz's* macro `%do_over()`. + +The `%DO_OVER2()` macro allows to iterate over *two* macroarray created with +the `macarray=Y` parameter of the `%ARRAY()` macro. + +The `%do_over2()` macro executes like a pure macro code. + +### SYNTAX: ##################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%do_over2( + arrayI + ,arrayJ + <,phrase=%nrstr(%&arrayI(&_I_.) %&arrayJ(&_J_.))> + <,between=%str( )> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `arrayI` - Required, indicates the first macroarray which metadata (Lbound, Hbouns) + are to be used in the outer loop in the `%do_over2()` + +2. `arrayJ` - Required, indicates the second macroarray which metadata (Lbound, Hbouns) + are to be used in the inner loop in the `%do_over2()` + +* `phrase=` - *Optional*, default value `%nrstr(%&arrayI(&_I_.) %&arrayJ(&_J_.))`, + a statement to be called in each iteration + of the *inner* loop. The outer loop iterator is `_I_`, + the inner loop iterator is `_J_`, + if you want to use `_I_`, `_J_`, or arrays names + [e.g. `%myArr(&_I_.)`] *enclose them* in the `%NRSTR()` + macro quoting function. + +* `between=` - *Optional*, default value `%str( )` (space), + a statement to be called in between each + iteration of the internal do_over2 loop. + If macroquoted (e.g. `%str( + )`) then the `%unquote()` + function is automatically applied. + + + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Looping over two arrays. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(alpha[*] j k l m n, vnames=Y, macarray=Y) + %array( beta[4] (101 102 103 104), macarray=Y) + + %put *%do_over2(alpha, beta + , phrase = %NRSTR((%alpha(&_I_.), %beta(&_J_))) + )*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Looping over two arrays with a separator. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(alpha[11] (5:15), macarray=Y) + %array( beta[ 4] (101 102 103 104), macarray=Y) + + %let x = %do_over2(alpha, beta + , phrase = %NRSTR((%alpha(&_I_.) * %beta(&_J_))) + , between= + + ); + %put &=x.; + %put %sysevalf(&x.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Looping over two arrays with *macroquoted* separator. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(alpha[11] (5:15), macarray=Y) + %array( beta[ 4] (101 102 103 104), macarray=Y) + + %let x = %do_over2(alpha, beta + , phrase = %NRSTR((%alpha(&_I_.) * %beta(&_J_))) + , between= %str( + ) + ); + %put &=x.; + %put %sysevalf(&x.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- + + + +## >>> `%do_over3()` macro: <<< #################### + +The code of the macro was inspired by +*Ted Clay's* and *David Katz's* macro `%do_over()`. + +The `%DO_OVER3()` macro allows to iterate over *three* macroarray created with +the `macarray=Y` parameter of the `%ARRAY()` macro. + +The `%do_over3()` macro executes like a pure macro code. + +### SYNTAX: ##################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%do_over2( + arrayI + ,arrayJ + ,arrayK + <,phrase=%nrstr(%&arrayI(&_I_.) %&arrayJ(&_J_.) %&arrayK(&_K_.))> + <,between=%str( )> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `arrayI` - *Required*, indicates the first macroarray which metadata (Lbound, Hbouns) + are to be used in the outer loop in the `%do_over3()` + +2. `arrayJ` - *Required*, indicates the second macroarray which metadata (Lbound, Hbouns) + are to be used in the inner loop in the `%do_over3()` + +3. `arrayK` - *Required*, indicates the third macroarray which metadata (Lbound, Hbouns) + are to be used in the inner loop in the `%do_over3()` + +* `phrase=` - *Optional*, default value `%nrstr(%&arrayI(&_I_.) %&arrayJ(&_J_.) %&arrayK(&_K_.))`, + a statement to be called in each iteration + of the *inner* loop. The *outer* loop iterator is `_I_`, + the *middle* loop iterator is `_J_`, the *inner* loop iterator is `_K_`, + if you want to use `_I_`, `_J_`, `_K_`, or arrays names + [e.g. `%myArr(&_I_.)`] *enclose them* in the `%NRSTR()` + macro quoting function. + +* `between=` - *Optional*, default value `%str( )` (space), + a statement to be called in between each + iteration of the internal do_over2 loop. + If macroquoted (e.g. `%str( + )`) then the `%unquote()` + function is automatically applied. + + + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Looping over 3 macroarrays. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(a1_[2] (0 1), macarray=Y) + %array(a2_[2] (2 3), macarray=Y) + %array(a3_[2] (4 5), macarray=Y) + + %do_over3(a1_, a2_, a3_ + , phrase = %NRSTR(%put (%a1_(&_I_.), %a2_(&_J_), %a3_(&_K_));) + ) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Looping 3 times over a macroarray. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(a[0:2] (0 1 2), macarray=Y) + + %do_over3(a, a, a + , phrase = %NRSTR(%put (%a(&_I_.), %a(&_J_), %a(&_K_));) + ) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- + + + +## >>> `%make_do_over()` macro: <<< ########### + +The code of the macro was inspired by +*Ted Clay's* and *David Katz's* macro `%do_over()`. + +The `%make_do_over()` macro allows to generate +the `%DO_OVER()` macros. It works *only* for *n>3*! + +The `%make_do_over()` macro does *not* executes like a pure macro code. + +### SYNTAX: ##################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%make_do_over( + size +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `size` - *Required*, indicates the number of dimensions + (i.e. inner loops) of the `%DO_OVER()` macro. + + + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Code of created "4-loop" `%DO_OVER4()` macro + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %macro do_over4( + arrayI1, + arrayI2, + arrayI3, + arrayI4, + phrase=%nrstr( + %&arrayI1(&_I1_.) + %&arrayI2(&_I2_.) + %&arrayI3(&_I3_.) + %&arrayI3(&_I4_.) + ), + between=%str( ) + ); + %local _I1_ _I2_ _I3_ _I4_; + %do _I1_ = &&&arrayI1.LBOUND %to &&&arrayI1.HBOUND; + %do _I2_ = &&&arrayI2.LBOUND %to &&&arrayI2.HBOUND; + %do _I3_ = &&&arrayI3.LBOUND %to &&&arrayI3.HBOUND; + %do _I4_ = &&&arrayI4.LBOUND %to &&&arrayI4.HBOUND; + %if not ( + &_I1_. = &&&arrayI1.LBOUND + AND &_I2_. = &&&arrayI2.LBOUND + AND &_I3_. = &&&arrayI3.LBOUND + AND &_I4_. = &&&arrayI4.LBOUND + ) + %then %do;%unquote(&between.)%end;%unquote(%unquote(&phrase.)) + %end; + %end; + %end; + %end; + %mend do_over4; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Create a "4-loop" `%DO_OVER4()` macro + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %make_do_over(4); + + %array(a1_[2] (0 1), macarray=Y) + + %do_over4(a1_, a1_, a1_, a1_ + , phrase = %NRSTR(%put (%a1_(&_I1_.), %a1_(&_I2_), %a1_(&_I3_), %a1_(&_I4_));) + ) + + %put *%do_over4(a1_, a1_, a1_, a1_ + , between = * + )*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** Create a "5-loop" `%DO_OVER5()` macro + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %make_do_over(5); + + %array(a1_[2] (0 1), macarray=Y) + + %do_over5(a1_, a1_, a1_, a1_, a1_ + , phrase = %NRSTR(%put (%a1_(&_I1_.), %a1_(&_I2_), %a1_(&_I3_), %a1_(&_I4_), %a1_(&_I5_));) + ) + + %put *%do_over5(a1_, a1_, a1_, a1_, a1_ + , between = * + )* + ; + + options nomprint; + data test2; + %do_over5(a1_, a1_, a1_, a1_, a1_ + , phrase = %NRSTR(x1 = %a1_(&_I1_.); x2 = %a1_(&_I2_); x3 = %a1_(&_I3_); x4 = %a1_(&_I4_); x5 = %a1_(&_I5_);) + , between = output; + ) + output; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Create all from 6 to 10 "do_overs" + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %array(loop[6:10] (6:10), macarray=Y) + %do_over(loop + , phrase = %nrstr( + %make_do_over(%loop(&_I_.)) + ) + ); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- + +## >>> `%mcHashTable()` macro: <<< ####################### + +The `%mcHashTable()` macro provided in the package +is designed to facilitate the idea of a "macro hash table" +concept, i.e. *a list of macrovariables with common prefix +and suffixes generated as a hash digest* which allows +to use values other than integers as indexes. + +The `%mcHashTable()` macro allows to generate other macros +which behaves like hash tables or dictionaries. See examples below. + +The `%mcHashTable()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%mcHashTable( + H + <,METHOD> + <,HASH=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `H` - *Required*, a hash table macro name and a declaration/definition, + e.g. `mcHashTable(HT)`. It names a macro which is generated by + the `%mcHashTable()` macro. Provided name cannot be empty + or an underscore (`_`). No longer than *16* characters. + +2. `METHOD` - *Optional*, if empty (or DECLARE or DCL) then the code of + a macro hash table is compiled. + If `DELETE` then the macro hash table named by `H` and all + macrovariables named like "`&H._`" are deleted. + +* `HASH=` - *Optional*, indicates which hashing algorithms should be used, + available values are `CRC32` or `MD5`, the `CRC32` is the default. + +--- + +### THE CREATED MACRO `%&H.()`: #################################################### + +The created macro imitates behaviour of a hash table or a dictionary. +It is *not* dedicated for "long-ish" lists (above 1000 elements) since +the performance may be poor. + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%&H.( + METHOD + <,KEY=> + <,DATA=> +) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `METHOD` - *Required*, indicate what behaviour should be executed. + Allowed values are: + - `ADD`, adds key and data portion to the macro hash table, + *multiple data portions* are available for one key. + - `FIND`, tests if given key exists in the macro hash table + and, if yes, returns data value associated with the key. + For multiple data portions see the `data=` parameter. + - `DP` (data portion) or `CHECK`, returns the number of data + portions for a given key. + - `CLEAR` removes all data and keys values. + - `KEYIDX`, allows to get data by the key index rather than value. + - `KEYVAL`, returns key value for a given key index. + - `CHECKIDX`, returns the number of data portions for + a given key index. + +* `KEY=` - *Optional*, provides key value for `ADD`, `FIND`,`DP`, `CHECK` + `CHECKIDX`, `KEYIDX`, and `KEYVAL` methods. Leading and trimming + spaces are removed from the value. + The `hashing(CRC32,...)` function or the `MD5(...)` function is + used to generate the hash. + +* `DATA=` - *Optional*, provides data value for the `ADD` method and + for the`FIND` method provides data portion number to be + extracted. Default value is `1` (used by the `FIND` method). + + +When macro is executed and when data are added the following types of +*global* macrovariables are created: +- `&H._########`, +- `&H._########_Xk`, +- `&H._########_Xi`, +- `&H._########_Xi_j`, +- `&H._KEYNUM`, +- and `&H._KEY_i`. + +The `#` represents value generated by the `hashing(CRC32,...)` function +or the `MD5(...)` function for the given key. + +The first type keeps information about possible collision for the key. + +The second type keeps information about value of a given key, +the `X` keeps the track of other colliding keys. + +The third type keeps information about number of data portions +for given key, the `X` keeps the track of other colliding keys. + +The fourth type keeps the data portion, the `j` indicates data portion number. + +The fifth type keeps the number of unique values of the key. + +The sixth type keeps the list of unique values of the key, +the `i` indicates key number. + +See examples below to see use cases. + +--- + +### EXAMPLES AND USECASES: #################################################### + + +**EXAMPLE 1.** Basic use-case. + Creating macro hash table, macro `HT` is generated. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcHashTable(HT) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Add elements to the `HT`. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%HT(ADD,key=x,data=17) +%HT(ADD,key=y,data=42) +%HT(ADD,key=z,data=303) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Add some duplicates for the key x. + See macrovariables created. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%HT(ADD,key=x,data=18) +%HT(ADD,key=x,data=19) + +%put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Check the number od data portions in macrohash + for the key `x` and non existing key `t`. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put ##%HT(DP,key=x)##; +%put ##%HT(DP,key=t)##; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Check the number od data portions in macrohash + for the key index 1 and 4. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put ##%HT(CHECKIDX,key=1)##; +%put ##%HT(CHECKIDX,key=4)##; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Prints first data values for various keys. + Key `t` does not exist in the macrohash. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put #%HT(FIND,key=x)#; +%put #%HT(FIND,key=y)#; +%put #%HT(FIND,key=z)#; +%put #%HT(FIND,key=t)#; + +%put #%HT(FIND,key=x,data=2)#; +%put #%HT(FIND,key=x,data=3)#; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Print first and subsequent data values + for a given KeyIDX. Index `4` does not exist. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put #%HT(KEYIDX,key=1)#; +%put #%HT(KEYIDX,key=2)#; +%put #%HT(KEYIDX,key=3)#; +%put #%HT(KEYIDX,key=4)#; + +%put #%HT(KEYIDX,key=1,data=2)#; +%put #%HT(KEYIDX,key=1,data=3)#; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Print the key values for a given KeyIDX. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put #%HT(KEYVAL,key=1)#; +%put #%HT(KEYVAL,key=2)#; +%put #%HT(KEYVAL,key=3)#; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Clear and delete macro hash table `HT`. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%HT(CLEAR) +%mcHashTable(HT,DELETE) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Combine `CHECK` and `FIND` methods + with macros `%array()` and `%do_over()` + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcHashTable(H) +%H(ADD,key=x,data=17) +%H(ADD,key=x,data=18) +%H(ADD,key=x,data=19) + +%array(A[%H(CHECK,key=x)]); + +%put %do_over(A, phrase=%nrstr( + %H(FIND,key=x,data=&_i_) +), between = %str(,)); + +%mcHashTable(H,delete) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Populate macro hash table from a dataset. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcHashTable(CLASS) +%let t = %sysfunc(datetime()); +data _null_; + set sashelp.class; + call execute('%CLASS(ADD,key=' !! name !! ',data=' !! age !! ')'); + call execute('%CLASS(ADD,key=' !! name !! ',data=' !! weight !! ')'); + call execute('%CLASS(ADD,key=' !! name !! ',data=' !! height !! ')'); +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put _user_; +%CLASS(CLEAR) + + +%mcHashTable(CARS) +%let t = %sysfunc(datetime()); +data _null_; + set sashelp.cars; + call execute('%CARS(ADD,key=' !! catx("|",make,model) !! ',data=' !! MPG_CITY !! ')'); +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%* %put _user_; +%CARS(CLEAR) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Data portion may require quoting and un-quoting.. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcHashTable(CODE) +%CODE(CLEAR) +%CODE(ADD,key=data, data=%str(data test; x = 42; run;)) +%CODE(ADD,key=proc, data=%str(proc print; run;)) +%CODE(ADD,key=macro,data=%nrstr(%put *****;)) + +%CODE(FIND,key=data) +%CODE(FIND,key=proc) +%unquote(%CODE(FIND,key=macro)) + +%mcHashTable(CODE,DELETE) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Longer lists. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let size = 1000; + +%mcHashTable(AAA) +%mcHashTable(BBB) +%mcHashTable(CCC) +%mcHashTable(DDD) + +%let t = %sysfunc(datetime()); +data _null_; + do i = 1 to &size.; + call execute(cats('%AAA(ADD,key=A', i, ',data=', i, ')')); + end; +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=AAA_KEYSNUM; +%AAA(CLEAR) + +%let t = %sysfunc(datetime()); +data _null_; + do i = 1 to &size.; + call execute(cats('%BBB(ADD,key=B', i, ',data=', i, ')')); + call execute(cats('%BBB(ADD,key=B', i, ',data=', i+1, ')')); + end; +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=BBB_KEYSNUM; +%BBB(CLEAR) + +%let t = %sysfunc(datetime()); +data _null_; + t= datetime(); + do i = 1 to &size.; + call execute(cats('%CCC(ADD,key=C', i, ',data=', i, ')')); + end; + t = datetime() - t; + put t=; + t= datetime(); + do i = 1 to &size.; + call execute(cats('%CCC(ADD,key=C', i, ',data=', i+1, ')')); + end; + t = datetime() - t; + put t=; +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); + +%let t = %sysfunc(datetime()); +data test; + do i = 1 to &size.; + x = resolve(cats('%CCC(FIND,key=C', i, ',data=1)')); + y = resolve(cats('%CCC(FIND,key=C', i, ',data=2)')); + output; + end; +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=CCC_KEYSNUM; +%CCC(CLEAR) + +%let t = %sysfunc(datetime()); +data _null_; + do i = 1 to &size.; + call execute(cats('%DDD(ADD,key=D,data=', i, ')')); + end; +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=DDD_KEYSNUM; +%put %DDD(CHECK,key=D); +%DDD(CLEAR) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** Forbidden names. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcHashTable() +%mcHashTable(_) + +%mcHashTable(ABCDEFGHIJKLMNOPQ) %* bad; +%mcHashTable(ABCDEFGHIJKLMNOP) %* good; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 7.** Hashing algorithms. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcHashTable(H1,DCL,HASH=MD5) +%mcHashTable(H2,DECLARE,HASH=CRC32) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + +## >>> `%mcDictionary()` macro: <<< ####################### + +The `%mcDictionary()` macro provided in the package +is designed to facilitate the idea of a "macro dictionary" +concept, i.e. *a list of macrovariables with common prefix +and suffixes generated as a hash digest* which allows +to use values other than integers as indexes. + +The `%mcDictionary()` macro allows to generate other macros +which behaves like a dictionary. See examples below. + +The `%mcDictionary()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%mcDictionary( + H + <,METHOD> + <,DS=> + <,K=Key> + <,D=Data> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `H` - *Required*, a dictionary macro name and a declaration/definition, + e.g. `mcDictionary(HT)`. It names a macro which is generated by + the `%mcDictionary()` macro. Provided name cannot be empty + or an underscore (`_`). No longer than *16* characters. + +2. `METHOD` - *Optional*, if empty (or DECLARE or DCL) then the code of + a macro dictionary is compiled. + If `DELETE` then the macro dictionary named by `H` and all + macrovariables named like "`&H._`" are deleted. + +* `DS=` - *Optional*, if NOT empty then the `&DS.` dataset is used to + populate dictionary with keys from variable `&K.` and data + from variable `&D.` Works only during declaration. + +* `K=` - *Optional*, if the `&DS.` is NOT empty then `&K.` holds a name of + a variable which keeps or an expression which generates keys values. + Default is `Key`. + +* `D=` - *Optional*, if the `&DS.` is NOT empty then `&D.` holds a name of + a variable which keeps or an expression which generates data values. + Default is `Data`. + +--- + +### THE CREATED MACRO `%&H.()`: #################################################### + +The created macro imitates behaviour of a dictionary. + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%&H.( + METHOD + <,KEY=> + <,DATA=> +) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `METHOD` - *Required*, indicate what behaviour should be executed. + Allowed values are: + - `ADD`, adds key and data portion to the macro dictionary, + *multiple data portions* are NOT available for one key. + - `FIND`, tests if given key exists in the macro dictionary + and, if yes, returns data value associated with the key. + For multiple data portions see the `data=` parameter. + - `CHECK`, returns indicator if the key exists in dictionary. + - `DEL`, removes key and data portion from the macro dictionary. + - `LIST`, prints out a dictionary to the log. + - `CLEAR` removes all data and keys values. + +* `KEY=` - *Optional*, provides key value for `ADD`, `FIND`, `CHECK` + and `DEL` methods. + Leading and trimming spaces are removed from the value. + The `MD5(...)` function is used to generate the hash. + Default value is `_`. + +* `DATA=` - *Optional*, provides data value for the `ADD` method. + Default value is blank. + + +When macro is executed and when data are added the following types of +*global* macrovariables are created: +- `&H._########_K`, +- `&H._########_V`, +- `&H._KEYSNUM`. + +The `#` represents value generated by the `MD5(...)` function for the given key. + +The first type keeps information about the key. + +The second type keeps information about the value of a given key + +The third type keeps the number of unique values of the key. + +See examples below to see use cases. + +--- + +### EXAMPLES AND USECASES: #################################################### + + +**EXAMPLE 1.** Basic use-case. + Creating macro dictionary, macro `Dict` is generated. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcDictionary(Dict) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Add elements to the `Dict`. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%Dict(ADD,key=x,data=17) +%Dict(ADD,key=y y,data=42) +%Dict(ADD,key=z z z,data=303) + +%put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Add some duplicates for the key x. + See macrovariables created. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%Dict(ADD,key=x,data=18) + +%put _user_; + +%Dict(ADD,key=x,data=19) + +%put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Check for the key `x` and non existing key `t`. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put ##%Dict(CHECK,key=x)##; +%put ##%Dict(CHECK,key=t)##; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Prints data values for various keys. + Key `t` does not exist in the macrodictionary. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put #%Dict(FIND,key=x)#; +%put #%Dict(FIND,key=y y)#; +%put #%Dict(FIND,key=z z z)#; +%put #%Dict(FIND,key=t)#; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + List dictionary content to the log. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%Dict(LIST); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Delete keys. + Key `t` does not exist in the macrodictionary. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put #%Dict(DEL,key=z z z)#; +%put _user_; +%put #%Dict(DEL,key=t)#; +%put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Clear and delete macro dictionary `Dict`. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%Dict(CLEAR) +%put _user_; + +%mcDictionary(Dict,DELETE) +%put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2A.** Populate macro dictionary from a dataset "by hand". + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcDictionary(CLASS) +%let t = %sysfunc(datetime()); +data _null_; + set sashelp.class; + call execute('%CLASS(ADD,key=' !! name !! ',data=' !! age !! ')'); +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=Class_KEYSNUM.; +%put _user_; +%CLASS(CLEAR) + + +%mcDictionary(CARS) +%let t = %sysfunc(datetime()); +data _null_; + set sashelp.cars(obs=42); + call execute('%CARS(ADD,key=' !! catx("|",make,model,type) !! ',data=' !! put(MPG_CITY*10,dollar10.2) !! ')'); +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=CARS_KEYSNUM.; +%CARS(LIST); + +%put %CARS(F,key=Audi|TT 3.2 coupe 2dr (convertible)|Sports); + +%CARS(CLEAR) +%put &=CARS_KEYSNUM.; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2B.** Populate macro dictionary from a dataset "automatically". + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let t = %sysfunc(datetime()); +%mcDictionary(CLASS,DCL,DS=sashelp.class,k=name,d=_N_) +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=CLASS_KEYSNUM.; +%put _user_; +%CLASS(CLEAR) + + +%let t = %sysfunc(datetime()); +%mcDictionary(CARS,DCL,DS=sashelp.cars(obs=42),k=catx("|",make,model,type),d=put(MPG_CITY*10,dollar10.2)) +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=CARS_KEYSNUM.; +%CARS(LIST); + +%put %CARS(F,key=Audi|TT 3.2 coupe 2dr (convertible)|Sports); + +%CARS(CLEAR) +%put &=CARS_KEYSNUM.; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Data portion may require quoting and un-quoting. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcDictionary(CODE) +%CODE(CLEAR) +%CODE(ADD,key=data, data=%str(data test; x = 42; run;)) +%CODE(ADD,key=proc, data=%str(proc print; run;)) +%CODE(ADD,key=macro,data=%nrstr(%put *1*2*3*4*;)) + +%CODE(FIND,key=data) +%CODE(FIND,key=proc) +%unquote(%CODE(FIND,key=macro)) + +%CODE(LIST); + +%mcDictionary(CODE,DELETE) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Longer lists. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let size = 1000; + +%mcDictionary(AAA) + +%let t = %sysfunc(datetime()); +data _null_; + do i = 1 to &size.; + call execute(cats('%AAA(ADD,key=A', i, ',data=', i, ')')); + end; +run; +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put %AAA(F,key=A555) %AAA(CHECK,key=A555); +%put &=AAA_KEYSNUM; +%AAA(CLEAR) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Forbidden names. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%mcDictionary() +%mcDictionary(_) + +%mcDictionary(ABCDEFGHIJKLMNOPQ) %* bad; +%mcDictionary(ABCDEFGHIJKLMNOP) %* good; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** More fun with datasets. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +data work.metadata; + input key :$16. data :$128.; +cards; +ID ABC-123-XYZ +path /path/to/study/data +cutoffDT 2023-01-01 +startDT 2020-01-01 +endDT 2024-12-31 +MedDRA v26.0 +; +run; +proc print; +run; + +%mcDictionary(Study,dcl,DS=work.metadata) + +%put _user_; + +%put *%Study(F,key=ID)**%Study(C,key=ID)*; + +title1 "Study %Study(F,key=ID) is located at %Study(F,key=path)"; +title2 "it starts %Study(F,key=startDT) and ends %Study(F,key=endDT)"; +footnote "MedDRA version: %Study(F,key=MedDRA)"; + +proc print data=sashelp.class(obs=7); +run; + +title; +footnote; + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + +## >>> `%QzipArrays()` macro: <<< ####################### + +The zipArrays() and QzipArrays() macros +allow to use a function on elements of pair of +macro arrays. + +For two macroarrays the corresponding +elements are taken and the macro applies a function, provided by user, +to calculate result of the function on taken elements. + +When one of the arrays is shorter then elements are, by default, +"reused" starting from the beginning. But this behaviour can be altered. +See examples for the details. + +By default newly created macroarray name is concatenation +of first 13 characters of names of arrays used to create the new one, +e.g. if arrays names are `abc` and `def` then the result name is `abcdef`, +if arrays names are `abcd1234567890` and `efgh1234567890` then the result +name is `abcd123456789efgh123456789` + +The `zipArrays()` returns unquoted value [by `%unquote()`]. +The `QzipArrays()` returns quoted value [by `%superq()`]. + +See examples below for the details. + +The `%QzipArrays()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%QzipArrays( + first + ,second + <,function=> + <,operator=> + <,argBf=> + <,argMd=> + <,argAf=> + <,format=> + <,result=> + <,macarray=> + <,reuse=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `first` - *Required*, a space separated list of texts. + +2. `second` - *Required*, a space separated list of texts. + +* `function = cat` - *Optional*, default value is `cat`, + a function which will be applied + to corresponding pairs of elements of + the first and the second list. + +* `operator =` - *Optional*, default value is empty, + arithmetic infix operator used with elements + the first and the second list. The first + list is used on the left side of the operator + the second list is used on the right side + of the operator. + +* `argBf =` - *Optional*, default value is empty, + arguments of the function inserted + *before* elements the first list. + If multiple should be comma separated. + +* `argMd =` - *Optional*, default value is empty, + arguments of the function inserted + *between* elements the first list and + the second list. + If multiple should be comma separated. + +* `argAf =` - *Optional*, default value is empty, + arguments of the function inserted + *after* elements the second list. + If multiple should be comma separated. + +* `format=` - *Optional*, default value is empty, + indicates a format which should be used + to format the result, does not work when + the `operator=` is used. + +* `result=` - *Optional*, default value is empty, + indicates a name of newly created macroarray, + by default created macroarray name is concatenation + of first 13 characters of names of arrays used + to create the new one. + +* `macarray=N` - *Optional*, default value is `N`, + if set to `Y`/`YES` then a macro, named with + the array name, is compiled to create convenient + envelope for multiple ampersands, see the + `%array()` macro for details. + +* `reuse=Y` - *Optional*, default value is `Y`, + when one of the arrays is shorter then elements + are *reused* starting from the beginning. + If `CP` then function is executed on the *Cartesian + product* of arrays elements. Any other value will + cut the process with the end of the shorter array. + See examples for the details. + +### EXAMPLES AND USECASES: #################################################### + +See examples in `%zipArrays()` help for the details. + +--- + +## >>> `%zipArrays()` macro: <<< ####################### + +The zipArrays() and QzipArrays() macros +allow to use a function on elements of pair of +macro arrays. + +For two macroarrays the corresponding +elements are taken and the macro applies a function, provided by user, +to calculate result of the function on taken elements. + +When one of the arrays is shorter then elements are, by default, +"reused" starting from the beginning. But this behaviour can be altered. +See examples for the details. + +By default newly created macroarray name is concatenation +of first 13 characters of names of arrays used to create the new one, +e.g. if arrays names are `abc` and `def` then the result name is `abcdef`, +if arrays names are `abcd1234567890` and `efgh1234567890` then the result +name is `abcd123456789efgh123456789` + +The `zipArrays()` returns unquoted value [by `%unquote()`]. +The `QzipArrays()` returns quoted value [by `%superq()`]. + +See examples below for the details. + +The `%zipArrays()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%zipArrays( + first + ,second + <,function=> + <,operator=> + <,argBf=> + <,argMd=> + <,argAf=> + <,format=> + <,result=> + <,macarray=> + <,reuse=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `first` - *Required*, a space separated list of texts. + +2. `second` - *Required*, a space separated list of texts. + +* `function = cat` - *Optional*, default value is `cat`, + a function which will be applied + to corresponding pairs of elements of + the first and the second list. + +* `operator =` - *Optional*, default value is empty, + arithmetic infix operator used with elements + the first and the second list. The first + list is used on the left side of the operator + the second list is used on the right side + of the operator. + +* `argBf =` - *Optional*, default value is empty, + arguments of the function inserted + *before* elements the first list. + If multiple should be comma separated. + +* `argMd =` - *Optional*, default value is empty, + arguments of the function inserted + *between* elements the first list and + the second list. + If multiple should be comma separated. + +* `argAf =` - *Optional*, default value is empty, + arguments of the function inserted + *after* elements the second list. + If multiple should be comma separated. + +* `format=` - *Optional*, default value is empty, + indicates a format which should be used + to format the result, does not work when + the `operator=` is used. + +* `result=` - *Optional*, default value is empty, + indicates a name of newly created macroarray, + by default created macroarray name is concatenation + of first 13 characters of names of arrays used + to create the new one. + +* `macarray=N` - *Optional*, default value is `N`, + if set to `Y`/`YES` then a macro, named with + the array name, is compiled to create convenient + envelope for multiple ampersands, see the + `%array()` macro for details. + +* `reuse=Y` - *Optional*, default value is `Y`, + when one of the arrays is shorter then elements + are *reused* starting from the beginning. + If `CP` then function is executed on the *Cartesian + product* of arrays elements. Any other value will + cut the process with the end of the shorter array. + See examples for the details. + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple concatenation of elements: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%array(a[*] x1-x3 (1:3)) +%array(b[*] x1-x5 (11:15)) + +%put _user_; + +%zipArrays(a, b); +%put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Shorter list is "reused": +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%array(a[6] (1:6)) +%array(b[3] (10 20 30)) + +%zipArrays(a, b, result=A_and_B, macarray=Y); +%put %do_over(A_and_B); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Use of the `operator=`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%array(c[0:4] (000 100 200 300 400)) +%array(d[2:16] (1002:1016)) + +%zipArrays(c, d, operator=+, result=C_plus_D, macarray=Y); +%put (%do_over(C_plus_D)); + +%put %C_plus_D(1); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** If one of array names is empty or an array does not exist: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%array(a[6] (1:6)) +%array(b[3] (10 20 30)) + +%zipArrays(a, ); +%zipArrays(, b); + +%zipArrays(a, z); +%zipArrays(z, b); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Use of the `function=`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%array(one[3] A B C, vnames=Y) +%array(two[5] p q r s t, vnames=Y) + +%zipArrays( + one +,two +,function = catx +,argBf = %str( ) +,format = $quote. +,macarray=Y +) +%put %do_over(onetwo); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** To reuse or not to reuse, or maybe Cartesian product: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%array(e[3] (10 20 30)) +%array(f[2] (5:6)) + +%zipArrays(e, f, reuse=n, operator=+, macarray=Y, result=_noReuse); +%zipArrays(e, f, reuse=y, operator=+, macarray=Y, result=_yesReuse); +%zipArrays(e, f, reuse=cp, operator=+, macarray=Y, result=_cartProdReuse); + +%put %do_over(_noReuse); +%put %do_over(_yesReuse); +%put %do_over(_cartProdReuse); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** Use middle argument: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%array(yr[3] (2018:2020)) +%array(mth[12] (1:12)) + +%zipArrays(mth, yr, argMd=5, function=MDY, format=date11., macarray=Y); +%put %do_over(mthyr); + +%zipArrays(mth, yr, argMd=5, function=MDY, format=date11., macarray=Y, reuse=cp); +%put %do_over(mthyr); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + +## >>> `%sortMacroArray()` macro: <<< ####################### + +The sortMacroArray() macro +allow to sort elements of a macro array. + +The **limitation** is that sorted values are limited to 32767 bytes of length. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%sortMacroArray( + array + <,valLength=> + <,outSet=> + <,sortseq=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `array` - *Required*, name of an array generated by the `%array()` macro. + +* `valLength = 32767` - *Optional*, default value is `32767`, + maximum length of a variable storing macrovariable data. + (the reason of 32767 limitation) + +* `outSet = _NULL_` - *Optional*, default value is `_NULL_`, + an optional output dataset name. + +* `sortseq =` - *Optional*, default value is `LINGUISTIC(NUMERIC_COLLATION = ON)`, + sorting options for use in an internal `Proc SORT`. + +### EXAMPLES AND USECASES: #################################################### + + +**EXAMPLE 1.** Basic use-case. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +options mprint; +ods html; +ods listing close; + + +%array(hij [4:9] $ 512 ("C33" "B22" "A11" "A01" "A02" "X42"), macarray=Y) + +%put NOTE: %do_over(hij); + +%sortMacroArray(hij, valLength=3, outSet = A_NULL_(compress=char)) + +%put NOTE: %do_over(hij); + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Basic use-case. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +options mprint; +ods html; +ods listing close; + + +%array(ds = sashelp.class, vars = name|NNN height|h, macarray=Y) +%array(ds = sashelp.cars, vars = model|, macarray=Y) + +%put NOTE: %do_over(NNN); +%put NOTE: %do_over(H); +%put NOTE: %do_over(model); + +%sortMacroArray(NNN, valLength=30, outSet = A_NULL_(compress=char)) +%sortMacroArray(H, valLength=32) +%sortMacroArray(model, valLength=120) + +%put NOTE: %do_over(NNN); +%put NOTE: %do_over(H); +%put NOTE: %do_over(model); + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + +## License #################################################################### + +Copyright (c) Bartosz Jablonski, since January 2019 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- diff --git a/hist/1.2.0/macroarray.zip b/hist/1.2.0/macroarray.zip new file mode 100644 index 0000000000000000000000000000000000000000..f494acf86d9b2cc29ecd1314c89bedbe94b767ad GIT binary patch literal 53208 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 ############################################### +# The macroArray package [ver. 1.2.0] ############################################### The **macroArray** package implements a macro array facility: - `%array()`, @@ -75,10 +75,10 @@ Package contains: Required SAS Components: *Base SAS Software* -*SAS package generated by generatePackage, version 20230904* +*SAS package generated by generatePackage, version 20231107* The SHA256 hash digest for package macroArray: -`F*E9C0C58FB36AC40C76A518066B8C6F9942202A9DB2C2D737E95D2BB6E4ECED50` +`F*8689194590698F9A00B57FB37BE3CA8D7330F16B3E591CEAF49E6BE0B70D61D0` --- # Content description ############################################################################################ @@ -1556,6 +1556,9 @@ The basic syntax is the following, the `<...>` means optional parameters: %mcDictionary( H <,METHOD> + <,DS=> + <,K=Key> + <,D=Data> ) ~~~~~~~~~~~~~~~~~~~~~~~ @@ -1571,6 +1574,18 @@ The basic syntax is the following, the `<...>` means optional parameters: If `DELETE` then the macro dictionary named by `H` and all macrovariables named like "`&H._`" are deleted. +* `DS=` - *Optional*, if NOT empty then the `&DS.` dataset is used to + populate dictionary with keys from variable `&K.` and data + from variable `&D.` Works only during declaration. + +* `K=` - *Optional*, if the `&DS.` is NOT empty then `&K.` holds a name of + a variable which keeps or an expression which generates keys values. + Default is `Key`. + +* `D=` - *Optional*, if the `&DS.` is NOT empty then `&D.` holds a name of + a variable which keeps or an expression which generates data values. + Default is `Data`. + --- ### THE CREATED MACRO `%&H.()`: #################################################### @@ -1704,7 +1719,7 @@ See examples below to see use cases. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**EXAMPLE 2.** Populate macro dictionary from a dataset. +**EXAMPLE 2A.** Populate macro dictionary from a dataset "by hand". ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas %mcDictionary(CLASS) @@ -1714,6 +1729,7 @@ data _null_; call execute('%CLASS(ADD,key=' !! name !! ',data=' !! age !! ')'); run; %put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=Class_KEYSNUM.; %put _user_; %CLASS(CLEAR) @@ -1721,25 +1737,52 @@ run; %mcDictionary(CARS) %let t = %sysfunc(datetime()); data _null_; - set sashelp.cars; - call execute('%CARS(ADD,key=' !! catx("|",make,model,type) !! ',data=' !! MPG_CITY !! ')'); + set sashelp.cars(obs=42); + call execute('%CARS(ADD,key=' !! catx("|",make,model,type) !! ',data=' !! put(MPG_CITY*10,dollar10.2) !! ')'); run; %put t = %sysevalf(%sysfunc(datetime()) - &t.); %put &=CARS_KEYSNUM.; %CARS(LIST); + +%put %CARS(F,key=Audi|TT 3.2 coupe 2dr (convertible)|Sports); + %CARS(CLEAR) %put &=CARS_KEYSNUM.; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**EXAMPLE 3.** Data portion may require quoting and un-quoting.. +**EXAMPLE 2B.** Populate macro dictionary from a dataset "automatically". + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let t = %sysfunc(datetime()); +%mcDictionary(CLASS,DCL,DS=sashelp.class,k=name,d=_N_) +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=CLASS_KEYSNUM.; +%put _user_; +%CLASS(CLEAR) + + +%let t = %sysfunc(datetime()); +%mcDictionary(CARS,DCL,DS=sashelp.cars(obs=42),k=catx("|",make,model,type),d=put(MPG_CITY*10,dollar10.2)) +%put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put &=CARS_KEYSNUM.; +%CARS(LIST); + +%put %CARS(F,key=Audi|TT 3.2 coupe 2dr (convertible)|Sports); + +%CARS(CLEAR) +%put &=CARS_KEYSNUM.; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Data portion may require quoting and un-quoting. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas %mcDictionary(CODE) %CODE(CLEAR) %CODE(ADD,key=data, data=%str(data test; x = 42; run;)) %CODE(ADD,key=proc, data=%str(proc print; run;)) -%CODE(ADD,key=macro,data=%nrstr(%put *****;)) +%CODE(ADD,key=macro,data=%nrstr(%put *1*2*3*4*;)) %CODE(FIND,key=data) %CODE(FIND,key=proc) @@ -1765,6 +1808,7 @@ data _null_; end; run; %put t = %sysevalf(%sysfunc(datetime()) - &t.); +%put %AAA(F,key=A555) %AAA(CHECK,key=A555); %put &=AAA_KEYSNUM; %AAA(CLEAR) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1780,6 +1824,43 @@ run; %mcDictionary(ABCDEFGHIJKLMNOP) %* good; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 6.** More fun with datasets. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +data work.metadata; + input key :$16. data :$128.; +cards; +ID ABC-123-XYZ +path /path/to/study/data +cutoffDT 2023-01-01 +startDT 2020-01-01 +endDT 2024-12-31 +MedDRA v26.0 +; +run; +proc print; +run; + +%mcDictionary(Study,dcl,DS=work.metadata) + +%put _user_; + +%put *%Study(F,key=ID)**%Study(C,key=ID)*; + +title1 "Study %Study(F,key=ID) is located at %Study(F,key=path)"; +title2 "it starts %Study(F,key=startDT) and ends %Study(F,key=endDT)"; +footnote "MedDRA version: %Study(F,key=MedDRA)"; + +proc print data=sashelp.class(obs=7); +run; + +title; +footnote; + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + --- ## >>> `%QzipArrays()` macro: <<< ####################### diff --git a/macroarray.zip b/macroarray.zip index 6bb2437b9e537f9c4344ad6d9b739a2822208c93..f494acf86d9b2cc29ecd1314c89bedbe94b767ad 100644 GIT binary patch delta 15321 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

WWwmQ3gOq5f8`o5yA4 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