/* prepend a template store, work.myTmp to ods path */
ods path (prepend) work.myTmp(update);
/* create a tagset, hfTab - html table with "filtering" */
proc template;
define tagset tagsets.hfTab;
/* ods events */
define event option;
set $name upcase(name);
set $options[$name] value; /* collect options specified */
end;
define event table_body;
start:
trigger openTable;
trigger doHeaders;
finish:
trigger closeTable;
end;
define event colspec_entry;
break /if cmp(name, 'obs'); /* ignore the obs column */
set $names[] name; /* collect column names */
end;
define event row;
start: /* some init for each row */
break /if ^$inTable;
unset $trClasses;
unset $tds;
finish:
break /if ^$inTable;
unset $class; /* open tr with classes, if any */
iterate $trClasses;
do /while _value_;
set $class catx(' ', $class, _value_);
next $trClasses;
done;
put "<tr";
put " class='" $class "'" /if ^missing($class);
put ">";
iterate $tds; /* output cells in the row */
do /while _value_;
put _value_;
next $tds;
done;
putl "</tr>"; /* close tr tag */
end;
define event data;
set $name upcase(name);
/* if column name matches a column filter then add a class attribute */
do /if $cfClasses[$name];
set $tds[] "<td class='" $cfClasses[$name] "'>" value "</td>";
else;
set $tds[] "<td>" value "</td>";
done;
/* if the cell matches a row filter then collect the class name */
set $trClasses[] $rfClasses[$name] /if cmp($rfValues[$name], value);
end;
/* user events */
define event openTable;
put "<table";
put " width=" quote($options["WIDTH"]) /if $options["WIDTH"];
putl ">";
trigger parseRowFilters /if $options["ROWFILTERS"];
trigger parseColFilters /if $options["COLFILTERS"];
set $inTable "True";
end;
define event doHeaders;
put "<tr>";
iterate $names;
do /while _value_;
set $name upcase(_value_);
do /if $cfClasses[$name];
put "<th class='" $cfClasses[$name] "'>" _value_ "</th>";
else;
put "<th>" _value_ "</th>";
done;
next $names;
done;
putl "</tr>";
end;
define event closeTable;
putl "</table>";
unset $options;
unset $inTable;
unset $names;
end;
/* for parsing filter options */
define event parseRowFilters;
set $str $options["ROWFILTERS"];
trigger getList;
iterate $list;
do /while _value_;
set $var scan(_value_, 1);
set $var upcase($var);
set $rfValues[$var] scan(_value_, 2);
set $rfClasses[$var] scan(_value_, 3);
next $list;
done;
unset $list;
end;
define event parseColFilters;
set $str $options["COLFILTERS"];
trigger getList;
iterate $list;
do /while _value_;
set $var scan(_value_, 1);
set $var upcase($var);
set $cfClasses[$var] scan(_value_, 2);
next $list;
done;
unset $list;
end;
/* utility */
define event getList;
/* given $str of a delimited list, returns a list, $list[] */
break /if missing($str);
set $dlm ",";
eval $i 1;
set $item scan($str, $i, $dlm);
do /while ^missing($item);
set $list[] strip($item);
eval $i $i + 1;
set $item scan($str, $i, $dlm);
done;
end;
/* quote those (in)famous five */
map = %nrstr("<>&'""");
mapsub = %nrstr("/</>/&/'/"/");
end;
run;
/* usage */
ods tagsets.hfTab file=_webout
options(width='100%' colfilters='weight weight'
rowfilters='sex F girl, name Thomas thomas, age 12 twelve');
proc print data=sashelp.class;
where age <= 12;
run;
ods tagsets.hfTab close;
/* _webout
<table width="100%">
<tr><th>Name</th><th>Sex</th><th>Age</th><th>Height</th><th class='weight'>Weight</th></tr>
<tr class='twelve'><td>James</td><td>M</td><td>12</td><td>57.3</td><td class='weight'> 83.0</td></tr>
<tr class='girl twelve'><td>Jane</td><td>F</td><td>12</td><td>59.8</td><td class='weight'> 84.5</td></tr>
<tr class='twelve'><td>John</td><td>M</td><td>12</td><td>59.0</td><td class='weight'> 99.5</td></tr>
<tr class='girl'><td>Joyce</td><td>F</td><td>11</td><td>51.3</td><td class='weight'> 50.5</td></tr>
<tr class='girl twelve'><td>Louise</td><td>F</td><td>12</td><td>56.3</td><td class='weight'> 77.0</td></tr>
<tr class='twelve'><td>Robert</td><td>M</td><td>12</td><td>64.8</td><td class='weight'>128.0</td></tr>
<tr class='thomas'><td>Thomas</td><td>M</td><td>11</td><td>57.5</td><td class='weight'> 85.0</td></tr>
</table>
*/
/* clean up */
ods path reset;
Don't miss out on SAS Innovate - Register now for the FREE Livestream!
Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.