/* * Richard A. DeVenezia * Multi Level Hash Macros * May 31, 2004 * Copyright 2004 * * At present * Each class level corresponds to a single variable, no composites * The leaf or final data level may have multiple variables */ %*---------------------------------------------------------------------;
%macro MLH_Start (top, keys=, data=, debug=no); /* * top - Base identifier used for creating DATA Step variable names * keys - A colon (:) separated list of variables that define a class hierarchy * (corresponds to Tabulate CLASS statement) * data - A space separated list of variables that define a data leaf * (corresponds to Tabulate VAR statement) */ %global _mlh_&top._keys; %global _mlh_&top._data; %let _mlh_&top._keys = &keys; %let _mlh_&top._data = &data; %local i n; %let i = 1; %do %while (%scan(&data,&i,%str( )) ne ); %global _mlh_&top._data_&i; %let _mlh_&top._data_&i = %scan(&data,&i,%str( )); %let i = %eval (&i+1); %end; %let n = %eval (&i-1); %global _mlh_&top._data_N; %let _mlh_&top._data_N = &n; %let i = 1; %do %while (%scan(&keys,&i,:) ne ); %global _mlh_&top._key_&i; %global _mlh_&top._wrk_&i; %let _mlh_&top._key_&i = %scan(&keys,&i,:); %let _mlh_&top._wrk_&i = &top._anon_&i; declare hash &&_mlh_&top._wrk_&i; %let i = %eval (&i+1); %end; %let n = %eval (&i-1); %global _mlh_&top._key_N; %let _mlh_&top._key_N = &n; declare hash &top._node; declare hash &top(); &top..defineKey ("&&_mlh_&top._key_1"); &top..defineData ("&&_mlh_&top._key_1"); &top..defineData ("&&_mlh_&top._wrk_1"); %* generic hash worker; &top..defineDone (); declare hash &top._tracker(); &top._tracker.defineKey ("node_addr"); &top._tracker.defineData ("&top._node"); &top._tracker.defineDone (); %if &debug=yes %then %do; a = addr(&top); put a= hex8. "&top defined (key: &&_mlh_&top._key_1, data:&&mlh_&top._wrk_1)" /; %end; %mend; %*---------------------------------------------------------------------;
%macro MLH_Finish (top); %symdel _mlh_&top._keys; %symdel _mlh_&top._data; %local i; %do i = 1 %to &&_mlh_&top._key_N; %symdel _mlh_&top._key_&i; %symdel _mlh_&top._wrk_&i; %end; %symdel _mlh_&top._key_N; %local i; %do i = 1 %to &&_mlh_&top._data_N; %symdel _mlh_&top._data_&i; %end; %symdel _mlh_&top._data_N; %local hi; %let hi = &top._hif; declare hiter &hi ("&top._tracker"); do rc = &hi..first() by 0 while (rc = 0); &top._node.delete(); rc = &hi..next(); end; put; &top..delete(); %mend; %*---------------------------------------------------------------------;
%macro MLH_Set (top, debug=no); %local i node work keyn key keyvar wrk wrkvar wrknext wrknextvar data datan; %let node = &top._node; %let keyn = &&_mlh_&top._key_N; %let key = &&_mlh_&top._key_1; %let wrk = &&_mlh_&top._wrk_1; %let datan= &&_mlh_&top._data_N; &node = ⊤ %do i = 1 %to %eval(&keyn-1); rc = &node..find(); %if &debug=yes %then %do; a = addr(&node); put a= hex8. rc= @30 &key=; %end; %let keyvar = _mlh_&top._key_%eval(&i+1); %let key = &&&keyvar; %let wrkvar = _mlh_&top._wrk_&i; %let wrk = &&&wrkvar; %let wrknextvar = _mlh_&top._wrk_%eval(&i+1); %let wrknext = &&&wrknextvar; if rc ne 0 then do; &wrk = _new_ hash(); &wrk..defineKey ("&key"); &wrk..defineData ("&key"); &wrk..defineData ("&wrknext"); &wrk..defineDone(); %if &debug=yes %then %do; a = addr(&wrk); put a= hex8. "defined (key: &key, data:_&top._work_)"; %end; &node..replace(); &node = &wrk; node_addr = addr (&node); &top._tracker.add (); end; else &node = &wrk; %end; %let wrk = &wrknext; &wrk._key + 1; rc = &node..find(); if rc ne 0 then do; &wrk = _new_ hash (); &wrk..defineKey ("&wrk._key"); %do i = 1 %to &datan; %let data = _mlh_&top._data_&i; %let data = &&&data; &wrk..defineData ("&data"); %end; &wrk..defineDone (); &node..replace(); &node = &wrk; node_addr = addr (&node); &top._tracker.add (); end; else &node = &wrk; &node..replace(); %if &debug=yes %then %do; a = addr(&node); put a= hex8. rc= @30 &key= '-> ' &data= '*' / ; %end; %mend; %*---------------------------------------------------------------------;
%macro MLH_Get (top, n); %local node wrkvar wrk; %let node = &top._node; &node = ⊤ %if %length(&n)=0 %then %let n = &&_mlh_&top._key_N; %do i = 1 %to %eval(&n); rc = &node..find(); %let wrkvar = _mlh_&top._wrk_&i; %let wrk = &&&wrkvar; if rc eq 0 then do; &node = &wrk; %end; %do i = 1 %to %eval(&n); end; %end; %mend; %*---------------------------------------------------------------------;
%macro MLH_Dump (top, debug=no); %local i j ii n datan node hiter; %let n = &&_mlh_&top._key_N; %let datan = &&_mlh_&top._data_N; %do i = 1 %to %eval(&n+1); %local hiter&i; %let hiter&i = &top._hi_&i._&sysindex; declare hiter &&hiter&i; %end; %let node = ⊤ %do i = 1 %to &n; %if debug=yes %then %do; a = addr (&node); put a=hex8. ; %end; %let hiter = &&hiter&i; &hiter = _new_ hiter ("&node"); do rc&i = &hiter..first() by 0 while (rc&i = 0); put +&i &&_mlh_&top._key_&i=; %let node = &&_mlh_&top._wrk_&i; %end; %let hiter = &&hiter&i; &hiter = _new_ hiter ("&node"); do rc&i = &hiter..first() by 0 while (rc&i = 0); put +&i %do j = 1 %to &datan; &&_mlh_&top._data_&j= %end; ; rc&i = &hiter..next(); end; %do i = 1 %to &n; %let ii = %eval (&n-&i+1); %let hiter = &&hiterⅈ rc&ii = &hiter..next(); end; &hiter..delete(); %end; %mend; %*---------------------------------------------------------------------;
%macro MLH_Foreach (top, link=section, debug=no); %local i ii n node ; %let n = &&_mlh_&top._key_N; %do i = 1 %to %eval(&n+1); %local hiter&i; %let hiter&i = &top._hi_&i._&sysindex; declare hiter &&hiter&i; %end; %let node = ⊤ %do i = 1 %to &n; %if debug=yes %then %do; a = addr (&node); put a=hex8. ; %end; %let hiter = &&hiter&i; &hiter = _new_ hiter ("&node"); do rc&i = &hiter..first() by 0 while (rc&i = 0); %let node = &&_mlh_&top._wrk_&i; %end; %let hiter = &&hiter&i; &hiter = _new_ hiter ("&node"); do rc&i = &hiter..first() by 0 while (rc&i = 0); link &link; rc&i = &hiter..next(); end; %do i = 1 %to &n; %let ii = %eval (&n-&i+1); %let hiter = &&hiterⅈ rc&ii = &hiter..next(); end; &hiter..delete(); %end; %mend; %*---------------------------------------------------------------------;
Sample code - create and iterate a multi level hash
/* Ron Fehd style comment block */ /* remove space before ending slash to uncomment the block * / dm 'clear log' log; options mprint source nosymbolgen; %let mlh = mlhx; data _null_; retain level1-level5; %* prep PDV; if 0 then set sashelp.company; %MLH_Start ( &mlh , data=job1 , keys=level1 : level2 : level3 : level4 : level5 , debug=no) do until (endOfData); set sashelp.company end=endOfData; %MLH_Set (&mlh, debug=no ) end; %MLH_Dump (&mlh) level1 = 'International Ai'; level2 = 'NEW YORK'; level3 = 'ADMIN'; level4 = 'SHIPPING'; %MLH_Get (&mlh, 4) put / (level1-level4) (=) / 20*'-'; if rc = 0 then do; declare hiter hi ("&mlh._anon_4"); do rc = hi.first() by 0 while (rc eq 0); put level5= job1=; rc = hi.next(); end; end; hi.delete(); put; %MLH_Foreach (&mlh, link=section1) %MLH_Finish (&mlh) stop; section1: put (level:)(=) job1=; return; run; /**/ /* Ron Fehd style comment block */ /* remove space before ending slash to uncomment the block * / ods listing; * check for yourself, mlh global macro variables have been cleaned out; proc print data=sashelp.vmacro; run; /**/