array.sas
%macro tq84_array(values, delim=%str( ));
/*
After an idea outlined in
ARRAY.sas (https://github.com/Jiangtang/SAS_ListProcessing/blob/master/ARRAY.sas)
by
Ted Clay , M.S. tclay@ashlandhome.net (541) 482-6435
David Katz, M.S. www.davidkatzconsulting.com
"Please keep, use and pass on the ARRAY and DO_OVER macros with
this authorship note. -Thanks "
*/
%local array_name;
%let array_name = %tq84_createId(tq84_array_);
%local elem_val i;
%let elem_val = qqqqqqqqqq;
%let i = 1;
%let elem_val = %scan(%str(&values), &i, %str(&delim));
%do %while (&elem_val ne %str());
%global &array_name&i;
%let &array_name&i=&elem_val;
%let i = %eval(&i + 1);
%let elem_val= %scan(%str(&values), &i, %str(&delim));
%end;
%global &array_name.s;
%let &array_name.s = %eval(&i-1);
&array_name
%mend tq84_array;
%macro tq84_arrayLength(array);
&&&array.s
%mend tq84_arrayLength;
%macro tq84_arraySetVal(array, pos, val);
%local len;
%let len = %tq84_arrayLength(&array);
%if &len < &pos %then %do;
%put tq84_arraySetVal: array &array: pos = &pos, but length only &len;
%abort;
%end; %else %do;
%global &array&pos;
%let &array&pos=&val;
%end;
%mend tq84_arraySetVal;
%macro tq84_arrayGetVal(array, pos);
%local len;
%let len = %tq84_arrayLength(&array);
%if &len < &pos %then %do;
%put tq84_arraySetVal: array &array: pos = &pos, but length only &len;
%abort;
%end; %else %do;
%global &array&pos;
&&&array&pos
%end;
%mend tq84_arrayGetVal;
%macro tq84_arrayPushBack(array, val);
%local len;
%let len = %tq84_arrayLength(&array);
%let len = %eval(&len+1);
%let &array.s=&len;
%tq84_arraySetVal(&array, &len, &val)
%mend tq84_arrayPushBack;
%macro tq84_arrayApply(array, funcref);
%local macroName;
%let macroName = %tq84_createMacro_M(&funcref, this);
%local i;
%do i = 1 %to &&&array.s;
%¯oName(&&&array.&i)
%end;
%mend tq84_arrayApply;
%macro tq84_arrayMap(array, funcref);
%local macroNameAM;
%let macroNameAM = %tq84_createMacro_M(&funcref, this);
%local ret;
%let ret = %tq84_array();
%tq84_arrayApply(&array, %nrstr(
%put this = &this;
%local evaluated;
%let evaluated = %¯oNameAM(&this);
%tq84_arrayPushBack(&ret, &evaluated)
))
&ret
%mend tq84_arrayMap;
%macro tq84_arrayJoin(array, delim=%str( ));
%local ret;
%local i;
%do i = 1 %to %tq84_arrayLength(&array);
%if &i > 1 %then %do;
%let ret = &ret&delim;
%end;
%let ret = &ret%tq84_arrayGetVal(&array, &i);
%end;
&ret
%mend tq84_arrayJoin;
Tests
Size/length of array
%let foo_bar_baz = %tq84_array(foo bar baz);
%put size of foo_bar_baz is %tq84_arrayLength(&foo_bar_baz); /* size of foo_bar_baz is 3 */
Get value of an element in array
%let oneTwoThreeEtc = %tq84_array(one two three four five six seven eight nine ten);
%put Element 5 in oneTwoThreeEtc is %tq84_arrayGetVal(&oneTwoThreeEtc, 5); /* Element 5 in oneTwoThreeEtc is five */
%put Element 8 in oneTwoThreeEtc is %tq84_arrayGetVal(&oneTwoThreeEtc, 8); /* Element 8 in oneTwoThreeEtc is eight */
Iterating over elements in array
%tq84_arrayApply
is modelled after the
apply
function in
JavaScript.
The first argument is the (name of the) array, the second argument a string from which a macro is compiled (using
createMacro). Within this macro, the
macro variable &this
contains the value of the element.
%let oneThroughFive = %tq84_array(one two three four five);
%tq84_arrayApply(&oneThroughFive, %nrstr(
%put this = &this;
))
/*
this = one
this = two
this = three
this = four
this = five
*/
Modifying the value of elements in the array
%tq84_arraySetVal(&array, &elem, &newValue
set the value of an element in the array.
%let oneThroughFive = %tq84_array(one two three four five);
%tq84_arraySetVal(&oneThroughFive, 1, ONE in UPPERCASE)
%tq84_arraySetVal(&oneThroughFive, 4, IV)
%tq84_arrayApply(&oneThroughFive, %nrstr(
%put this = &this;
))
/*
this = ONE in UPPERCASE
this = two
this = three
this = IV
this = five
*/
Adding elements to the array
%tq84_arrayPushBack(&array, &val)
adds an element to the array.
%let fooBarBaz = %tq84_array(foo bar baz);
%tq84_arrayPushBack(&fooBarBaz, more)
%put size of fooBarBaz is %tq84_arrayLength(&fooBarBaz);
/*
size of fooBarBaz is 4
*/
%tq84_arrayApply(&fooBarBaz, %nrstr(
%put this = &this;
))
/*
this = foo
this = bar
this = baz
this = more
*/
Joining elements to a string
The values/elements of an array can be joined to create a string.
By default, the elements are joined by a single space; the delim
option can be used to change that.
Evaluating an expression for each element in the array
With %tq84_arrayMap
, an expression is evaluated for each element in the array. From the values of these expressions, a new array is returned.
%let abc_def_ghi = %tq84_array(abc def ghi);
%let abc_def_ghi_ = %tq84_arrayMap(&abc_def_ghi, %nrstr(>&this<));
%tq84_arrayApply(&abc_def_ghi_, %nrstr(
%put this = &this;
))
/*
this = >abc<
this = >def<
this = >ghi<
*/
Array of arrays
%let arrayOfArrays = %tq84_array();
%tq84_arrayPushBack(&arrayOfArrays, %tq84_array(one two three four));
%tq84_arrayPushBack(&arrayOfArrays, %tq84_array(foo bar baz));
%tq84_arrayPushBack(&arrayOfArrays, %tq84_array(what is this));
%tq84_arrayPushBack(&arrayOfArrays, %tq84_array(New York|Berlin|Zürich|Paris,delim=|));
%tq84_arrayApply(&arrayOfArrays, %nrstr(
%put this = &this;
%tq84_arrayApply(&this, %nrstr(
%put %nrstr( ) this = &this;
))
))
/*
this = tq84_array_33_
this = one
this = two
this = three
this = four
this = tq84_array_34_
this = foo
this = bar
this = baz
this = tq84_array_35_
this = what
this = is
this = this
this = tq84_array_36_
this = New York
this = Berlin
this = Zürich
this = Paris
*/
delim
When creating an array, the optional parameter delim
can be used to specify the elements:
%let abcEtc = %tq84_array(abc-def-g h i-jkl, delim= -);
%tq84_arrayApply(&abcEtc, %nrstr(
%put this = &this;
))
/*
this = abc
this = def
this = g h i
this = jkl
*/
Name of array and its elements
%let array_1through4 = %tq84_array(one two three four);
%put array_1through4 = &array_1through4<;
%put &tq84_array_0_1; /* one */
%put &tq84_array_0_2; /* two */
%put &tq84_array_0_3; /* three */
%put &tq84_array_0_4; /* four */