mirror of
https://github.com/SASPAC/macroarray.git
synced 2025-12-10 18:04:35 +00:00
## The macroArray package [ver. 1.2.3] - Update in `q=` parameter for the `%array()` macro. - Fix in data set existence for `ds=` parameter in the `%array()` macro. - Documentation updated. --- SHA256 digests for packages: - macroArray: `F*A0840B92EB9356EDB318DBE9B579A345C85ABF69E8D5F7C73C144C66F2F74FB4`
2287 lines
71 KiB
Markdown
2287 lines
71 KiB
Markdown
- [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.3] <a name="macroarray-package"></a> ###############################################
|
|
|
|
The **macroArray** package implements a macroarray 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 20231123*
|
|
|
|
The SHA256 hash digest for package macroArray:
|
|
`F*A0840B92EB9356EDB318DBE9B579A345C85ABF69E8D5F7C73C144C66F2F74FB4`
|
|
|
|
---
|
|
# Content description ############################################################################################
|
|
|
|
## >>> `%appendArray()` macro: <<< <a name="appendarray-macro"></a> ############
|
|
|
|
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: <<< <a name="appendcell-macro"></a> ##############
|
|
|
|
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: <<< <a name="array-macro"></a> #######################
|
|
|
|
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 macroarray 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 macroarrays 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, <br>
|
|
e.g. `myArr[*] x1-x3 (4:6)` <br>
|
|
or `myBrr[*] $ y1-y3 ("a" "b" "c")` <br>
|
|
or `myCrr[3] $ ("d d d" "e,e,e" "f;f;f")` <br>
|
|
or `myDrr p q r s`. <br>
|
|
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`. <br>
|
|
Three additional *global* macrovariables:
|
|
`<arrayName>LBOUND`, `<arrayName>HBOUND`, and `<arrayName>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<delimiter><arrayname1> <... variableN<delimiter><arraynameN>>`
|
|
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: <br>
|
|
1) macroarray "H" with ALL(`#`) values of variable "height" <br>
|
|
2) macroarray "WEIGHT" with ALL(no separator is equivalent to #)
|
|
values of variable "weight" <br>
|
|
3) macroarray "W" with UNIQUE(|) values of variable "weight" and <br>
|
|
4) macroarray "AGE" with UNIQUE(|) values of variable "age".
|
|
|
|
* `q=` - *Optional*, indicates (when set to `1` or '2') if the value should be surrounded by quotes.
|
|
It uses `quote(cats(...))` combo under the hood. Default value is `0`.
|
|
Value `1` is for apostrophes, value `2` is for double quotes.
|
|
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 `<arrayname>N` is set to `M`
|
|
In case when range is different
|
|
the `<arrayname>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: <<< <a name="concatarrays-macro"></a> ###########
|
|
|
|
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: <<< <a name="deletemacarray-macro"></a> #######
|
|
|
|
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: <<< <a name="do-over-macro"></a>######################
|
|
|
|
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: <<< <a name="do-over2-macro"></a>####################
|
|
|
|
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: <<< <a name="do-over3-macro"></a>####################
|
|
|
|
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: <<< <a name="make-do-over-macro"></a> ###########
|
|
|
|
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<n>()` 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<n>()` 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: <<< <a name="mchashtable-macro"></a> #######################
|
|
|
|
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: <<< <a name="mcdictionary-macro"></a> #######################
|
|
|
|
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: <<< <a name="qziparrays-macro"></a> #######################
|
|
|
|
The zipArrays() and QzipArrays() macros
|
|
allow to use a function on elements of pair of
|
|
macroarrays.
|
|
|
|
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: <<< <a name="ziparrays-macro"></a> #######################
|
|
|
|
The zipArrays() and QzipArrays() macros
|
|
allow to use a function on elements of pair of
|
|
macroarrays.
|
|
|
|
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: <<< <a name="sortmacroarray-macro"></a> #######################
|
|
|
|
The sortMacroArray() macro
|
|
allow to sort elements of a macroarray.
|
|
|
|
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.
|
|
|
|
---
|