From 371333b1022c12194bec572413ff9aa14725c7ef Mon Sep 17 00:00:00 2001 From: Bart Jablonski Date: Fri, 12 Jan 2024 10:58:13 +0100 Subject: [PATCH] The BasePlus package [ver. 1.36.0] The BasePlus package [ver. 1.36.0] - small functional update for: %today(), %date(), %time(), and %datetime() macros. - documentation update. --- README.md | 4 +- baseplus.md | 8537 ++++++++++++++++++++------------------ baseplus.zip | Bin 298625 -> 299390 bytes hist/1.36.0/baseplus.md | 6745 ++++++++++++++++++++++++++++++ hist/1.36.0/baseplus.zip | Bin 0 -> 299390 bytes 5 files changed, 11217 insertions(+), 4069 deletions(-) create mode 100644 hist/1.36.0/baseplus.md create mode 100644 hist/1.36.0/baseplus.zip diff --git a/README.md b/README.md index d9cf4d7..6340931 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,13 @@ libname NEW "%workPath()/new"; %put %date() %time() %datetime(); +%put %date(yymmddn10.) %time(time5.) %datetime(e8601dt.); + %put %monthShift(2023,1,-5); ``` and more. -SHA256 digest for the latest version of `BasePlus`: F*BCD89EDF856762EB8E441BC53933774483258453D1F7D74185F8A1861E414B0E +SHA256 digest for the latest version of `BasePlus`: F*B9F1B3243FD3956F0B68652C21EA1EBC19F3EB0931774A57FECE1F02A9448108 [**Documentation for BasePlus**](./baseplus.md "Documentation for BasePlus") diff --git a/baseplus.md b/baseplus.md index 09d47a1..d03dcdb 100644 --- a/baseplus.md +++ b/baseplus.md @@ -1,87 +1,28 @@ -- [The BasePlus package](#baseplus-package) -- [Content description](#content-description) - * [`%getVars()` macro](#getvars-macro) - * [`%QgetVars()` macro](#qgetvars-macro) - * [`%symdelGlobal()` macro](#symdelglobal-macro) - * [`bool.` format](#bool-format) - * [`boolz.` format](#boolz-format) - * [`ceil.` format](#ceil-format) - * [`floor.` format](#floor-format) - * [`int.` format](#int-format) - * [`arrFill()` subroutine](#arrfill-subroutine) - * [`arrFillC()` subroutine](#arrfillc-subroutine) - * [`arrMissFill()` subroutine](#arrmissfill-subroutine) - * [`arrMissFillC()` subroutine](#arrmissfillc-subroutine) - * [`arrMissToLeft()` subroutine](#arrmisstoleft-subroutine) - * [`arrMissToLeftC()` subroutine](#arrmisstoleftc-subroutine) - * [`arrMissToRight()` subroutine](#arrmisstoright-subroutine) - * [`arrMissToRightC()` subroutine](#arrmisstorightc-subroutine) - * [`catXFc()` function](#catxfc-function) - * [`catXFi()` function](#catxfi-function) - * [`catXFj()` function](#catxfj-function) - * [`catXFn()` function](#catxfn-function) - * [`delDataset()` function](#deldataset-function) - * [`qsortInCbyProcProto()` proto function](#qsortincbyprocproto-proto-function) - * [`fromMissingToNumberBS()` function](#frommissingtonumberbs-function) - * [`fromNumberToMissing()` function](#fromnumbertomissing-function) - * [`quickSort4NotMiss()` subroutine](#quicksort4notmiss-subroutine) - * [`quickSortHash()` subroutine](#quicksorthash-subroutine) - * [`quickSortHashSDDV()` subroutine](#quicksorthashsddv-subroutine) - * [`quickSortLight()` subroutine](#quicksortlight-subroutine) - * [`%dedupListS()` macro](#deduplists-macro) - * [`%dedupListC()` macro](#deduplistc-macro) - * [`%dedupListP()` macro](#deduplistp-macro) - * [`%dedupListX()` macro](#deduplistx-macro) - * [`%QdedupListX()` macro](#qdeduplistx-macro) - * [`brackets.` format](#brackets-format) - * [`semicolon.` format](#semicolon-format) - * [`bracketsC()` function](#bracketsc-function) - * [`bracketsN()` function](#bracketsn-function) - * [`semicolonC()` function](#semicolonc-function) - * [`semicolonN()` function](#semicolonn-function) - * [`%zipEvalf()` macro](#zipevalf-macro) - * [`%QzipEvalf()` macro](#qzipevalf-macro) - * [`%functionExists()` macro](#functionexists-macro) - * [`%RainCloudPlot()` macro](#raincloudplot-macro) - * [`%zipLibrary()` macro](#ziplibrary-macro) - * [`%unzipLibrary()` macro](#unziplibrary-macro) - * [`%zipArch()` macro](#ziparch-macro) - * [`%unzipArch()` macro](#unziparch-macro) - * [`%downloadFilesTo()` macro](#downloadfilesto-macro) - * [`%LDSN()` macro](#ldsn-macro) - * [`%LDsNm()` macro](#ldsnm-macro) - * [`%LVarNm()` macro](#lvarnm-macro) - * [`%LVarNmLab()` macro](#lvarnmlab-macro) - * [`%bpPIPE()` macro](#bppipe-macro) - * [`%dirsAndFiles()` macro](#dirsandfiles-macro) - * [`%repeatTxt()` macro](#repeattxt-macro) - * [`%repList()` macro](#replist-macro) - * [`%intsList()` macro](#intslist-macro) - * [`%letters()` macro](#letters-macro) - * [`%splitDSIntoBlocks()` macro](#splitdsintoblocks-macro) - * [`%splitDSIntoParts()` macro](#splitdsintoparts-macro) - * [`%filePath()` macro](#filepath-macro) - * [`%libPath()` macro](#libpath-macro) - * [`%workPath()` macro](#workpath-macro) - * [`%date()` macro](#date-macro) - * [`%today()` macro](#today-macro) - * [`%time()` macro](#time-macro) - * [`%datetime()` macro](#datetime-macro) - * [`%monthShift()` macro](#monthshift-macro) - * [`%translate()` macro](#translate-macro) - * [`%tranwrd()` macro](#tranwrd-macro) - * [`%findDSwithVarVal()` macro](#finddswithvarval-macro) - * [`%getTitle()` macro](#gettitle-macro) - * [`%mInclude()` macro](#minclude-macro) - * [`%fmt()` macro](#fmt-macro) - * [`%infmt()` macro](#infmt-macro) - - - * [License](#license) +# Documentation for the `BasePlus` package. --- + +### Version information: + + *The BASE SAS plus a bunch of functionalities I am missing in BASE SAS* + +- Package: BasePlus +- Version: 1.36.0 +- Generated: 2024-01-12T10:32:46 +- Author(s): Bartosz Jablonski (yabwon@gmail.com), Quentin McMullen (qmcmullen@gmail.com) +- Maintainer(s): Bartosz Jablonski (yabwon@gmail.com) +- License: MIT +- File SHA256: `F*B9F1B3243FD3956F0B68652C21EA1EBC19F3EB0931774A57FECE1F02A9448108` for this version +- Content SHA256: `C*5A51FA3E5B3A6E9AE2AF37D6604B49B8656D4CC50AFF1F975E546D4419AA0461` for this version + +--- + +# The `BasePlus` package, version: `1.36.0`; + +--- + -# The BasePlus package [ver. 1.35.1] ############################################### +# The BasePlus package [ver. 1.36.0] ############################################### The **BasePlus** package implements useful functions and functionalities I miss in the BASE SAS. @@ -346,7 +287,9 @@ run; **EXAMPLE 23** Date and time one-liners: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put %today() %date() %time() %datetime(); +%put #%today()#%date()#%time()#%datetime()#; + +%put @%today(yymmdd10.)@%date(date11.)@%time(time8.)@%datetime(e8601dt.)@; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **EXAMPLE 24** Months shifting: @@ -412,100 +355,589 @@ https://www.lexjansen.com/wuss/2023/WUSS-2023-Paper-189.zip run; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - --- - -Package contains: -1. macro bppipe -2. macro deduplistc -3. macro deduplistp -4. macro deduplists -5. macro deduplistx -6. macro dirsandfiles -7. macro functionexists -8. macro getvars -9. macro intslist -10. macro ldsn -11. macro ldsnm -12. macro lvarnm -13. macro lvarnmlab -14. macro qdeduplistx -15. macro qgetvars -16. macro qzipevalf -17. macro raincloudplot -18. macro repeattxt -19. macro splitdsintoblocks -20. macro splitdsintoparts -21. macro symdelglobal -22. macro unziparch -23. macro unziplibrary -24. macro ziparch -25. macro zipevalf -26. macro ziplibrary -27. format bool -28. format boolz -29. format ceil -30. format floor -31. format int -32. functions arrfill -33. functions arrfillc -34. functions arrmissfill -35. functions arrmissfillc -36. functions arrmisstoleft -37. functions arrmisstoleftc -38. functions arrmisstoright -39. functions arrmisstorightc -40. functions bracketsc -41. functions bracketsn -42. functions catxfc -43. functions catxfi -44. functions catxfj -45. functions catxfn -46. functions deldataset -47. functions semicolonc -48. functions semicolonn -49. format brackets -50. format semicolon -51. proto qsortincbyprocproto -52. functions frommissingtonumberbs -53. functions fromnumbertomissing -54. functions quicksort4notmiss -55. functions quicksorthash -56. functions quicksorthashsddv -57. functions quicksortlight -58. macro date -59. macro datetime -60. macro downloadfilesto -61. macro filepath -62. macro finddswithvarval -63. macro fmt -64. macro gettitle -65. macro infmt -66. macro letters -67. macro libpath -68. macro minclude -69. macro monthshift -70. macro replist -71. macro time -72. macro today -73. macro translate -74. macro tranwrd -75. macro workpath - - - -Package contains additional content, run: %loadPackageAddCnt(BasePlus) to load it -or look for the baseplus_AdditionalContent directory in the Packages fileref + +--- + + +--- + + +--- + +Package contains additional content, run: `%loadPackageAddCnt(BasePlus)` to load it +or look for the `baseplus_AdditionalContent` directory in the `packages` fileref localization (only if additional content was deployed during the installation process). + +-------------------------------------------------------------------- + +*SAS package generated by SAS Package Framework, version `20231210`* + +-------------------------------------------------------------------- + +# The `BasePlus` package content +The `BasePlus` package consists of the following content: +1. [`%bppipe()` macro ](#bppipe-macro-1 ) +2. [`%deduplistc()` macro ](#deduplistc-macro-2 ) +3. [`%deduplistp()` macro ](#deduplistp-macro-3 ) +4. [`%deduplists()` macro ](#deduplists-macro-4 ) +5. [`%deduplistx()` macro ](#deduplistx-macro-5 ) +6. [`%dirsandfiles()` macro ](#dirsandfiles-macro-6 ) +7. [`%functionexists()` macro ](#functionexists-macro-7 ) +8. [`%getvars()` macro ](#getvars-macro-8 ) +9. [`%intslist()` macro ](#intslist-macro-9 ) +10. [`%ldsn()` macro ](#ldsn-macro-10 ) +11. [`%ldsnm()` macro ](#ldsnm-macro-11 ) +12. [`%lvarnm()` macro ](#lvarnm-macro-12 ) +13. [`%lvarnmlab()` macro ](#lvarnmlab-macro-13 ) +14. [`%qdeduplistx()` macro ](#qdeduplistx-macro-14 ) +15. [`%qgetvars()` macro ](#qgetvars-macro-15 ) +16. [`%qzipevalf()` macro ](#qzipevalf-macro-16 ) +17. [`%raincloudplot()` macro ](#raincloudplot-macro-17 ) +18. [`%repeattxt()` macro ](#repeattxt-macro-18 ) +19. [`%splitdsintoblocks()` macro ](#splitdsintoblocks-macro-19 ) +20. [`%splitdsintoparts()` macro ](#splitdsintoparts-macro-20 ) +21. [`%symdelglobal()` macro ](#symdelglobal-macro-21 ) +22. [`%unziparch()` macro ](#unziparch-macro-22 ) +23. [`%unziplibrary()` macro ](#unziplibrary-macro-23 ) +24. [`%ziparch()` macro ](#ziparch-macro-24 ) +25. [`%zipevalf()` macro ](#zipevalf-macro-25 ) +26. [`%ziplibrary()` macro ](#ziplibrary-macro-26 ) +27. [`$bool.` format/informat ](#bool-format-27 ) +28. [`$boolz.` format/informat ](#boolz-format-28 ) +29. [`$ceil.` format/informat ](#ceil-format-29 ) +30. [`$floor.` format/informat ](#floor-format-30 ) +31. [`$int.` format/informat ](#int-format-31 ) +32. [`arrfill()` function ](#arrfill-functions-32 ) +33. [`arrfillc()` function ](#arrfillc-functions-33 ) +34. [`arrmissfill()` function ](#arrmissfill-functions-34 ) +35. [`arrmissfillc()` function ](#arrmissfillc-functions-35 ) +36. [`arrmisstoleft()` function ](#arrmisstoleft-functions-36 ) +37. [`arrmisstoleftc()` function ](#arrmisstoleftc-functions-37 ) +38. [`arrmisstoright()` function ](#arrmisstoright-functions-38 ) +39. [`arrmisstorightc()` function ](#arrmisstorightc-functions-39 ) +40. [`bracketsc()` function ](#bracketsc-functions-40 ) +41. [`bracketsn()` function ](#bracketsn-functions-41 ) +42. [`catxfc()` function ](#catxfc-functions-42 ) +43. [`catxfi()` function ](#catxfi-functions-43 ) +44. [`catxfj()` function ](#catxfj-functions-44 ) +45. [`catxfn()` function ](#catxfn-functions-45 ) +46. [`deldataset()` function ](#deldataset-functions-46 ) +47. [`semicolonc()` function ](#semicolonc-functions-47 ) +48. [`semicolonn()` function ](#semicolonn-functions-48 ) +49. [`$brackets.` format/informat ](#brackets-format-49 ) +50. [`$semicolon.` format/informat ](#semicolon-format-50 ) +51. [`qsortincbyprocproto()` proto ](#qsortincbyprocproto-proto-51 ) +52. [`frommissingtonumberbs()` function ](#frommissingtonumberbs-functions-52 ) +53. [`fromnumbertomissing()` function ](#fromnumbertomissing-functions-53 ) +54. [`quicksort4notmiss()` function ](#quicksort4notmiss-functions-54 ) +55. [`quicksorthash()` function ](#quicksorthash-functions-55 ) +56. [`quicksorthashsddv()` function ](#quicksorthashsddv-functions-56 ) +57. [`quicksortlight()` function ](#quicksortlight-functions-57 ) +58. [`%date()` macro ](#date-macro-58 ) +59. [`%datetime()` macro ](#datetime-macro-59 ) +60. [`%downloadfilesto()` macro ](#downloadfilesto-macro-60 ) +61. [`%filepath()` macro ](#filepath-macro-61 ) +62. [`%finddswithvarval()` macro ](#finddswithvarval-macro-62 ) +63. [`%fmt()` macro ](#fmt-macro-63 ) +64. [`%gettitle()` macro ](#gettitle-macro-64 ) +65. [`%infmt()` macro ](#infmt-macro-65 ) +66. [`%letters()` macro ](#letters-macro-66 ) +67. [`%libpath()` macro ](#libpath-macro-67 ) +68. [`%minclude()` macro ](#minclude-macro-68 ) +69. [`%monthshift()` macro ](#monthshift-macro-69 ) +70. [`%replist()` macro ](#replist-macro-70 ) +71. [`%time()` macro ](#time-macro-71 ) +72. [`%today()` macro ](#today-macro-72 ) +73. [`%translate()` macro ](#translate-macro-73 ) +74. [`%tranwrd()` macro ](#tranwrd-macro-74 ) +75. [`%workpath()` macro ](#workpath-macro-75 ) + + +93. [License note](#license) + +--- + +## `%bppipe()` macro ###### + +## >>> `%bpPIPE()` macro: <<< ####################### -* SAS package generated by generatePackage, version 20231107 * +The bpPIPE() [Base Plus PIPE] macro executes OS command +and print to the log output of the execution. -The SHA256 hash digest for package BasePlus: -`F*BCD89EDF856762EB8E441BC53933774483258453D1F7D74185F8A1861E414B0E` +Under the hood it uses `_` filename reference to PIPE device. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%bpPIPE( ) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +* **NO Arguments** - Everything inside brackets is treated as an OS command. --- -# Content description ############################################################################################ + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** List, to the log, content of D and C drives: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %bpPIPE(D: & dir & dir "C:\") +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** List, to the log, content of `home` directory: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %bpPIPE(ls -halt ~/) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%deduplistc()` macro ###### + +## >>> `%dedupListC()` macro: <<< ####################### + +The `%dedupListC()` macro deletes duplicated values from +a *COMMA separated* list of values. List, including separators, +can be no longer than a value carried by a single macrovariable. + +Returned value is *unquoted*. Leading and trailing spaces are ignored. + +The `%dedupListC()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dedupListC( + list,of,comma,separated,values +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - A list of *comma separated* values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListC(a,b,c,b,c)*; + + %put *%dedupListC(a,b c,b c)*; + + %put *%dedupListC(%str(a,b,c,b,c))*; + + %put *%dedupListC(%str(a),%str(b),%str(c),b,c)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Leading and trailing spaces are ignored. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListC( a , b b , c , b b, c )*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** Macro variable as an argument. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4, 5, 6, 1, 2, 3, 1, 2, 3, 4, 5, 6; + %put *%dedupListC(&list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%deduplistp()` macro ###### + +## >>> `%dedupListP()` macro: <<< ####################### + +The `%dedupListP()` macro deletes duplicated values from +a *PIPE(`|`) separated* list of values. List, including separators, +can be no longer than a value carried by a single macrovariable. + +Returned value is *unquoted*. Leading and trailing spaces are ignored. + +The `%dedupListP()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dedupListP( + list|of|pipe|separated|values +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - A list of *pipe separated* values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListP(a|b|c|b|c)*; + + %put *%dedupListP(a|b c|b c)*; + + %put *%dedupListP(%str(a|b|c|b|c))*; + + %put *%dedupListP(%str(a)|%str(b)|%str(c)|b|c)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Leading and trailing spaces are ignored. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListP( a | b b | c | b b| c )*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** Macro variable as an argument. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4|5|6|1|2|3|1|2|3|4|5|6; + %put *%dedupListP(&list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%deduplists()` macro ###### + +## >>> `%dedupListS()` macro: <<< ####################### + +The `%dedupListS()` macro deletes duplicated values from +a *SPACE separated* list of values. List, including separators, +can be no longer than a value carried by a single macrovariable. + +Returned value is *unquoted*. + +The `%dedupListS()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dedupListS( + list of space separated values +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - A list of *space separated* values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListS(a b c b c)*; + + %put *%dedupListS(a b,c b,c)*; + + %put *%dedupListS(%str(a b c b c))*; + + %put *%dedupListS(%str(a) %str(b) %str(c) b c)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Macro variable as an argument. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4 5 6 1 2 3 1 2 3 4 5 6; + %put *%dedupListS(&list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%deduplistx()` macro ###### + +## >>> `%dedupListX()` macro: <<< ####################### + +The `%dedupListX()` macro deletes duplicated values from +a *X separated* list of values, where the `X` represents +a *single character* separator. List, including separators, +can be no longer than a value carried by a single macrovariable. + +**Caution.** The value of `X` *has to be* in **the first** byte of the list, + just after the opening bracket, i.e. `(X...)`. + +Returned value is *unquoted*. Leading and trailing spaces are ignored. + +The `%dedupListX()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dedupListX( +XlistXofXxXseparatedXvalues +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - A list of *X separated* values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListX(|a|b|c|b|c)*; + + %put *%dedupListX( a b c b c)*; + + %put *%dedupListX(,a,b,c,b,c)*; + + %put *%dedupListX(XaXbXcXbXc)*; + + %put *%dedupListX(/a/b/c/b/c)*; + + data _null_; + x = "%dedupListX(%str(;a;b;c;b;c))"; + put x=; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Leading and trailing spaces are ignored. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListX(| a | b.b | c | b.b| c )*; + + %put *%dedupListX(. a . b b . c . b b. c )*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** Macro variable as an argument. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4$5.5$6$1$2$3$1$2$3$4$5.5$6; + %put *%dedupListX($&list.)*; + + %let list = 4$ 5.5$ 6$ 1$ 2$ 3$ 1$ 2$ 3$ 4$ 5.5$ 6$; + %put *%dedupListX( &list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%dirsandfiles()` macro ###### + +## >>> `%dirsAndFiles()` macro: <<< ####################### + +The `%dirsAndFiles()` macro allows to extract info about all files +and subdirectories of a given `root` directory. + +The extracted info may be just a list of files and subdirectories or, if +the `details=` parameter is set to 1, additional operating system information +is extracted (information is OSS dependent and gives different results for Linux +and for Windows) + +The extracted info can be narrowed down to files (`keepFiles=1`) or to +directories (`keepDirs=1`) if need be. + +The extracted info can be presented in wide or long format (`longFormat=1`). + +The extracted info for files can be narrowed down to only files with particular +extension, for example: `fileExt=sas7bdat`. + +The extracted info can be narrowed down maximal path depth +by setting up the `maxDepth=` parameter. + +See examples below for the details. + +### REFERENCES: ################################################################### + +The macro is based on Kurt Bremser's "*Talking to Your Host*" article +presented at WUSS 2022 conference. + +The article is available [here](https://communities.sas.com/t5/SAS-User-Groups-Library/WUSS-Presentation-Talking-to-Your-Host/ta-p/838344) +and also as an additional content of this package. +The paper was awarded the "Best Paper Award - Programming". + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles( + root + <,ODS=> + <,details=> + <,keepDirs=> + <,keepFiles=> + <,longFormat=> + <,fileExt=> + <,maxDepth=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `root` - *Required*, path to be searched + for information. + +* `ODS=work.dirsAndFilesInfo` - *Optional*, output data set, + name of a dataset to store information. + +* `details=0` - *Optional*, indicates if detailed info + will be collected, `1` = yes, `0` = no. + +* `keepDirs=1` - *Optional*, indicates if directories info + will be collected, `1` = yes, `0` = no. + +* `keepFiles=1` - *Optional*, indicates if files info + will be collected, `1` = yes, `0` = no. + +* `longFormat=0` - *Optional*, indicates if output be + in long format, `1` = yes, `0` = no. + +* `fileExt=` - *Optional*, if not missing then indicates + file extension to filter out results. + +* `maxDepth=0` - *Optional*, if not zero then indicates + maximum depth of search in the root path. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get list of files and directories: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Get detailed info: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result2,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Get only files info: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result3,keepDirs=0) + +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result5,keepDirs=0,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Get only directories info: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result4,keepFiles=0) + +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result6,keepFiles=0,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Filter out by `sas` extension: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(~/,ODS=work.result7,fileExt=sas) + +%dirsAndFiles(~/,ODS=work.result8,fileExt=sas,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** Keep result in the long format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(~/,ODS=work.result9,details=1,longFormat=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** Get info for maximum depth of 2: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result10,details=1,maxDepth=2) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 8.** How locked/unavailable files are handled: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(%sysfunc(pathname(WORK)),ODS=work.result11,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 9.** Not existing directory: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(%sysfunc(pathname(WORK))/noSuchDir,ODS=work.result12,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +--- + +## `%functionexists()` macro ###### + +## >>> `%functionExists()` macro: <<< ####################### + +The functionExists() macro function tests +if given funcion exists in the SAS session. +The `sashelp.vfunc` view is used. + +See examples below for the details. + +The `%functionExists()` macro executes like a pure macro code. + +The function is a result of cooperation with [Allan Bowe](https://www.linkedin.com/in/allanbowe/) + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%functionExists( + funName +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `funName` - *Required*, the name of the function + existence of which you are testing. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Test if function exists: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %functionExists(HASHING); + + %put %functionExists(COSsinLOG); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%getvars()` macro ###### + ## >>> `%getVars()` macro: <<< ####################### The getVars() and QgetVars() macro functions @@ -564,6 +996,7 @@ The basic syntax is the following, the `<...>` means optional parameters: an Explicit & Radical Refuse Of Run (aka ERROR). + ### EXAMPLES AND USECASES: #################################################### **EXAMPLE 1.** A list of all variables from the @@ -793,1930 +1226,543 @@ run; --- -## >>> `%QgetVars()` macro: <<< ####################### + +--- + +## `%intslist()` macro ###### + +## >>> `%intsList()` macro: <<< ####################### -The getVars() and QgetVars() macro functions -allow to extract variables names form a dataset -according to a given pattern into a list. +The intsList() macro function allows to print a list of +integers starting from `start` up to `end` incremented by `by` +and separated by `sep=`. -The getVars() returns unquoted value [by %unquote()]. -The QgetVars() returns quoted value [by %superq()]. +If `start`, `end` or `by` are non-integers the are converted to integers. -The `%QgetVars()` macro executes like a pure macro code. +See examples below for the details. + +The `%intsList()` macro executes like a pure macro code. ### SYNTAX: ################################################################### The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -%QgetVars( - ds - <,sep=> - <,pattern=> - <,varRange=> - <,quote=> +%intsList( + start + <,end> + <,by> + <,sep=> ) ~~~~~~~~~~~~~~~~~~~~~~~ **Arguments description**: -1. `ds` - *Required*, the name of the dataset from - which variables are to be taken. +1. `start` - *Required*, the first value of the list. + If `end` is missing then the list is generated + from 1 to `start` by 1. -* `sep = %str( )` - *Optional*, default value `%str( )`, - a variables separator on the created list. +2. `end` - *Required/Optional*, the last value of the list. -* `pattern = .*` - *Optional*, default value `.*` (i.e. any text), - a variable name regexp pattern, case INSENSITIVE! +3. `by` - *Required/Optional*, the increment of the list. + If missing then set to `1`. + *Cannot* be equal to `0`. -* `varRange = _all_` - *Optional*, default value `_all_`, - a named range list of variables. - -* `quote =` - *Optional*, default value is blank, a quotation - symbol to be used around values. - -### EXAMPLES AND USECASES: #################################################### - -See examples in `%getVars()` help for the details. +* `s = %str( )` - *Optional*, it is a separator between + elements of the list. Default value is space. --- -## >>> `%symdelGlobal()` macro: <<< ####################### + +### EXAMPLES AND USECASES: #################################################### -The `%symdelGlobal()` macro deletes all global macrovariables -created by the user. The only exceptions are read only variables -and variables the one which starts with SYS, AF, or FSP. -In that case a warning is printed in the log. +**EXAMPLE 1.** Simple list of integers from 1 to 10 by 1: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %intsList(10); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -One temporary global macrovariable `________________98_76_54_32_10_` -and a dataset, in `work` library, named `_%sysfunc(datetime(),hex7.)` -are created and deleted during the process. -The `%symdelGlobal()` macro executes like a pure macro code. +**EXAMPLE 2.** Ten copies of `sashelp.class` in `test11` to `test20`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data + %zipEvalf(test, %intsList(11,20)) + ; + set sashelp.class; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Non-integers are converted to integers, the list is `1 3 5`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %intsList(1.1,5.2,2.3); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** A list with a separator: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %intsList(1,5,2,sep=+); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%ldsn()` macro ###### + +## >>> `%LDSN()` macro: <<< ####################### + +The LDSN (Long DataSet Names) macro function +allows to use an "arbitrary" text string to name a dataset. + +The LDSN macro has some limitation described below, to overcome them +another macro, with different name: LDSNM (Long DataSet Names Modified) +was created. See its description to learn how to use it. + +--- + +The idea for the macro came from the following story: + +Good friend of mine, who didn't use SAS for quite some time, +told me that he lost a few hours for debugging because +he forgot that the SAS dataset name limitation is 32 bytes. + +I replied that it shouldn't be a problem to do a workaround +for this inconvenience with a macro and the `MD5()` hashing function. + +I said: *The macro should take an "arbitrary string" for a dataset +name, convert it, with help of `MD5()`, to a hash digest, and +create a dataset with an "artificial" `hex16.` formated name.* + +Starting with something like this: + +~~~~~~~~~~~~~~~~~~~~~~~sas +data %LDSN(work. peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s (drop = sex rename=(name=first_name) where = (age in (12,13,14))) ); + set sashelp.class; +run; +~~~~~~~~~~~~~~~~~~~~~~~ + +the macro would do: + +~~~~~~~~~~~~~~~~~~~~~~~sas +%sysfunc(MD5(peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s), hex16.) +~~~~~~~~~~~~~~~~~~~~~~~ + +and (under the hood) return and execute the following code: + +~~~~~~~~~~~~~~~~~~~~~~~sas +data work.DSN_41D599EF51FBA58_(drop = sex rename=(name=first_name) where = (age in (12,13,14))) ; + set sashelp.class; +run; +~~~~~~~~~~~~~~~~~~~~~~~ + +Also in the next data step user should be able to do: + +~~~~~~~~~~~~~~~~~~~~~~~sas +data my_next_data_step; + set %DSN(work. peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s); +run; +~~~~~~~~~~~~~~~~~~~~~~~ + +and work without the "dataset-name-length-limitation" issue. + +--- + +See examples below for the details. + +The `%LDSN()` macro executes like a pure macro code. + +**Known "Limitations":** + +- dataset name _cannot_ contain dots (`.`) since they are used as separators! + +- dataset name _cannot_ contain round brackets(`(` and `)`) since they are used as separators + (but `[]` and `{}` are allowed)! + +- dataset name _cannot_ contain unpaired quotes (`'` and `"`), + text: `a "hot-dog"` is ok, but `John's dog` is not! + +**Behaviour:** + +- dataset name text is *converted to upcase* + +- dataset name text *leading and trailing spaces are ignored*, + e.g. the following will give the same hash digest: + `%ldsn(work.test)`, `%ldsn( work.test)`, `%ldsn(work.test )`, + `%ldsn(work .test)`, `%ldsn(work. test)`, `%ldsn(work . test)`. + +- macro calls of the form: + `data %LDSN(); run;`, `data %LDSN( ); run;`, `data %LDSN( . ); run;` or even + `data %LDSN( . (keep=x)); run;` are resolved to empty string, so the result is + equivalent to `data; run;` ### SYNTAX: ################################################################### The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -%symdelGlobal( - info +%LDSN( + arbitrary text string (in line with limitations) ) ~~~~~~~~~~~~~~~~~~~~~~~ -**Arguments description**: +The text string is concider as *"fully qualified dataset name"*, i.e. macro +assumes it may contain library as prefix and data set options as sufix. +See the `%LDsNm()` macro for comparison. -1. `info` - *Optional*, default value should be empty, - if set to `NOINFO` or `QUIET` then infos and - warnings about variables deletion are suspended. +--- ### EXAMPLES AND USECASES: #################################################### -**EXAMPLE 1.** Basic use-case one. - Delete global macrovariables, info notes - and warnings are printed in the log. - +**EXAMPLE 1.** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let a = 1; - %let b = 2; - %let c = 3; - %let sys_my_var = 11; - %let af_my_var = 22; - %let fsp_my_var = 33; - %global / readonly read_only_x = 1234567890; - - %put _user_; - - %symdelGlobal(); - - %put _user_; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**EXAMPLE 2.** Basic use-case two. - Delete global macrovariables in quite mode - No info notes and warnings are printed in the log. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let a = 1; - %let b = 2; - %let c = 3; - %let sys_my_var = 11; - %let af_my_var = 22; - %let fsp_my_var = 33; - %global / readonly read_only_x = 1234567890; - - %put _user_; - %put *%symdelGlobal(NOINFO)*; - %put _user_; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ---- - -## >>> `bool.` format: <<< ####################### - -The **bool** format returns: -*zero* for 0 or missing, -*one* for other values. - -### EXAMPLES AND USECASES: #################################################### - -It allows for a %sysevalf()'ish -conversion-type [i.e. `%sysevalf(1.7 & 4.2, boolean)`] -inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), bool.)`] - ---- - -## >>> `boolz.` format: <<< ####################### - -The **boolz** format returns: -*zero* for 0 or missing, -*one* for other values. - -*Fuzz* value is 0. - -### EXAMPLES AND USECASES: #################################################### - -It allows for a %sysevalf()'ish -conversion-type [i.e. `%sysevalf(1.7 & 4.2, boolean)`] -inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), boolz.)`] - ---- - -## >>> `ceil.` format: <<< ####################### - -The **ceil** format is a "wrapper" for the `ceil()` function. - -### EXAMPLES AND USECASES: #################################################### - -It allows for a %sysevalf()'ish -conversion-type [i.e. `%sysevalf(1.7 + 4.2, ceil)`] -inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), ceil.)`] - ---- - -## >>> `floor.` format: <<< ####################### - -The **floor** format is a "wrapper" for the `floor()` function. - -### EXAMPLES AND USECASES: #################################################### - -It allows for a %sysevalf()'ish -conversion-type [i.e. `%sysevalf(1.7 + 4.2, floor)`] -inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), floor.)`] - ---- - -## >>> `int.` format: <<< ####################### - -The **int** format is a "wrapper" for the `int()` function. - -### EXAMPLES AND USECASES: #################################################### - -It allows for a %sysevalf()'ish -conversion-type [i.e. `%sysevalf(1.7 + 4.2, integer)`] -inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), int.)`] - ---- - -## >>> `arrFill()` subroutine: <<< ####################### - -The **arrFill()** subroutine is a wrapper -for the Call Fillmatrix() [a special FCMP subroutine]. - -A numeric array is filled with selected numeric value, e.g. - -for array `A = [. . . .]` the subroutine -`call arrFill(42, A)` returns `A = [42 42 42 42]` - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -call arrFill(N ,A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `N` - Numeric value. - -2. `A` - Numeric array. - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data _null_; - array X[*] a b c; - - put "before: " (_all_) (=); - call arrFill(42, X); - put "after: " (_all_) (=); +options nomprint source nomlogic nosymbolgen ls = max ps = max; +data %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s (drop = sex rename=(name=first_name) where = (age in (12,13,14))) ); + set sashelp.class; run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +proc print data = %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ); +run; + +data MyNextDataset; + set %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ); + where age > 12; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- -## >>> `arrFillC()` subroutine: <<< ####################### - -The **arrFillC()** subroutine fills -a character array with selected character value, e.g. - -for array `A = [" ", " ", " "]` the subroutine -`call arrFillC("B", A)` returns `A = ["B", "B", "B"]` - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -call arrFillC(C ,A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `C` - Character value. - -2. `A` - Character array. - + +--- -### EXAMPLES AND USECASES: #################################################### +## `%ldsnm()` macro ###### + +## >>> `%LDSNM()` macro: <<< ####################### -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data _null_; - array X[*] $ a b c; +The LDSNM (Long DataSet Names Modified) macro function +allows to use an "arbitrary" text string to name a dataset. - put "before: " (_all_) (=); - call arrFillC("ABC", X); - put "after: " (_all_) (=); +The LDSN macro had some limitation (see its documentation), to overcome them +another `%LDSNM()` (Long DataSet Names Modified) macro was created. -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The main idea behind the `%LDSNM()` is the same as for `%LDSN()` - see the description there. --- -## >>> `arrMissFill()` subroutine: <<< ####################### +The `%LDSNM()` works differently then the `%LDSN()`. -The **arrMissFill()** subroutine fills -all missing values (i.e. less or equal than `.Z`) -of a numeric array with selected numeric value, e.g. +The `%LDSN()` assumed that *both* libname and dataset options *could* +be passed as elements in macro argument, e.g. -for array `A = [1 . . 4]` the subroutine -`call arrMissFill(42, A)` returns `A = [1 42 42 4]` - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -call arrMissFill(N ,A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `N` - Numeric value. - -2. `A` - Numeric array. - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data have; - input a b c; -cards4; -1 . 3 -. 2 . -. . 3 -;;;; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data %LDSN( WORK.peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s (drop = sex) ); + set sashelp.class; run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -data _null_; - set have ; - array X[*] a b c; - - put "before: " (_all_) (=); - call arrMissFill(42, X); - put "after: " (_all_) (=); +The `%LDSNM()`, in contrary, assumes that both libname and dataset options are +passed **outside** the macro, i.e. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data WORK.%LDSNM( peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s ) (drop = sex); + set sashelp.class; run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ---- +This approach reduces some limitations the LDSN has. -## >>> `arrMissFillC()` subroutine: <<< ####################### +The **additional** feature of the `%LDSNM()` is that when the macro is called +a global macrovariable, which name is the same as hashed dataset name, is created. +The macrovariable value is the text of the argument of the macro. For example +the following macro call: -The **arrMissFillC()** subroutine fills -all missing values of a character array -with selected character value, e.g. - -for array `A = ["A", " ", "C"]` the subroutine -`call arrMissFillC("B", A)` returns `A = ["A", "B", "C"]` - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -call arrMissFillC(C, A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `C` - Character value. - -2. `A` - Character array. - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data have; - infile cards dsd dlm="," missover; - input (a b c) (: $ 1.); -cards4; -A, ,C - ,B, - , ,C -;;;; -run; - -data _null_; - set have ; - array X[*] $ a b c; - - put "before: " (_all_) (=); - call arrMissFillC("X", X); - put "after: " (_all_) (=); - -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `arrMissToLeft()` subroutine: <<< ####################### - -The **arrMissToLeft()** subroutine shifts -all non-missing (i.e. greater than `.Z`) -numeric elements to the right side of an array -and missing values to the left, e.g. - -for array `A = [1 . 2 . 3]` the subroutine -`call arrMissToLeft(A)` returns `A = [. . 1 2 3]` - -All missing values are replaced with the dot (`.`) - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -call arrMissToLeft(A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `A` - Numeric array. - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data have; - input a b c; -cards4; -1 . 3 -. 2 . -. . 3 -;;;; -run; - -data _null_; - set have ; - array X[*] a b c; - - put "before: " (_all_) (=); - call arrMissToLeft(X); - put "after: " (_all_) (=); - -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `arrMissToLeftC()` subroutine: <<< ####################### - -The **arrMissToLeftC()** subroutine shifts -all non-missing (i.e. different than empty string) -character elements to the right side of an array -and all missing values to the left, e.g. - -for array `A = ["A", " ", "B", " ", "C"]` the subroutine -`call arrMissToLeftC(A)` returns `A = [" ", " ", "A", "B", "C"]` - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -call arrMissToLeftC(A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `A` - Character array. - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data have; - infile cards dsd dlm="," missover; - input (a b c) (: $ 1.); -cards4; -A, ,C - ,B, - , ,C -;;;; -run; - -data _null_; - set have ; - array X[*] $ a b c; - - put "before: " (_all_) (=); - call arrMissToLeftC(X); - put "after: " (_all_) (=); - -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `arrMissToRight()` subroutine: <<< ####################### - -The **arrMissToRight()** subroutine shifts -all non-missing (i.e. greater than `.Z`) -numeric elements to the left side of an array -and missing values to the right, e.g. - -for array `A = [1 . 2 . 3]` the subroutine -`call arrMissToRight(A)` returns `A = [1 2 3 . .]` - -All missing values are replaced with the dot (`.`) - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -call arrMissToRight(A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `A` - Numeric array. - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data have; - input a b c; -cards4; -1 . 3 -. 2 . -. . 3 -;;;; -run; - -data _null_; - set have ; - array X[*] a b c; - - put "before: " (_all_) (=); - call arrMissToRight(X); - put "after: " (_all_) (=); - -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `arrMissToRightC()` subroutine: <<< ####################### - -The **arrMissToRightC()** subroutine shifts -all non-missing (i.e. different than empty string) -character elements to the left side of an array -and missing values to the right, e.g. - -for array `A = ["A", " ", "B", " ", "C"]` the subroutine -`call arrMissToRightC(A)` returns `A = ["A", "B", "C", " ", " "]` - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -call arrMissToRightC(A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `A` - Character array. - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data have; - infile cards dsd dlm="," missover; - input (a b c) (: $ 1.); -cards4; -A, ,C - ,B, - , ,C -;;;; -run; - -data _null_; - set have ; - array X[*] $ a b c; - - put "before: " (_all_) (=); - call arrMissToRightC(X); - put "after: " (_all_) (=); - -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `catXFc()` function: <<< ####################### - -The **catXFc()** function is a wrapper -of the `catX()` function but with ability -to format character values. - -For array `A = ["a", " ", "c"]` the -`catXFc("upcase.", "*", A)` returns `"A*C"`. - -If format does not handle nulls they are ignored. - -*Caution!* Array parameters to function calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -catXFc(format, delimiter, A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `format` - A name of the *character* format to be used. - -2. `delimiter` - A delimiter string to be used. - -3. `A` - Character array - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data _null_; - t = "t"; - u = " "; - v = "v"; - - array b[*] t u v; - - length s $ 17; - s = catXFc("upcase.", "*", B); - put (_all_) (=); -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `catXFi()` function: <<< ####################### - -The **catXFi()** function is a wrapper -of the `catX()` function but with ability -to format numeric values but -IGNORES missing values (i.e. `._`, `.`, `.a`, ..., `.z`). - -For array `A = [0, ., 2]` the -`catXFi("date9.", "#", A)` returns -`"01JAN1960#03JAN1960"` - -*Caution!* Array parameters to function calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -catXFi(format, delimiter, A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `format` - A name of the *numeric* format to be used. - -2. `delimiter` - A delimiter string to be used. - -3. `A` - Numeric array - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data _null_; - x = 1; - y = .; - z = 3; - - array a[*] x y z; - - length s $ 17; - s = catXFi("z5.", "#", A); - put (_all_) (=); -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `catXFj()` function: <<< ####################### - -The **catXFj()** function is a wrapper -of the catX() function but with ability -to format character values. - -For array `A = ["a", " ", "c"]` the -`catXFj("upcase.", "*", A)` returns `"A**C"` - -If format does not handle nulls they are -printed as an empty string. - -*Caution!* Array parameters to function calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -catXFj(format, delimiter, A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `format` - A name of the *character* format to be used. - -2. `delimiter` - A delimiter string to be used. - -3. `A` - Character array - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data _null_; - t = "t"; - u = " "; - v = "v"; - - array b[*] t u v; - - length s $ 17; - s = catXFj("upcase.", "*", B); - put (_all_) (=); -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `catXFn()` function: <<< ####################### - -The **catXFn()** function is a wrapper -of the `catX()` function but with ability -to format numeric values. - -For array `A = [0, 1, 2]` the -`catXFn("date9.", "#", A)` returns -`"01JAN1960#02JAN1960#03JAN1960"` - -*Caution!* Array parameters to function calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -catXFn(format, delimiter, A) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `format` - A name of the *numeric* format to be used. - -2. `delimiter` - A delimiter string to be used. - -3. `A` - Numeric array - - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data _null_; - x = 1; - y = .; - z = 3; - - array a[*] x y z; - - length s $ 17; - s = catXFn("z5.", "#", A); - put (_all_) (=); -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `delDataset()` function: <<< ####################### - -The **delDataset()** function is a "wrapper" -for the `Fdelete()` function. -`delDataset()` function uses a text string with -a dataset name as an argument. - -Function checks for `*.sas7bdat`, `*.sas7bndx`, -and `*.sas7bvew` files and delete them. -Return code of 0 means dataset was deleted. - -For compound library files are -deleted from _ALL_ locations! - - -*Note:* -Currently only the BASE SAS engine datasets/views are deleted. - -Tested on Windows and Linux. Not tested on Z/OS. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -delDataset(lbds_) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `lbds_` - *Required*, character argument containing - name of the dataset/view to be deleted. - The `_last_` special name is honored. - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - data TEST1 TEST2(index=(x)); - x = 17; - run; - - data TEST3 / view=TEST3; - set test1; - run; - - data _null_; - p = delDataset("WORK.TEST1"); - put p=; - - p = delDataset("TEST2"); - put p=; - - p = delDataset("WORK.TEST3"); - put p=; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**Example 2.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - data TEST4; - x=42; - run; - data _null_; - p = delDataset("_LAST_"); - put p=; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**Example 3.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - options dlcreatedir; - libname user "%sysfunc(pathname(work))/user"; - - data TEST5; - x=42; - run; - - data _null_; - p = delDataset("test5"); - put p=; - run; - - libname user clear; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**Example 4.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - data TEST6; - x=42; - run; - - %put *%sysfunc(delDataset(test6))*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**Example 5.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - options dlcreatedir; - libname L1 "%sysfunc(pathname(work))/L)1"; - libname L2 "%sysfunc(pathname(work))/L(2"; - libname L3 "%sysfunc(pathname(work))/L'3"; - - data L1.TEST7 L2.TEST7 L3.TEST7; - x=42; - run; - - libname L12 ("%sysfunc(pathname(work))/L(1" "%sysfunc(pathname(work))/L)2"); - libname L1L2 (L2 L3); - - %put *%sysfunc(delDataset(L12.test7))*; - %put *%sysfunc(delDataset(L1L2.test7))*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `qsortInCbyProcProto()` proto function: <<< ####################### - -The **qsortInCbyProcProto()** is external *C* function, -this is the implementation of the *Quick Sort* algorithm. - -The function is used **internally** by -functions in the *BasePlus* package. - -Asumptions: -- smaller subarray is sorted first, -- subarrays of *size < 11* are sorted by *insertion sort*, -- pivot is selected as median of low index value, - high index value, and (low+high)/2 index value. - -`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`
-`!CAUTION! Sorted array CANNOT contains SAS missing values !`
-`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`
- -### SYNTAX: ################################################################### - -The basic syntax is the following: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -qsortInCbyProcProto(arr, low, high) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `arr` - An array of double type to be sorted. - -2. `low` - An integer low index of starting position (from which the sorting is done). - -3. `high` - An integer high index of ending position (up to which the sorting is done). - - -### REFERENCES: #################################################### - -*Reference 1.* - -Insertion sort for arrays smaller then 11 elements: - -Based on the code from the following WikiBooks page [2020.08.14]: - -[https://pl.wikibooks.org/wiki/Kody_%C5%BAr%C3%B3d%C5%82owe/Sortowanie_przez_wstawianie](https://pl.wikibooks.org/wiki/Kody_%C5%BAr%C3%B3d%C5%82owe/Sortowanie_przez_wstawianie) - - -*Reference 2.* - -Iterative Quick Sort: - -Based on the code from the following pages [2020.08.14]: - -[https://www.geeksforgeeks.org/iterative-quick-sort/](https://www.geeksforgeeks.org/iterative-quick-sort/) - -[https://www.geeksforgeeks.org/c-program-for-iterative-quick-sort/](https://www.geeksforgeeks.org/c-program-for-iterative-quick-sort/) - ---- - -## >>> `fromMissingToNumberBS()` function: <<< ####################### - -The **fromMissingToNumberBS()** function -gets numeric missing value or a number -as an argument and returns an integer -from 1 to 29. - -For a numeric missing argument -the returned values are: -- 1 for `._` -- 2 for `.` -- 3 for `.a` -- ... -- 28 for `.z` and -- 29 for *all other*. - -The function is used **internally** by -functions in the *BasePlus* package. - -For *missing value arguments* the function -is an inverse of the `fromNumberToMissing()` function. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -fromMissingToNumberBS(x) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `x` - A numeric missing value or a number. - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - data _null_; - do x = ._, ., .a, .b, .c, 42; - y = fromMissingToNumberBS(x); - put x= y=; - end; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `fromNumberToMissing()` function: <<< ####################### - -The **fromNumberToMissing()** function -gets a number as an argument and returns -a numeric missing value or zero. - -For a numeric argument -the returned values are: -- `._` for 1 -- `.` for 2 -- `.a` for 3 -- ... -- `.z` for 28 and -- `0` for *all other*. - -The function is used **internally** by -functions in the *BasePlus* package. - -For arguments 1,2,3, ..., and 28 the function -is an inverse of the `fromMissingToNumberBS()` function. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -fromNumberToMissing(x) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `x` - A numeric value. - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - data _null_; - do x = 1 to 29; - y = fromNumberToMissing(x); - put x= y=; - end; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `quickSort4NotMiss()` subroutine: <<< ####################### - -The **quickSort4NotMiss()** subroutine is an alternative to the -`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) -when memory used by `call sortn()` may be an issue. -For smaller arrays the memory footprint is not significant. - -The subroutine is based on an iterative quick sort algorithm -implemented in the `qsortInCbyProcProto()` *C* prototype function. - - -**Caution 1!** Array _CANNOT_ contains missing values! - -**Caution 2!** Array parameters to subroutine calls must be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -call quickSort4NotMiss(A) +data %LDSNM(John "x" 'y' dog); + set sashelp.class; + where name = 'John'; +run; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**Arguments description**: +creates `DSN_BF1F8C4D6495B34A_` macrovariable with value: `JOHN "X" 'Y' DOG`. -1. `A` - Argument is a 1-based array of NOT missing numeric values. +The macrovariable is useful when combined with `symget()` function and +the `indsname=` option to get the original text string value back, +like in this example: - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** For session with 8GB of RAM, - array of size 250'000'000 with values in range - from 0 to 99'999'999 and _NO_ missing values. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let size = 250000000; - options fullstimer; - - data _null_; - array test[&size.] _temporary_ ; - - t = time(); - call streaminit(123); - do _N_ = &size. to 1 by -1; - test[_N_] = int(100000000*rand("uniform")); - end; - t = time() - t; - put "Array population time: " t; - - put "First 50 elements before sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - - t = time(); - call quickSort4NotMiss (test); - t = time()-t; - put "Sorting time: " / t=; - - put; put "First 50 elements after sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**Example 2.** Resources comparison for - session with 8GB of RAM. - - Array of size 250'000'000 with random values - from 0 to 999'999'999 and _NO_ missing values. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 8.82s - memory 1'953'470.62k - OS Memory 1'977'436.00k - - Call quickSort4NotMiss: - Sorting time 66.92s - Memory 1'954'683.06k - OS Memory 1'977'436.00k - - Call quickSortLight: - Sorting time 70.98s - Memory 1'955'479.71k - OS Memory 1'977'436.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `quickSortHash()` subroutine: <<< ####################### - -The **quickSortHash()** subroutine is an alternative to the -`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) -when memory used by `call sortn()` may be an issue. -For smaller arrays the memory footprint is not significant. - -The subroutine is based on an iterative quick sort algorithm -implemented in the `qsortInCbyProcProto()` *C* prototype function. - -The number of "sparse distinct data values" is set to `100'000` to -use the hash sort instead of the quick sort. - E.g. when number of unique values for sorting is less then - 100'000 then an ordered hash table is used to store the data - and their count and sort them. - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -*Note!* Due to improper memory reporting/releasing for hash - tables in FCMP procedure the reported memory used after running - the function may not be in line with the RAM memory required - for processing. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -call quickSortHash(A) +data test; + set %LDSNM(John "x" 'y' dog) indsname = i; + + indsname = symget(scan(i,-1,".")); +run; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**Arguments description**: - -1. `A` - Argument is a 1-based array of numeric values. - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** For session with 8GB of RAM - Array of size 250'000'000 with values in range - from 0 to 99'999'999 and around 10% of various - missing values. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let size = 250000000; - options fullstimer; - - data _null_; - array test[&size.] _temporary_ ; - - array m[0:27] _temporary_ - (._ . .A .B .C .D .E .F .G .H .I .J .K .L - .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); - - t = time(); - call streaminit(123); - do _N_ = &size. to 1 by -1; - _I_ + 1; - if rand("uniform") > 0.1 then test[_I_] = int(100000000*rand("uniform")); - else test[_I_] = m[mod(_N_,28)]; - end; - t = time() - t; - put "Array population time: " t; - - put "First 50 elements before sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - - t = time(); - call quickSortHash (test); - t = time()-t; - put "Sorting time: " / t=; - - put; put "First 50 elements after sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**Example 2.** For session with 8GB of RAM - Array of size 250'000'000 with values in range - from 0 to 9'999 and around 10% of various - missing values. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let size = 250000000; - options fullstimer; - - data _null_; - array test[&size.] _temporary_ ; - - array m[0:27] _temporary_ - (._ . .A .B .C .D .E .F .G .H .I .J .K .L - .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); - - t = time(); - call streaminit(123); - do _N_ = &size. to 1 by -1; - _I_ + 1; - if rand("uniform") > 0.1 then test[_I_] = int(10000*rand("uniform")); - else test[_I_] = m[mod(_N_,28)]; - end; - t = time() - t; - put "Array population time: " t; - - put "First 50 elements before sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - - t = time(); - call quickSortHash (test); - t = time()-t; - put "Sorting time: " / t=; - - put; put "First 50 elements after sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**Example 3.** Resources comparison for - session with 8GB of RAM - - A) Array of size 10'000'000 with - random values from 0 to 9'999 range (sparse) - and around 10% of missing data. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 0.61s - Memory 78'468.50k - OS Memory 101'668.00k - - Call sortn: - Sorting time 0.87s - Memory 1'120'261.53k - OS Memory 1'244'968.00k - - Call quickSortHash: - Sorting time 6.76s - Memory 1'222'242.75k(*) - OS Memory 1'402'920.00k(*) - - Call quickSortLight: - Sorting time 23.45s - Memory 80'527.75k - OS Memory 101'924.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - B) Array of size 10'000'000 with - random values from 0 to 99'999'999 range (dense) - and around 10% of missing data. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 0.6s - Memory 78'463.65k - OS Memory 101'924.00k - - Call sortn: - Sorting time 1.51s - Memory 1'120'253.53k - OS Memory 1'244'968.00k - - Call quickSortHash: - Sorting time 6.28s - Memory 1'222'241.93k(*) - OS Memory 1'402'920.00k(*) - - Call quickSortLight: - Sorting time 0.78s - Memory 80'669.28k - OS Memory 102'436.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - C) Array of size 250'000'000 with - random values from 0 to 999'999'999 range (dense) - and around 10% of missing data. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 15.34s - memory 1'953'471.81k - OS Memory 1'977'436.00k - - Call sortn: - FATAL: Insufficient memory to execute DATA step program. - Aborted during the COMPILATION phase. - ERROR: The SAS System stopped processing this step - because of insufficient memory. - - Call quickSortHash: - Sorting time 124.68s - Memory 7'573'720.34k(*) - OS Memory 8'388'448.00k(*) - - Call quickSortLight: - Sorting time 72.41s - Memory 1'955'520.78k - OS Memory 1'977'180.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - D) Array of size 250'000'000 with - random values from 0 to 99'999 range (sparse) - and around 10% of missing data. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 16.07 - Memory 1'953'469.78k - OS Memory 1'977'180.00k - - Call sortn: - FATAL: Insufficient memory to execute DATA step program. - Aborted during the COMPILATION phase. - ERROR: The SAS System stopped processing this step - because of insufficient memory. - - Call quickSortHash: - Sorting time 123.5s - Memory 7'573'722.03k - OS Memory 8'388'448.00k - - Call quickSortLight: - Sorting time 1'338.25s - Memory 1'955'529.90k - OS Memory 1'977'436.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -(*) When using hash tables in `Proc FCMP` the RAM - usage is not indicated properly. The memory - allocation is reported up to the session limit - and then reused if needed. The really required - memory is in fact much less then reported. - ---- - -## >>> `quickSortHashSDDV()` subroutine: <<< ####################### - -The **quickSortHashSDDV()** subroutine is an alternative to the -`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) -when memory used by `call sortn()` may be an issue. -For smaller arrays the memory footprint is not significant. - -The subroutine is based on an iterative quick sort algorithm -implemented in the `qsortInCbyProcProto()` *C* prototype function. - -The number of "sparse distinct data values" (argument `SDDV`) may -be adjusted to use the hash sort instead of the quick sort. - E.g. when number of unique values for sorting is less then - some *N* then an ordered hash table is used to store the data - and their count and sort them. - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -*Note!* Due to improper memory reporting/releasing for hash - tables in FCMP procedure the report memory used after running - the function may not be in line with the RAM memory required - for processing. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -call quickSortHashSDDV(A, SDDV) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `A` - Argument is a 1-based array of numeric values. - -2. `SDDV` - A number of distinct data values, e.g. 100'000. - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** For session with 8GB of RAM - Array of size 250'000'000 with values in range - from 0 to 99'999'999 and around 10% of various - missing values. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let size = 250000000; - options fullstimer; - - data _null_; - array test[&size.] _temporary_ ; - - array m[0:27] _temporary_ - (._ . .A .B .C .D .E .F .G .H .I .J .K .L - .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); - - t = time(); - call streaminit(123); - do _N_ = &size. to 1 by -1; - _I_ + 1; - if rand("uniform") > 0.1 then test[_I_] = int(100000000*rand("uniform")); - else test[_I_] = m[mod(_N_,28)]; - end; - t = time() - t; - put "Array population time: " t; - - put "First 50 elements before sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - - t = time(); - call quickSortHashSDDV (test, 2e4); - t = time()-t; - put "Sorting time: " / t=; - - put; put "First 50 elements after sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**Example 2.** For session with 8GB of RAM - Array of size 250'000'000 with values in range - from 0 to 9'999 and around 10% of various - missing values. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let size = 250000000; - options fullstimer; - - data _null_; - array test[&size.] _temporary_ ; - - array m[0:27] _temporary_ - (._ . .A .B .C .D .E .F .G .H .I .J .K .L - .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); - - t = time(); - call streaminit(123); - do _N_ = &size. to 1 by -1; - _I_ + 1; - if rand("uniform") > 0.1 then test[_I_] = int(10000*rand("uniform")); - else test[_I_] = m[mod(_N_,28)]; - end; - t = time() - t; - put "Array population time: " t; - - put "First 50 elements before sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - - t = time(); - call quickSortHashSDDV (test, 2e4); - t = time()-t; - put "Sorting time: " / t=; - - put; put "First 50 elements after sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `quickSortLight()` subroutine: <<< ####################### - -The **quickSortLight()** subroutine is an alternative to the -`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) -when memory used by `call sortn()` may be an issue. -For smaller arrays the memory footprint is not significant. - -The subroutine is based on an iterative quick sort algorithm -implemented in the `qsortInCbyProcProto()` *C* prototype function. - -*Caution!* Array parameters to subroutine calls *must* be 1-based. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -call quickSortLight(A) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `A` - Argument is a 1-based array of numeric values. - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** For session with 8GB of RAM - Array of size 250'000'000 with values in range - from 0 to 99'999'999 and around 10% of various - missing values. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let size = 250000000; - options fullstimer; - - data _null_; - array test[&size.] _temporary_ ; - - array m[0:27] _temporary_ - (._ . .A .B .C .D .E .F .G .H .I .J .K .L - .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); - - t = time(); - call streaminit(123); - do _N_ = &size. to 1 by -1; - _I_ + 1; - if rand("uniform") > 0.1 then test[_I_] = int(100000000*rand("uniform")); - else test[_I_] = m[mod(_N_,28)]; - end; - t = time() - t; - put "Array population time: " t; - - put "First 50 elements before sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - - t = time(); - call quickSortLight (test); - t = time()-t; - put "Sorting time: " / t=; - - put; put "First 50 elements after sorting:"; - do _N_ = 1 to 20; - put test[_N_] = @; - end; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**Example 2.** Resources comparison for - session with 8GB of RAM. - - Array of size 250'000'000 with random values - from 0 to 999'999'999 and _NO_ missing values. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 8.82s - memory 1'953'470.62k - OS Memory 1'977'436.00k - - Call quickSort4NotMiss: - Sorting time 66.92s - Memory 1'954'683.06k - OS Memory 1'977'436.00k - - Call quickSortLight: - Sorting time 70.98s - Memory 1'955'479.71k - OS Memory 1'977'436.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Example 3.** Resources comparison for - session with 8GB of RAM - - A) Array of size 10'000'000 with - random values from 0 to 9'999 range (sparse) - and around 10% of missing data. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 0.61s - Memory 78'468.50k - OS Memory 101'668.00k - - Call sortn: - Sorting time 0.87s - Memory 1'120'261.53k - OS Memory 1'244'968.00k - - Call quickSortHash: - Sorting time 6.76s - Memory 1'222'242.75k(*) - OS Memory 1'402'920.00k(*) - - Call quickSortLight: - Sorting time 23.45s - Memory 80'527.75k - OS Memory 101'924.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - B) Array of size 10'000'000 with - random values from 0 to 99'999'999 range (dense) - and around 10% of missing data. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 0.6s - Memory 78'463.65k - OS Memory 101'924.00k - - Call sortn: - Sorting time 1.51s - Memory 1'120'253.53k - OS Memory 1'244'968.00k - - Call quickSortHash: - Sorting time 6.28s - Memory 1'222'241.93k(*) - OS Memory 1'402'920.00k(*) - - Call quickSortLight: - Sorting time 0.78s - Memory 80'669.28k - OS Memory 102'436.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - C) Array of size 250'000'000 with - random values from 0 to 999'999'999 range (dense) - and around 10% of missing data. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 15.34s - memory 1'953'471.81k - OS Memory 1'977'436.00k - - Call sortn: - FATAL: Insufficient memory to execute DATA step program. - Aborted during the COMPILATION phase. - ERROR: The SAS System stopped processing this step - because of insufficient memory. - - Call quickSortHash: - Sorting time 124.68s - Memory 7'573'720.34k(*) - OS Memory 8'388'448.00k(*) - - Call quickSortLight: - Sorting time 72.41s - Memory 1'955'520.78k - OS Memory 1'977'180.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - D) Array of size 250'000'000 with - random values from 0 to 99'999 range (sparse) - and around 10% of missing data. -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - Array: - Population time 16.07 - Memory 1'953'469.78k - OS Memory 1'977'180.00k - - Call sortn: - FATAL: Insufficient memory to execute DATA step program. - Aborted during the COMPILATION phase. - ERROR: The SAS System stopped processing this step - because of insufficient memory. - - Call quickSortHash: - Sorting time 123.5s - Memory 7'573'722.03k - OS Memory 8'388'448.00k - - Call quickSortLight: - Sorting time 1'338.25s - Memory 1'955'529.90k - OS Memory 1'977'436.00k -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -(*) When using hash tables in `Proc FCMP` the RAM - usage is not indicated properly. The memory - allocation is reported up to the session limit - and then reused if needed. The really required - memory is in fact much less then reported. +See examples below for the details. --- -## >>> `%dedupListS()` macro: <<< ####################### +The `%LDSN()` macro executes like a pure macro code. -The `%dedupListS()` macro deletes duplicated values from -a *SPACE separated* list of values. List, including separators, -can be no longer than a value carried by a single macrovariable. +**Known "Limitations":** -Returned value is *unquoted*. +- dataset name _cannot_ contain _unpaired_ round brackets(`(` and `)`) + (but unmatched `[]` and `{}` are allowed)! -The `%dedupListS()` macro executes like a pure macro code. +- dataset name _cannot_ contain _unpaired_ quotes (`'` and `"`), + text: `a "hot-dog"` is ok, but `John's dog` is not! + +**Behaviour:** + +- dataset name text is *converted to upcase* + +- dataset name text *leading and trailing spaces are ignored*, + e.g. the following will give the same hash digest: + `%ldsn(test)`, `%ldsn( test)`, `%ldsn(test )`. + +- macro calls of the form: + `data %LDSN(); run;` or `data %LDSN( ); run;` are resolved + to empty string, so the result is equivalent to `data; run;` + +- created macrovariable is _global_ in scope. ### SYNTAX: ################################################################### The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -%dedupListS( - list of space separated values +%LDSNM( + arbitrary text string (in line with limitations) ) ~~~~~~~~~~~~~~~~~~~~~~~ -**Arguments description**: +The text string is concider as *"only dataset name"*, i.e. macro does not +assume it contain library as prefix or data set options as sufix. +See the `%LDSN()` macro for comparison. -1. `list` - A list of *space separated* values. +--- ### EXAMPLES AND USECASES: #################################################### -**EXAMPLE 1.** Basic use-case one. - Delete duplicated values from a list. - +**EXAMPLE 1.** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put *%dedupListS(a b c b c)*; +data %LDSNM(John "x" 'y' & dog); + set sashelp.class; + where name = 'John'; +run; - %put *%dedupListS(a b,c b,c)*; +data %LDSNM(John "x"[ 'y' & dog); + set sashelp.class; + where name = 'John'; +run; - %put *%dedupListS(%str(a b c b c))*; - - %put *%dedupListS(%str(a) %str(b) %str(c) b c)*; +data %LDSNM(John "x" 'y'} & dog); + set sashelp.class; + where name = 'John'; +run; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**EXAMPLE 2.** Macro variable as an argument. - Delete duplicated values from a list. +**EXAMPLE 2.** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let list = 4 5 6 1 2 3 1 2 3 4 5 6; - %put *%dedupListS(&list.)*; +data work.%LDsNm( peanut butter & jelly, a hot-dog in [a box], and s(*)t(*)a(*)r(*)s!! ) (drop = sex rename=(name=first_name) where = (age in (12,13,14))) +; + set sashelp.class; +run; + +data test; + set work.%LDsNm( peanut butter & jelly, a hot-dog in [a box], and s(*)t(*)a(*)r(*)s!! ) indsname=i; + + indsname=symget(scan(i,-1,".")); +run; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data work.%LDsNm( . ); + set sashelp.class; +run; + +data %LDsNm( ); + set sashelp.class; +run; + + +data %LDsNm(); + set sashelp.class; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + --- -## >>> `%dedupListC()` macro: <<< ####################### + +--- + +## `%lvarnm()` macro ###### + +## >>> `%LVarNm()` macro: <<< ####################### -The `%dedupListC()` macro deletes duplicated values from -a *COMMA separated* list of values. List, including separators, -can be no longer than a value carried by a single macrovariable. +The LVarNm() macro function works like the LDSN() macro function, but for variables. +Supported by LVarNmLab() macro function which allows to remember "user names" in labels. -Returned value is *unquoted*. Leading and trailing spaces are ignored. +The motivation for the macro was similar one as for the LDSN() macro. -The `%dedupListC()` macro executes like a pure macro code. +--- + +See examples below for the details. + +The `%LVarNm()` macro executes like a pure macro code. + +**Known "Limitations":** + +- variable name _cannot_ contain unpaired quotes (`'` and `"`), + text: `a "hot-dog"` is ok, but `John's dog` is not! + +**Behaviour:** + +- variable name text is *converted to upcase* + +- variable name text *leading and trailing spaces are ignored*, + e.g. the following will give the same hash digest: + `%LVarNm(test)`, `%LVarNm( test)`, `%LVarNm(test )`. + +- if the user want to add an extra suffix to the variable, + e.g. to get a numerical suffix, the `%LVarNm()` macro + **has** to be wrapped inside the `%unquote()` macro function. +~~~~~~~~~~~~~~~~~~~~~~~sas +data test4; + array X[*] %unquote(%LVarNm(some strange! name))_0 - %unquote(%LVarNm(some strange! name))_10; + + do i = lbound(X) to hbound(X); + X[i] = 2**(i-1); + put X[i]=; + end; +run; +~~~~~~~~~~~~~~~~~~~~~~~ + The reason for this is a "bug" like behaviour of SAS tokenizer/macroprocesor. + See the following SAS-L discussion thread: + `https://listserv.uga.edu/scripts/wa-UGA.exe?A2=SAS-L;4b2bcf80.2205A&S=` ### SYNTAX: ################################################################### The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -%dedupListC( - list,of,comma,separated,values +%LVarNm( + arbitrary text string (in line with limitations) ) ~~~~~~~~~~~~~~~~~~~~~~~ -**Arguments description**: - -1. `list` - A list of *comma separated* values. +--- ### EXAMPLES AND USECASES: #################################################### -**EXAMPLE 1.** Basic use-case one. - Delete duplicated values from a list. - +**EXAMPLE 1.** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put *%dedupListC(a,b,c,b,c)*; +options ls=max; +data test; + %LVarNmLab( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) - %put *%dedupListC(a,b c,b c)*; + do %LVarNm( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) = 1 to 10; - %put *%dedupListC(%str(a,b,c,b,c))*; + y = 5 + %LVarNm( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) * 17; + output; + end; +run; - %put *%dedupListC(%str(a),%str(b),%str(c),b,c)*; +data test2; + set test; + where %LVarNm( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) < 5; +run; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**EXAMPLE 2.** Leading and trailing spaces are ignored. - Delete duplicated values from a list. +**EXAMPLE 2.** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put *%dedupListC( a , b b , c , b b, c )*; +data test3; + %LVarNmLab() = 17; + + %LVarNm() = 17; + + %LVarNm( ) = 42; + + %LVarNm( ) = 303; +run; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**EXAMPLE 3.** Macro variable as an argument. - Delete duplicated values from a list. +**EXAMPLE 3.** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let list = 4, 5, 6, 1, 2, 3, 1, 2, 3, 4, 5, 6; - %put *%dedupListC(&list.)*; +data test3; + %LVarNm(test) = 1; + + %LVarNm( test) = 2; + + %LVarNm(test ) = 3; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data test4; + array X[*] %LVarNm(some strange! name)_0 - %LVarNm(some strange! name)_10; + + do i = lbound(X) to hbound(X); + X[i] = 2**(i-1); + put X[i]=; + end; +run; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- -## >>> `%dedupListP()` macro: <<< ####################### + +--- + +## `%lvarnmlab()` macro ###### + +## >>> `%LVarNmLab()` macro: <<< ####################### -The `%dedupListP()` macro deletes duplicated values from -a *PIPE(`|`) separated* list of values. List, including separators, -can be no longer than a value carried by a single macrovariable. +The LVarNmLab() macro function supports LVarNm() and allows to remember "user names" in labels. -Returned value is *unquoted*. Leading and trailing spaces are ignored. +The motivation for the macro was similar one as for the LDSN() macro. -The `%dedupListP()` macro executes like a pure macro code. +--- + +See examples in LVarNm() documentation for the details. + +The `%LVarNmLab()` macro executes like a pure macro code. + +**Known "Limitations":** + +- variable name _cannot_ contain unpaired quotes (`'` and `"`), + text: `a "hot-dog"` is ok, but `John's dog` is not! + +**Behaviour:** + +- variable name text is *converted to upcase* + +- variable name text *leading and trailing spaces are ignored*, + e.g. the following will give the same hash digest: + `%LVarNmLab(test)`, `%LVarNmLab( test)`, `%LVarNmLab(test )`. ### SYNTAX: ################################################################### The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -%dedupListP( - list|of|pipe|separated|values +%LVarNmLab( + arbitrary text string (in line with limitations) ) ~~~~~~~~~~~~~~~~~~~~~~~ -**Arguments description**: - -1. `list` - A list of *pipe separated* values. - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Basic use-case one. - Delete duplicated values from a list. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put *%dedupListP(a|b|c|b|c)*; - - %put *%dedupListP(a|b c|b c)*; - - %put *%dedupListP(%str(a|b|c|b|c))*; - - %put *%dedupListP(%str(a)|%str(b)|%str(c)|b|c)*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**EXAMPLE 2.** Leading and trailing spaces are ignored. - Delete duplicated values from a list. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put *%dedupListP( a | b b | c | b b| c )*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**EXAMPLE 3.** Macro variable as an argument. - Delete duplicated values from a list. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let list = 4|5|6|1|2|3|1|2|3|4|5|6; - %put *%dedupListP(&list.)*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - --- -## >>> `%dedupListX()` macro: <<< ####################### - -The `%dedupListX()` macro deletes duplicated values from -a *X separated* list of values, where the `X` represents -a *single character* separator. List, including separators, -can be no longer than a value carried by a single macrovariable. - -**Caution.** The value of `X` *has to be* in **the first** byte of the list, - just after the opening bracket, i.e. `(X...)`. - -Returned value is *unquoted*. Leading and trailing spaces are ignored. - -The `%dedupListX()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%dedupListX( -XlistXofXxXseparatedXvalues -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `list` - A list of *X separated* values. - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Basic use-case one. - Delete duplicated values from a list. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put *%dedupListX(|a|b|c|b|c)*; - - %put *%dedupListX( a b c b c)*; - - %put *%dedupListX(,a,b,c,b,c)*; - - %put *%dedupListX(XaXbXcXbXc)*; - - %put *%dedupListX(/a/b/c/b/c)*; - - data _null_; - x = "%dedupListX(%str(;a;b;c;b;c))"; - put x=; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**EXAMPLE 2.** Leading and trailing spaces are ignored. - Delete duplicated values from a list. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put *%dedupListX(| a | b.b | c | b.b| c )*; - - %put *%dedupListX(. a . b b . c . b b. c )*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**EXAMPLE 3.** Macro variable as an argument. - Delete duplicated values from a list. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let list = 4$5.5$6$1$2$3$1$2$3$4$5.5$6; - %put *%dedupListX($&list.)*; - - %let list = 4$ 5.5$ 6$ 1$ 2$ 3$ 1$ 2$ 3$ 4$ 5.5$ 6$; - %put *%dedupListX( &list.)*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - + --- - + +## `%qdeduplistx()` macro ###### + ## >>> `%QdedupListX()` macro: <<< ####################### The `%QdedupListX()` macro deletes duplicated values from @@ -2790,123 +1836,64 @@ XlistXofXxXseparatedXvalues --- -## >>> `brackets.` format: <<< ####################### - -The **brackets** format adds brackets around a text or a number. -Leading and trailing spaces are dropped before adding brackets. - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data _null_; - input x; - if x < 0 then put x= brackets.; - else put x= best32.; -cards; -2 -1 -0 --1 --2 -; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - + --- - -## >>> `semicolon.` format: <<< ####################### - -The **semicolon** format adds semicolon after text or number. -Leading and trailing spaces are dropped before adding semicolon. - -### EXAMPLES AND USECASES: #################################################### - -**Example 1.** - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data _null_; - x = 1; - y = "A"; - put x= semicolon. y= $semicolon.; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `bracketsC()` function: <<< ####################### - -The **bracketsC()** function is internal function used by the *brackets* format. -Returns character value of length 32767. +## `%qgetvars()` macro ###### + +## >>> `%QgetVars()` macro: <<< ####################### + +The getVars() and QgetVars() macro functions +allow to extract variables names form a dataset +according to a given pattern into a list. + +The getVars() returns unquoted value [by %unquote()]. +The QgetVars() returns quoted value [by %superq()]. + +The `%QgetVars()` macro executes like a pure macro code. + ### SYNTAX: ################################################################### -The basic syntax is the following: +The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -bracketsC(X) +%QgetVars( + ds + <,sep=> + <,pattern=> + <,varRange=> + <,quote=> +) ~~~~~~~~~~~~~~~~~~~~~~~ **Arguments description**: -1. `X` - Character value. +1. `ds` - *Required*, the name of the dataset from + which variables are to be taken. ---- +* `sep = %str( )` - *Optional*, default value `%str( )`, + a variables separator on the created list. -## >>> `bracketsN()` function: <<< ####################### +* `pattern = .*` - *Optional*, default value `.*` (i.e. any text), + a variable name regexp pattern, case INSENSITIVE! + +* `varRange = _all_` - *Optional*, default value `_all_`, + a named range list of variables. + +* `quote =` - *Optional*, default value is blank, a quotation + symbol to be used around values. -The **bracketsN()** function is internal function used by the *brackets* format. -Returns character value of length 34. -### SYNTAX: ################################################################### +### EXAMPLES AND USECASES: #################################################### -The basic syntax is the following: -~~~~~~~~~~~~~~~~~~~~~~~sas -bracketsN(X) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `X` - Numeric value. +See examples in `%getVars()` help for the details. --- -## >>> `semicolonC()` function: <<< ####################### - -The **semicolonC()** function is internal function used by the *semicolon* format. -Returns character value of length 32767. + +--- -### SYNTAX: ################################################################### - -The basic syntax is the following: -~~~~~~~~~~~~~~~~~~~~~~~sas -semicolonC(X) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `X` - Character value. - ---- - -## >>> `semicolonN()` function: <<< ####################### - -The **semicolonN()** function is internal function used by the *semicolon* format. -Returns character value of length 33. +## `%qzipevalf()` macro ###### -### SYNTAX: ################################################################### - -The basic syntax is the following: -~~~~~~~~~~~~~~~~~~~~~~~sas -semicolonN(X) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `X` - Numeric value. - ---- - ## >>> `%QzipEvalf()` macro: <<< ####################### The zipEvalf() and QzipEvalf() macro functions @@ -2982,267 +1969,18 @@ The basic syntax is the following, the `<...>` means optional parameters: to format the result, does not work when the `operator=` is used. + ### EXAMPLES AND USECASES: #################################################### See examples in `%zipEvalf()` help for the details. --- -## >>> `%zipEvalf()` macro: <<< ####################### - -The zipEvalf() and QzipEvalf() macro functions -allow to use a function on elements of pair of -space separated lists. - -For two space separated lists of text strings 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 lists is shorter then elements are "reused" starting -from the beginning. - -The zipEvalf() returns unquoted value [by %unquote()]. -The QzipEvalf() returns quoted value [by %superq()]. - -See examples below for the details. - -The `%zipEvalf()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%zipEvalf( - first - ,second - <,function=> - <,operator=> - <,argBf=> - <,argMd=> - <,argAf=> - <,format=> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**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. - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Simple concatenation of elements: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y); -%put &=x; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** Shorter list is "reused": -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%let x = %zipEvalf(1 2 3 4 5 6, a b c); -%put &=x; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Use of the `operator=`, shorter list is "reused": -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%let y = %zipEvalf(1 2 3 4 5 6, 100 200, operator = +); -%put &=y; - -%let z = %zipEvalf(1 2 3 4 5 6 8 9 10, 1 2 3 4 5 6 8 9 10, operator = **); -%put &=z; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 4.** Format result: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y, format=$upcase.); -%put &=x; - -%put * -%zipEvalf( - ą ż ś ź ę ć ń ó ł -,Ą Ż Ś Ź Ę Ć Ń Ó Ł -,format = $brackets. -) -*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 5.** Use with macrovariables: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%let abc = 10 100 1000; -%put * -%zipEvalf( -%str(1 2 3 4 5 6 7 8 9) -,&abc. -,function = sum -) -*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 6.** If one of elements is empty: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put * -%zipEvalf( - abc efg -, -) -*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 7.** Use of the `function=`, shorter list is "reused": -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put * -%zipEvalf( - a b c -,efg -,function = catx -,argBf = %str(,) -,format = $brackets. -) -*; - -%put * -%zipEvalf( - a b c -,efg -,function = catx -,argBf = %str( ) -,format = $upcase. -) -*; - -%put * -%zipEvalf( - %str(! @ # $ [ ] % ^ & * ) -,1 2 3 4 5 6 7 8 9 -,function = catx -,argBf = %str( ) -,format = $quote. -) -*; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 8.** Use inside resolve: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data _null_; -z = resolve(' -%zipEvalf( - %nrstr(! @ # $ [ ] % ^ & *) -,1 2 3 4 5 6 7 8 9 -,function = catx -,argBf = %str(.) -,format = $quote. -)'); -put z=; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 9.** Use in data step: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data test; - %zipEvalf( - a b c d e f g - ,1 2 3 4 5 6 7 - ,function = catx - ,argBf = = - ,format = $semicolon. - ) -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 10.** With 9.4M6 hashing() function: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put %zipEvalf(MD5 SHA1 SHA256 SHA384 SHA512 CRC32, abcd, function = HASHING); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 11.** Use middle argument: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%let x = %zipEvalf(1 2 3 4 5 6, 2020, argMd=5, function=MDY, format=date11.); -%put &=x; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - + --- - -## >>> `%functionExists()` macro: <<< ####################### - -The functionExists() macro function tests -if given funcion exists in the SAS session. -The `sashelp.vfunc` view is used. - -See examples below for the details. - -The `%functionExists()` macro executes like a pure macro code. - -The function is a result of cooperation with [Allan Bowe](https://www.linkedin.com/in/allanbowe/) - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%functionExists( - funName -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `funName` - *Required*, the name of the function - existence of which you are testing. - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Test if function exists: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %functionExists(HASHING); - - %put %functionExists(COSsinLOG); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - + +## `%raincloudplot()` macro ###### + ## >>> `%RainCloudPlot()` macro: <<< ####################### The RainCloudPlot() macro allow to plot Rain Cloud plots, i.e. pots of @@ -3488,6 +2226,7 @@ See examples below. --- + ### EXAMPLES AND USECASES: #################################################### **EXAMPLE 1.** Simple Rain Cloud Plot for a `have` dataset: @@ -3524,8 +2263,6 @@ See examples below. The output: ![Example 1](./baseplus_RainCloudPlot_Ex1.png) - - **EXAMPLE 2.** Rain Cloud plot for `sashelp.cars` dataset with groups by Origin or Type for Invoice variables: @@ -3548,9 +2285,7 @@ The output: ![Example 2a](./baseplus_RainCloudPlot_Ex2a.png) ![Example 2b](./baseplus_RainCloudPlot_Ex2b.png) - - -**EXAMPLE 3.** Rain Cloud plot with formatted groups +**EXAMPLE 3.** Rain Cloud plot with formated groups: and annotations. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas @@ -3606,17 +2341,115 @@ The output: --- -## >>> `%zipLibrary()` macro: <<< ####################### + +--- + +## `%repeattxt()` macro ###### + +## >>> `%repeatTxt()` macro: <<< ####################### -The zipLibrary() macro allows to zip content of a SAS library. +The repeatTxt() macro function allows to repeat `n` +times a `text` string separated by string `s=`. -Files can be zipped into a single file (named as the input library) -or into multiple files (named as "dataset.sas7bdat.zip"). -If a file is indexed also the index file is zipped. +The repeatTxt() returns unquoted value [by %unquote()]. -Source files can be deleted after compression. +See examples below for the details. -Status of compression and processing time is reported. +The `%repeatTxt()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%repeatTxt( + text + <,n> + <,s=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `text` - *Required*, a text to be repeated. + +2. `n` - *Required/Optional*, the number of repetitions. + If missing then set to `1`; + +* `s = %str( )` - *Optional*, it is a separator between + repeated elements. Default value is space. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple repetition of dataset name: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options mprint; +data work.test5; + set + %repeatTxt(sashelp.cars, 5) + ; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Simple repetition of data step: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options mprint; +%repeatTxt(data _null_; set sashelp.cars; run;, 3) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** "Nice" output: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repeatTxt(#,15,s=$) HELLO SAS! %repeatTxt(#,15,s=$); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Macroquote a text with commas: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%repeatTxt( + %str(proc sql; create table wh as select weight,height from sashelp.class; quit;) + ,3 +) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 5.** Empty `n` repeats `text` one time: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options mprint; +data work.test1; + set + %repeatTxt(sashelp.cars) + ; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 6.** Dynamic "formatting": +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%macro printWork(); + %let work=%sysfunc(pathname(work)); + %put +%repeatTxt(~,%length(&work.)+5,s=)+; + %put {&=work.}; + %put +%repeatTxt(~,%length(&work.)+5,s=)+; +%mend printWork; + +%printWork() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%splitdsintoblocks()` macro ###### + +## >>> `%splitDSIntoBlocks()` macro: <<< ####################### + +The splitDSIntoBlocks() macro allows to split the `set` dataset into blocks +of size `blockSize` in datasets: `prefix1` to `prefixN`. + +The last dataset may have less observations then the `blockSize`. + +Macro covers `BASE` engine (`v9`, `v8`, `v7`, `v6`) and `SPDE` engine datasets. See examples below for the details. @@ -3624,94 +2457,325 @@ See examples below for the details. The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -%zipLibrary( - lib - <,mode=> - <,clean=> - <,libOut=> - <,compression=> +%splitDSIntoBlocks( + blockSize + <,set> + <,prefix> ) ~~~~~~~~~~~~~~~~~~~~~~~ **Arguments description**: -1. `lib` - *Required*, a name of the library to be zipped. - Must be a valid SAS V7, V8, or V9 library. +1. `blockSize` - *Required*, the size of the block of data, + in other words number of observations in + one block of split data. + Block size must be positive integer. +2. `set` - *Required/Optional*, the name of the dataset to split. + If empty then `&syslast.` is used. -* `mode = S` - *Optional*, default value is `S`, - indicates mode of compression - generates single zip file (`SINGLE/S`) - or multiple files (`MULTI/M`) +3. `prefix` - *Required/Optional*, the name-prefix for new datasets. + If missing then set to `part`. -* `clean = 0` - *Optional*, default value is `0`, - should datasets be deleted after zipping? - `1` means *yes*, `0` means *no*. - -* `libOut =` - *Optional*, default value is empty, - output library for a single zip file. - -* `compression =` - *Optional*, default value is `6`, - specifies the compression level - `0` to `9`, where `0` is no compression - and `9` is maximum compression. +--- + ### EXAMPLES AND USECASES: #################################################### -**EXAMPLE 1.** Generate data: +**EXAMPLE 1.** Split `sashelp.class` into 5 elements datasets ABC1 to ABC4: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - -options dlcreatedir; - libname test1 "%sysfunc(pathname(work))/test1"; - libname test2 "%sysfunc(pathname(work))/test2"; - libname test3 (test1 test2); - libname test4 "%sysfunc(pathname(work))/test4"; -options nodlcreatedir; - -%put %sysfunc(pathname(test3)); -%put %sysfunc(pathname(test4)); - -data - test1.A(index=(model)) - test1.B - test2.C - test2.D(index=(model make io=(invoice origin))) -; - set sashelp.cars; -run; - -data test1.B2 / view=test1.B2; - set test1.B; - output; - output; -run; + %splitDSIntoBlocks(5,sashelp.class,ABC) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**EXAMPLE 2.** Zip content of test3 library - into the same location in one zip file: +**EXAMPLE 2.** By default splits the `_last_` dataset into `part1` to `partN` datasets: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%zipLibrary(test3) + data lastData; + set sashelp.cars; + run; + + %splitDSIntoBlocks(123) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**EXAMPLE 3.** Zip content of test3 library - into the same location in multiple zip files: +**EXAMPLE 3.** Works with `SPDE` engine too: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%zipLibrary(test3, mode=MULTI) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + options dlcreatedir; + libname test "%sysfunc(pathname(work))/testSPDE"; + libname test; + libname test SPDE "%sysfunc(pathname(work))/testSPDE"; + data test.test; + set sashelp.cars; + run; -**EXAMPLE 4.** Zip content of test3 library - with maximum compression level - into different location in one zip file - and delete source files: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%zipLibrary(test3, clean=1, libOut=test4, compression=9) + %splitDSIntoBlocks(100,test.test,work.spde) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- + +--- + +## `%splitdsintoparts()` macro ###### + +## >>> `%splitDSIntoParts()` macro: <<< ####################### + +The splitDSIntoParts() macro allows to split the `set` dataset into `parts` parts +of approximately `NOBS/parts` size in datasets: `prefix1` to `prefixN`. + +The splitDSIntoParts() macro internally runs the splitDSIntoBlocks() macro. + +Macro covers `BASE` engine (`v9`, `v8`, `v7`, `v6`) and `SPDE` engine datasets. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%splitDSIntoParts( + parts + <,set> + <,prefix> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `parts` - *Required*, the number of parts to split data into. + Number of parts must be positive integer. + +2. `set` - *Required/Optional*, the name of the dataset to split. + If empty then `&syslast.` is used. + +3. `prefix` - *Required/Optional*, the name-prefix for new datasets. + If missing then set to `part`. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Split `sashelp.cars` into 7 parts: datasets carsInParts1 to carsInParts7: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %splitDSIntoParts(7,sashelp.cars, carsInParts) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** By default splits the `_last_` dataset into `part1` to `part3` datasets: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data lastData; + set sashelp.cars; + run; + + %splitDSIntoBlocks(3) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Works with `SPDE` engine too: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + options dlcreatedir; + libname test "%sysfunc(pathname(work))/testSPDE"; + libname test; + libname test SPDE "%sysfunc(pathname(work))/testSPDE"; + + data test.test; + set sashelp.cars; + run; + + %splitDSIntoParts(3,test.test,work.spde) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%symdelglobal()` macro ###### + +## >>> `%symdelGlobal()` macro: <<< ####################### + +The `%symdelGlobal()` macro deletes all global macrovariables +created by the user. The only exceptions are read only variables +and variables the one which starts with SYS, AF, or FSP. +In that case a warning is printed in the log. + +One temporary global macrovariable `________________98_76_54_32_10_` +and a dataset, in `work` library, named `_%sysfunc(datetime(),hex7.)` +are created and deleted during the process. + +The `%symdelGlobal()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%symdelGlobal( + info +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `info` - *Optional*, default value should be empty, + if set to `NOINFO` or `QUIET` then infos and + warnings about variables deletion are suspended. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete global macrovariables, info notes + and warnings are printed in the log. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let a = 1; + %let b = 2; + %let c = 3; + %let sys_my_var = 11; + %let af_my_var = 22; + %let fsp_my_var = 33; + %global / readonly read_only_x = 1234567890; + + %put _user_; + + %symdelGlobal(); + + %put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Basic use-case two. + Delete global macrovariables in quite mode + No info notes and warnings are printed in the log. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let a = 1; + %let b = 2; + %let c = 3; + %let sys_my_var = 11; + %let af_my_var = 22; + %let fsp_my_var = 33; + %global / readonly read_only_x = 1234567890; + + %put _user_; + %put *%symdelGlobal(NOINFO)*; + %put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- + + +--- + +## `%unziparch()` macro ###### + +## >>> `%unzipArch()` macro: <<< ####################### + +The unzipArch() macro allows to unzip content of a ZIP archive. +Macro is OS independent, the `XCMD` option is not required. + +The `dlCreateDir` option is used under the hood. + +Content of unzipped archive can be listed in the log. + +Source files can be deleted after decompression. +Errors of decompression and are reported. If any occur +the deletion is suspended. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%unzipArch( + archName + <,path=> + <,pathRef=> + <,target=> + <,targetRef=> + <,list=> + <,clean=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `archName` - *Required*, name of the ZIP archive to be extracted. + Name should be full, i.e., with the extension! + +* `path=` - *Optional*, a path pointing to zipped file location. + The path should be provided unquoted. + Default value is `WORK` location. + +* `pathRef=` - *Optional*, a fileref to path pointing to zipped file location. + The `path`, if not null, has precedense over the `pathRef`. + +* `target=` - *Optional*, a path pointing to target location where + files will be extracted. + The path should be provided unquoted. + Default value is `WORK` location. + +* `target=` - *Optional*, a fileref to path pointing to target location where + files will be extracted. + The `target`, if not null, has precedense over the `targetRef`. + +* `list = 0` - *Optional*, default value is `0`, + indicates if zip content should be listed in the log. + `1` means *yes*, `0` means *no*. + +* `clean = 0` - *Optional*, default value is `0`, + indicates if zip file should be deleted after unzipping. + `1` means *yes*, `0` means *no*. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Unzip compressed archive. Example requires the `basePlus` package. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +filename arch ZIP "%workPath()/testArch.zip"; + +data _null_; + file arch(abc/test1.txt); + put "text for test file 1"; +data _null_; + file arch(abc/subdir/test2.txt); + put "text for test file 2"; +data _null_; + file arch(abc/subdir/test3.txt); + put "text for test file 3"; +run; + +%unzipArch( + testArch.zip +, path = %workPath() +, target = %workPath() +, list=1 +); + + + +filename pR "%workPath()"; + +%unzipArch( + testArch.zip +, pathRef = pR +, targetRef = pR +, clean=1 +); + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%unziplibrary()` macro ###### + ## >>> `%unzipLibrary()` macro: <<< ####################### The unzipLibrary() macro allows to unzip content of a SAS library. @@ -3744,6 +2808,7 @@ The basic syntax is the following, the `<...>` means optional parameters: **Arguments description**: 1. `path` - *Required*, a path pointing to zipped file(s) location. + The path should be unquoted, e.g. `%unzipLibrary(/some/dir, ...)`. * `zip =` - *Optional*, When `mode=S` a name of the zip file containing SAS files to be unzipped. @@ -3761,6 +2826,7 @@ The basic syntax is the following, the `<...>` means optional parameters: output library for a single zip file decompression. + ### EXAMPLES AND USECASES: #################################################### **EXAMPLE 1.** Generate data: @@ -3831,8 +2897,11 @@ run; %unzipLibrary(%sysfunc(pathname(work)), zip=sashelp, mode=S, clean=1) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + --- - + +## `%ziparch()` macro ###### + ## >>> `%zipArch()` macro: <<< ####################### The zipArch() macro allows to ZIP content of a directory. @@ -3903,6 +2972,7 @@ The basic syntax is the following, the `<...>` means optional parameters: --- + ### EXAMPLES AND USECASES: #################################################### **EXAMPLE 1.** Zip a directory . Example requires the `basePlus` package. @@ -3963,18 +3033,246 @@ run; --- -## >>> `%unzipArch()` macro: <<< ####################### + +--- + +## `%zipevalf()` macro ###### + +## >>> `%zipEvalf()` macro: <<< ####################### -The unzipArch() macro allows to unzip content of a ZIP archive. -Macro is OS independent, the `XCMD` option is not required. +The zipEvalf() and QzipEvalf() macro functions +allow to use a function on elements of pair of +space separated lists. -The `dlCreateDir` option is used under the hood. +For two space separated lists of text strings the corresponding +elements are taken and the macro applies a function, provided by user, +to calculate result of the function on taken elements. -Content of unzipped archive can be listed in the log. +When one of the lists is shorter then elements are "reused" starting +from the beginning. -Source files can be deleted after decompression. -Errors of decompression and are reported. If any occur -the deletion is suspended. +The zipEvalf() returns unquoted value [by %unquote()]. +The QzipEvalf() returns quoted value [by %superq()]. + +See examples below for the details. + +The `%zipEvalf()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%zipEvalf( + first + ,second + <,function=> + <,operator=> + <,argBf=> + <,argMd=> + <,argAf=> + <,format=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**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. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple concatenation of elements: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y); +%put &=x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Shorter list is "reused": +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let x = %zipEvalf(1 2 3 4 5 6, a b c); +%put &=x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Use of the `operator=`, shorter list is "reused": +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let y = %zipEvalf(1 2 3 4 5 6, 100 200, operator = +); +%put &=y; + +%let z = %zipEvalf(1 2 3 4 5 6 8 9 10, 1 2 3 4 5 6 8 9 10, operator = **); +%put &=z; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Format result: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y, format=$upcase.); +%put &=x; + +%put * +%zipEvalf( + ą ż ś ź ę ć ń ó ł +,Ą Ż Ś Ź Ę Ć Ń Ó Ł +,format = $brackets. +) +*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Use with macrovariables: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let abc = 10 100 1000; +%put * +%zipEvalf( +%str(1 2 3 4 5 6 7 8 9) +,&abc. +,function = sum +) +*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** If one of elements is empty: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put * +%zipEvalf( + abc efg +, +) +*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** Use of the `function=`, shorter list is "reused": +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put * +%zipEvalf( + a b c +,efg +,function = catx +,argBf = %str(,) +,format = $brackets. +) +*; + +%put * +%zipEvalf( + a b c +,efg +,function = catx +,argBf = %str( ) +,format = $upcase. +) +*; + +%put * +%zipEvalf( + %str(! @ # $ [ ] % ^ & * ) +,1 2 3 4 5 6 7 8 9 +,function = catx +,argBf = %str( ) +,format = $quote. +) +*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 8.** Use inside resolve: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; +z = resolve(' +%zipEvalf( + %nrstr(! @ # $ [ ] % ^ & *) +,1 2 3 4 5 6 7 8 9 +,function = catx +,argBf = %str(.) +,format = $quote. +)'); +put z=; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 9.** Use in data step: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data test; + %zipEvalf( + a b c d e f g + ,1 2 3 4 5 6 7 + ,function = catx + ,argBf = = + ,format = $semicolon. + ) +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 10.** With 9.4M6 hashing() function: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %zipEvalf(MD5 SHA1 SHA256 SHA384 SHA512 CRC32, abcd, function = HASHING); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 11.** Use middle argument: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let x = %zipEvalf(1 2 3 4 5 6, 2020, argMd=5, function=MDY, format=date11.); +%put &=x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%ziplibrary()` macro ###### + +## >>> `%zipLibrary()` macro: <<< ####################### + +The zipLibrary() macro allows to zip content of a SAS library. + +Files can be zipped into a single file (named as the input library) +or into multiple files (named as "dataset.sas7bdat.zip"). +If a file is indexed also the index file is zipped. + +Source files can be deleted after compression. + +Status of compression and processing time is reported. See examples below for the details. @@ -3982,88 +3280,2043 @@ See examples below for the details. The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -%unzipArch( - archName - <,path=> - <,pathRef=> - <,target=> - <,targetRef=> - <,list=> - <,clean=> +%zipLibrary( + lib + <,mode=> + <,clean=> + <,libOut=> + <,compression=> ) ~~~~~~~~~~~~~~~~~~~~~~~ **Arguments description**: -1. `archName` - *Required*, name of the ZIP archive to be extracted. - Name should be full, i.e., with the extension! +1. `lib` - *Required*, a name of the library to be zipped. + Must be a valid SAS V7, V8, or V9 library. -* `path=` - *Optional*, a path pointing to zipped file location. - The path should be provided unquoted. - Default value is `WORK` location. -* `pathRef=` - *Optional*, a fileref to path pointing to zipped file location. - The `path`, if not null, has precedense over the `pathRef`. - -* `target=` - *Optional*, a path pointing to target location where - files will be extracted. - The path should be provided unquoted. - Default value is `WORK` location. - -* `target=` - *Optional*, a fileref to path pointing to target location where - files will be extracted. - The `target`, if not null, has precedense over the `targetRef`. - -* `list = 0` - *Optional*, default value is `0`, - indicates if zip content should be listed in the log. - `1` means *yes*, `0` means *no*. +* `mode = S` - *Optional*, default value is `S`, + indicates mode of compression + generates single zip file (`SINGLE/S`) + or multiple files (`MULTI/M`) * `clean = 0` - *Optional*, default value is `0`, - indicates if zip file should be deleted after unzipping. + should datasets be deleted after zipping? `1` means *yes*, `0` means *no*. ---- +* `libOut =` - *Optional*, default value is empty, + output library for a single zip file. + +* `compression =` - *Optional*, default value is `6`, + specifies the compression level + `0` to `9`, where `0` is no compression + and `9` is maximum compression. + ### EXAMPLES AND USECASES: #################################################### -**EXAMPLE 1.** Unzip compressed archive. Example requires the `basePlus` package. +**EXAMPLE 1.** Generate data: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -filename arch ZIP "%workPath()/testArch.zip"; +options dlcreatedir; + libname test1 "%sysfunc(pathname(work))/test1"; + libname test2 "%sysfunc(pathname(work))/test2"; + libname test3 (test1 test2); + libname test4 "%sysfunc(pathname(work))/test4"; +options nodlcreatedir; -data _null_; - file arch(abc/test1.txt); - put "text for test file 1"; -data _null_; - file arch(abc/subdir/test2.txt); - put "text for test file 2"; -data _null_; - file arch(abc/subdir/test3.txt); - put "text for test file 3"; +%put %sysfunc(pathname(test3)); +%put %sysfunc(pathname(test4)); + +data + test1.A(index=(model)) + test1.B + test2.C + test2.D(index=(model make io=(invoice origin))) +; + set sashelp.cars; run; -%unzipArch( - testArch.zip -, path = %workPath() -, target = %workPath() -, list=1 -); +data test1.B2 / view=test1.B2; + set test1.B; + output; + output; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +**EXAMPLE 2.** Zip content of test3 library + into the same location in one zip file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(test3) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -filename pR "%workPath()"; -%unzipArch( - testArch.zip -, pathRef = pR -, targetRef = pR -, clean=1 -); +**EXAMPLE 3.** Zip content of test3 library + into the same location in multiple zip files: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(test3, mode=MULTI) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 4.** Zip content of test3 library + with maximum compression level + into different location in one zip file + and delete source files: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(test3, clean=1, libOut=test4, compression=9) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +--- + +## `$bool.` format/informat ###### + +## >>> `bool.` format: <<< ####################### + +The **bool** format returns: +*zero* for 0 or missing, +*one* for other values. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 & 4.2, boolean)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), bool.)`] + +--- + + +--- + +## `$boolz.` format/informat ###### + +## >>> `boolz.` format: <<< ####################### + +The **boolz** format returns: +*zero* for 0 or missing, +*one* for other values. + +*Fuzz* value is 0. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 & 4.2, boolean)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), boolz.)`] + +--- + + +--- + +## `$ceil.` format/informat ###### + +## >>> `ceil.` format: <<< ####################### + +The **ceil** format is a "wrapper" for the `ceil()` function. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 + 4.2, ceil)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), ceil.)`] + +--- + + +--- + +## `$floor.` format/informat ###### + +## >>> `floor.` format: <<< ####################### + +The **floor** format is a "wrapper" for the `floor()` function. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 + 4.2, floor)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), floor.)`] + +--- + + +--- + +## `$int.` format/informat ###### + +## >>> `int.` format: <<< ####################### + +The **int** format is a "wrapper" for the `int()` function. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 + 4.2, integer)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), int.)`] + +--- + + +--- + +## `arrfill()` function ###### + +## >>> `arrFill()` subroutine: <<< ####################### + +The **arrFill()** subroutine is a wrapper +for the Call Fillmatrix() [a special FCMP subroutine]. + +A numeric array is filled with selected numeric value, e.g. + +for array `A = [. . . .]` the subroutine +`call arrFill(42, A)` returns `A = [42 42 42 42]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrFill(N ,A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `N` - Numeric value. + +2. `A` - Numeric array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + array X[*] a b c; + + put "before: " (_all_) (=); + call arrFill(42, X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrfillc()` function ###### + +## >>> `arrFillC()` subroutine: <<< ####################### + +The **arrFillC()** subroutine fills +a character array with selected character value, e.g. + +for array `A = [" ", " ", " "]` the subroutine +`call arrFillC("B", A)` returns `A = ["B", "B", "B"]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrFillC(C ,A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `C` - Character value. + +2. `A` - Character array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + array X[*] $ a b c; + + put "before: " (_all_) (=); + call arrFillC("ABC", X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmissfill()` function ###### + +## >>> `arrMissFill()` subroutine: <<< ####################### + +The **arrMissFill()** subroutine fills +all missing values (i.e. less or equal than `.Z`) +of a numeric array with selected numeric value, e.g. + +for array `A = [1 . . 4]` the subroutine +`call arrMissFill(42, A)` returns `A = [1 42 42 4]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissFill(N ,A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `N` - Numeric value. + +2. `A` - Numeric array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + input a b c; +cards4; +1 . 3 +. 2 . +. . 3 +;;;; +run; + +data _null_; + set have ; + array X[*] a b c; + + put "before: " (_all_) (=); + call arrMissFill(42, X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmissfillc()` function ###### + +## >>> `arrMissFillC()` subroutine: <<< ####################### + +The **arrMissFillC()** subroutine fills +all missing values of a character array +with selected character value, e.g. + +for array `A = ["A", " ", "C"]` the subroutine +`call arrMissFillC("B", A)` returns `A = ["A", "B", "C"]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissFillC(C, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `C` - Character value. + +2. `A` - Character array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + infile cards dsd dlm="," missover; + input (a b c) (: $ 1.); +cards4; +A, ,C + ,B, + , ,C +;;;; +run; + +data _null_; + set have ; + array X[*] $ a b c; + + put "before: " (_all_) (=); + call arrMissFillC("X", X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmisstoleft()` function ###### + +## >>> `arrMissToLeft()` subroutine: <<< ####################### + +The **arrMissToLeft()** subroutine shifts +all non-missing (i.e. greater than `.Z`) +numeric elements to the right side of an array +and missing values to the left, e.g. + +for array `A = [1 . 2 . 3]` the subroutine +`call arrMissToLeft(A)` returns `A = [. . 1 2 3]` + +All missing values are replaced with the dot (`.`) + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissToLeft(A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Numeric array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + input a b c; +cards4; +1 . 3 +. 2 . +. . 3 +;;;; +run; + +data _null_; + set have ; + array X[*] a b c; + + put "before: " (_all_) (=); + call arrMissToLeft(X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmisstoleftc()` function ###### + +## >>> `arrMissToLeftC()` subroutine: <<< ####################### + +The **arrMissToLeftC()** subroutine shifts +all non-missing (i.e. different than empty string) +character elements to the right side of an array +and all missing values to the left, e.g. + +for array `A = ["A", " ", "B", " ", "C"]` the subroutine +`call arrMissToLeftC(A)` returns `A = [" ", " ", "A", "B", "C"]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissToLeftC(A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Character array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + infile cards dsd dlm="," missover; + input (a b c) (: $ 1.); +cards4; +A, ,C + ,B, + , ,C +;;;; +run; + +data _null_; + set have ; + array X[*] $ a b c; + + put "before: " (_all_) (=); + call arrMissToLeftC(X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmisstoright()` function ###### + +## >>> `arrMissToRight()` subroutine: <<< ####################### + +The **arrMissToRight()** subroutine shifts +all non-missing (i.e. greater than `.Z`) +numeric elements to the left side of an array +and missing values to the right, e.g. + +for array `A = [1 . 2 . 3]` the subroutine +`call arrMissToRight(A)` returns `A = [1 2 3 . .]` + +All missing values are replaced with the dot (`.`) + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissToRight(A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Numeric array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + input a b c; +cards4; +1 . 3 +. 2 . +. . 3 +;;;; +run; + +data _null_; + set have ; + array X[*] a b c; + + put "before: " (_all_) (=); + call arrMissToRight(X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmisstorightc()` function ###### + +## >>> `arrMissToRightC()` subroutine: <<< ####################### + +The **arrMissToRightC()** subroutine shifts +all non-missing (i.e. different than empty string) +character elements to the left side of an array +and missing values to the right, e.g. + +for array `A = ["A", " ", "B", " ", "C"]` the subroutine +`call arrMissToRightC(A)` returns `A = ["A", "B", "C", " ", " "]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissToRightC(A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Character array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + infile cards dsd dlm="," missover; + input (a b c) (: $ 1.); +cards4; +A, ,C + ,B, + , ,C +;;;; +run; + +data _null_; + set have ; + array X[*] $ a b c; + + put "before: " (_all_) (=); + call arrMissToRightC(X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `bracketsc()` function ###### + +## >>> `bracketsC()` function: <<< ####################### + +The **bracketsC()** function is internal function used by the *brackets* format. +Returns character value of length 32767. + +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~sas +bracketsC(X) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `X` - Character value. + +--- + +--- + +## `bracketsn()` function ###### + +## >>> `bracketsN()` function: <<< ####################### + +The **bracketsN()** function is internal function used by the *brackets* format. +Returns character value of length 34. + +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~sas +bracketsN(X) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `X` - Numeric value. + +--- + +--- + +## `catxfc()` function ###### + +## >>> `catXFc()` function: <<< ####################### + +The **catXFc()** function is a wrapper +of the `catX()` function but with ability +to format character values. + +For array `A = ["a", " ", "c"]` the +`catXFc("upcase.", "*", A)` returns `"A*C"`. + +If format does not handle nulls they are ignored. + +*Caution!* Array parameters to function calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +catXFc(format, delimiter, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `format` - A name of the *character* format to be used. + +2. `delimiter` - A delimiter string to be used. + +3. `A` - Character array + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + t = "t"; + u = " "; + v = "v"; + + array b[*] t u v; + + length s $ 17; + s = catXFc("upcase.", "*", B); + put (_all_) (=); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `catxfi()` function ###### + +## >>> `catXFi()` function: <<< ####################### + +The **catXFi()** function is a wrapper +of the `catX()` function but with ability +to format numeric values but +IGNORES missing values (i.e. `._`, `.`, `.a`, ..., `.z`). + +For array `A = [0, ., 2]` the +`catXFi("date9.", "#", A)` returns +`"01JAN1960#03JAN1960"` + +*Caution!* Array parameters to function calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +catXFi(format, delimiter, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `format` - A name of the *numeric* format to be used. + +2. `delimiter` - A delimiter string to be used. + +3. `A` - Numeric array + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + x = 1; + y = .; + z = 3; + + array a[*] x y z; + + length s $ 17; + s = catXFi("z5.", "#", A); + put (_all_) (=); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `catxfj()` function ###### + +## >>> `catXFj()` function: <<< ####################### + +The **catXFj()** function is a wrapper +of the catX() function but with ability +to format character values. + +For array `A = ["a", " ", "c"]` the +`catXFj("upcase.", "*", A)` returns `"A**C"` + +If format does not handle nulls they are +printed as an empty string. + +*Caution!* Array parameters to function calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +catXFj(format, delimiter, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `format` - A name of the *character* format to be used. + +2. `delimiter` - A delimiter string to be used. + +3. `A` - Character array + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + t = "t"; + u = " "; + v = "v"; + + array b[*] t u v; + + length s $ 17; + s = catXFj("upcase.", "*", B); + put (_all_) (=); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `catxfn()` function ###### + +## >>> `catXFn()` function: <<< ####################### + +The **catXFn()** function is a wrapper +of the `catX()` function but with ability +to format numeric values. + +For array `A = [0, 1, 2]` the +`catXFn("date9.", "#", A)` returns +`"01JAN1960#02JAN1960#03JAN1960"` + +*Caution!* Array parameters to function calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +catXFn(format, delimiter, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `format` - A name of the *numeric* format to be used. + +2. `delimiter` - A delimiter string to be used. + +3. `A` - Numeric array + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + x = 1; + y = .; + z = 3; + + array a[*] x y z; + + length s $ 17; + s = catXFn("z5.", "#", A); + put (_all_) (=); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `deldataset()` function ###### + +## >>> `delDataset()` function: <<< ####################### + +The **delDataset()** function is a "wrapper" +for the `Fdelete()` function. +`delDataset()` function uses a text string with +a dataset name as an argument. + +Function checks for `*.sas7bdat`, `*.sas7bndx`, +and `*.sas7bvew` files and delete them. +Return code of 0 means dataset was deleted. + +For compound library files are +deleted from _ALL_ locations! + + +*Note:* +Currently only the BASE SAS engine datasets/views are deleted. + +Tested on Windows and Linux. Not tested on Z/OS. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +delDataset(lbds_) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `lbds_` - *Required*, character argument containing + name of the dataset/view to be deleted. + The `_last_` special name is honored. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data TEST1 TEST2(index=(x)); + x = 17; + run; + + data TEST3 / view=TEST3; + set test1; + run; + + data _null_; + p = delDataset("WORK.TEST1"); + put p=; + + p = delDataset("TEST2"); + put p=; + + p = delDataset("WORK.TEST3"); + put p=; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data TEST4; + x=42; + run; + data _null_; + p = delDataset("_LAST_"); + put p=; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 3.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + options dlcreatedir; + libname user "%sysfunc(pathname(work))/user"; + + data TEST5; + x=42; + run; + + data _null_; + p = delDataset("test5"); + put p=; + run; + + libname user clear; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 4.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data TEST6; + x=42; + run; + + %put *%sysfunc(delDataset(test6))*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 5.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + options dlcreatedir; + libname L1 "%sysfunc(pathname(work))/L)1"; + libname L2 "%sysfunc(pathname(work))/L(2"; + libname L3 "%sysfunc(pathname(work))/L'3"; + + data L1.TEST7 L2.TEST7 L3.TEST7; + x=42; + run; + + libname L12 ("%sysfunc(pathname(work))/L(1" "%sysfunc(pathname(work))/L)2"); + libname L1L2 (L2 L3); + + %put *%sysfunc(delDataset(L12.test7))*; + %put *%sysfunc(delDataset(L1L2.test7))*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `semicolonc()` function ###### + +## >>> `semicolonC()` function: <<< ####################### + +The **semicolonC()** function is internal function used by the *semicolon* format. +Returns character value of length 32767. + +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~sas +semicolonC(X) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `X` - Character value. + +--- + + +--- + +## `semicolonn()` function ###### + +## >>> `semicolonN()` function: <<< ####################### + +The **semicolonN()** function is internal function used by the *semicolon* format. +Returns character value of length 33. + +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~sas +semicolonN(X) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `X` - Numeric value. + +--- + + +--- + +## `$brackets.` format/informat ###### + +## >>> `brackets.` format: <<< ####################### + +The **brackets** format adds brackets around a text or a number. +Leading and trailing spaces are dropped before adding brackets. + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + input x; + if x < 0 then put x= brackets.; + else put x= best32.; +cards; +2 +1 +0 +-1 +-2 +; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `$semicolon.` format/informat ###### + +## >>> `semicolon.` format: <<< ####################### + +The **semicolon** format adds semicolon after text or number. +Leading and trailing spaces are dropped before adding semicolon. + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + x = 1; + y = "A"; + put x= semicolon. y= $semicolon.; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `qsortincbyprocproto()` proto ###### + +## >>> `qsortInCbyProcProto()` proto function: <<< ####################### + +The **qsortInCbyProcProto()** is external *C* function, +this is the implementation of the *Quick Sort* algorithm. + +The function is used **internally** by +functions in the *BasePlus* package. + +Asumptions: +- smaller subarray is sorted first, +- subarrays of *size < 11* are sorted by *insertion sort*, +- pivot is selected as median of low index value, + high index value, and (low+high)/2 index value. + +`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`
+`!CAUTION! Sorted array CANNOT contains SAS missing values !`
+`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`
+ +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +qsortInCbyProcProto(arr, low, high) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `arr` - An array of double type to be sorted. + +2. `low` - An integer low index of starting position (from which the sorting is done). + +3. `high` - An integer high index of ending position (up to which the sorting is done). + + +### REFERENCES: #################################################### + +*Reference 1.* + +Insertion sort for arrays smaller then 11 elements: + +Based on the code from the following WikiBooks page [2020.08.14]: + +[https://pl.wikibooks.org/wiki/Kody_%C5%BAr%C3%B3d%C5%82owe/Sortowanie_przez_wstawianie](https://pl.wikibooks.org/wiki/Kody_%C5%BAr%C3%B3d%C5%82owe/Sortowanie_przez_wstawianie) + + +*Reference 2.* + +Iterative Quick Sort: + +Based on the code from the following pages [2020.08.14]: + +[https://www.geeksforgeeks.org/iterative-quick-sort/](https://www.geeksforgeeks.org/iterative-quick-sort/) + +[https://www.geeksforgeeks.org/c-program-for-iterative-quick-sort/](https://www.geeksforgeeks.org/c-program-for-iterative-quick-sort/) + +--- + + +--- + +## `frommissingtonumberbs()` function ###### + +## >>> `fromMissingToNumberBS()` function: <<< ####################### + +The **fromMissingToNumberBS()** function +gets numeric missing value or a number +as an argument and returns an integer +from 1 to 29. + +For a numeric missing argument +the returned values are: +- 1 for `._` +- 2 for `.` +- 3 for `.a` +- ... +- 28 for `.z` and +- 29 for *all other*. + +The function is used **internally** by +functions in the *BasePlus* package. + +For *missing value arguments* the function +is an inverse of the `fromNumberToMissing()` function. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +fromMissingToNumberBS(x) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `x` - A numeric missing value or a number. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data _null_; + do x = ._, ., .a, .b, .c, 42; + y = fromMissingToNumberBS(x); + put x= y=; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `fromnumbertomissing()` function ###### + +## >>> `fromNumberToMissing()` function: <<< ####################### + +The **fromNumberToMissing()** function +gets a number as an argument and returns +a numeric missing value or zero. + +For a numeric argument +the returned values are: +- `._` for 1 +- `.` for 2 +- `.a` for 3 +- ... +- `.z` for 28 and +- `0` for *all other*. + +The function is used **internally** by +functions in the *BasePlus* package. + +For arguments 1,2,3, ..., and 28 the function +is an inverse of the `fromMissingToNumberBS()` function. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +fromNumberToMissing(x) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `x` - A numeric value. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data _null_; + do x = 1 to 29; + y = fromNumberToMissing(x); + put x= y=; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `quicksort4notmiss()` function ###### + +## >>> `quickSort4NotMiss()` subroutine: <<< ####################### + +The **quickSort4NotMiss()** subroutine is an alternative to the +`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) +when memory used by `call sortn()` may be an issue. +For smaller arrays the memory footprint is not significant. + +The subroutine is based on an iterative quick sort algorithm +implemented in the `qsortInCbyProcProto()` *C* prototype function. + + +**Caution 1!** Array _CANNOT_ contains missing values! + +**Caution 2!** Array parameters to subroutine calls must be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +call quickSort4NotMiss(A) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Argument is a 1-based array of NOT missing numeric values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** For session with 8GB of RAM, + array of size 250'000'000 with values in range + from 0 to 99'999'999 and _NO_ missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + test[_N_] = int(100000000*rand("uniform")); + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSort4NotMiss (test); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** Resources comparison for + session with 8GB of RAM. + + Array of size 250'000'000 with random values + from 0 to 999'999'999 and _NO_ missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 8.82s + memory 1'953'470.62k + OS Memory 1'977'436.00k + + Call quickSort4NotMiss: + Sorting time 66.92s + Memory 1'954'683.06k + OS Memory 1'977'436.00k + + Call quickSortLight: + Sorting time 70.98s + Memory 1'955'479.71k + OS Memory 1'977'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `quicksorthash()` function ###### + +## >>> `quickSortHash()` subroutine: <<< ####################### + +The **quickSortHash()** subroutine is an alternative to the +`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) +when memory used by `call sortn()` may be an issue. +For smaller arrays the memory footprint is not significant. + +The subroutine is based on an iterative quick sort algorithm +implemented in the `qsortInCbyProcProto()` *C* prototype function. + +The number of "sparse distinct data values" is set to `100'000` to +use the hash sort instead of the quick sort. + E.g. when number of unique values for sorting is less then + 100'000 then an ordered hash table is used to store the data + and their count and sort them. + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +*Note!* Due to improper memory reporting/releasing for hash + tables in FCMP procedure the reported memory used after running + the function may not be in line with the RAM memory required + for processing. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +call quickSortHash(A) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Argument is a 1-based array of numeric values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 99'999'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(100000000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortHash (test); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 9'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(10000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortHash (test); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 3.** Resources comparison for + session with 8GB of RAM + + A) Array of size 10'000'000 with + random values from 0 to 9'999 range (sparse) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 0.61s + Memory 78'468.50k + OS Memory 101'668.00k + + Call sortn: + Sorting time 0.87s + Memory 1'120'261.53k + OS Memory 1'244'968.00k + + Call quickSortHash: + Sorting time 6.76s + Memory 1'222'242.75k(*) + OS Memory 1'402'920.00k(*) + + Call quickSortLight: + Sorting time 23.45s + Memory 80'527.75k + OS Memory 101'924.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + B) Array of size 10'000'000 with + random values from 0 to 99'999'999 range (dense) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 0.6s + Memory 78'463.65k + OS Memory 101'924.00k + + Call sortn: + Sorting time 1.51s + Memory 1'120'253.53k + OS Memory 1'244'968.00k + + Call quickSortHash: + Sorting time 6.28s + Memory 1'222'241.93k(*) + OS Memory 1'402'920.00k(*) + + Call quickSortLight: + Sorting time 0.78s + Memory 80'669.28k + OS Memory 102'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + C) Array of size 250'000'000 with + random values from 0 to 999'999'999 range (dense) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 15.34s + memory 1'953'471.81k + OS Memory 1'977'436.00k + + Call sortn: + FATAL: Insufficient memory to execute DATA step program. + Aborted during the COMPILATION phase. + ERROR: The SAS System stopped processing this step + because of insufficient memory. + + Call quickSortHash: + Sorting time 124.68s + Memory 7'573'720.34k(*) + OS Memory 8'388'448.00k(*) + + Call quickSortLight: + Sorting time 72.41s + Memory 1'955'520.78k + OS Memory 1'977'180.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + D) Array of size 250'000'000 with + random values from 0 to 99'999 range (sparse) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 16.07 + Memory 1'953'469.78k + OS Memory 1'977'180.00k + + Call sortn: + FATAL: Insufficient memory to execute DATA step program. + Aborted during the COMPILATION phase. + ERROR: The SAS System stopped processing this step + because of insufficient memory. + + Call quickSortHash: + Sorting time 123.5s + Memory 7'573'722.03k + OS Memory 8'388'448.00k + + Call quickSortLight: + Sorting time 1'338.25s + Memory 1'955'529.90k + OS Memory 1'977'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +(*) When using hash tables in `Proc FCMP` the RAM + usage is not indicated properly. The memory + allocation is reported up to the session limit + and then reused if needed. The really required + memory is in fact much less then reported. + +--- + + +--- + +## `quicksorthashsddv()` function ###### + +## >>> `quickSortHashSDDV()` subroutine: <<< ####################### + +The **quickSortHashSDDV()** subroutine is an alternative to the +`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) +when memory used by `call sortn()` may be an issue. +For smaller arrays the memory footprint is not significant. + +The subroutine is based on an iterative quick sort algorithm +implemented in the `qsortInCbyProcProto()` *C* prototype function. + +The number of "sparse distinct data values" (argument `SDDV`) may +be adjusted to use the hash sort instead of the quick sort. + E.g. when number of unique values for sorting is less then + some *N* then an ordered hash table is used to store the data + and their count and sort them. + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +*Note!* Due to improper memory reporting/releasing for hash + tables in FCMP procedure the report memory used after running + the function may not be in line with the RAM memory required + for processing. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +call quickSortHashSDDV(A, SDDV) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Argument is a 1-based array of numeric values. + +2. `SDDV` - A number of distinct data values, e.g. 100'000. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 99'999'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(100000000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortHashSDDV (test, 2e4); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 9'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(10000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortHashSDDV (test, 2e4); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `quicksortlight()` function ###### + +## >>> `quickSortLight()` subroutine: <<< ####################### + +The **quickSortLight()** subroutine is an alternative to the +`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) +when memory used by `call sortn()` may be an issue. +For smaller arrays the memory footprint is not significant. + +The subroutine is based on an iterative quick sort algorithm +implemented in the `qsortInCbyProcProto()` *C* prototype function. + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +call quickSortLight(A) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Argument is a 1-based array of numeric values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 99'999'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(100000000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortLight (test); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** Resources comparison for + session with 8GB of RAM. + + Array of size 250'000'000 with random values + from 0 to 999'999'999 and _NO_ missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 8.82s + memory 1'953'470.62k + OS Memory 1'977'436.00k + + Call quickSort4NotMiss: + Sorting time 66.92s + Memory 1'954'683.06k + OS Memory 1'977'436.00k + + Call quickSortLight: + Sorting time 70.98s + Memory 1'955'479.71k + OS Memory 1'977'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 3.** Resources comparison for + session with 8GB of RAM + + A) Array of size 10'000'000 with + random values from 0 to 9'999 range (sparse) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 0.61s + Memory 78'468.50k + OS Memory 101'668.00k + + Call sortn: + Sorting time 0.87s + Memory 1'120'261.53k + OS Memory 1'244'968.00k + + Call quickSortHash: + Sorting time 6.76s + Memory 1'222'242.75k(*) + OS Memory 1'402'920.00k(*) + + Call quickSortLight: + Sorting time 23.45s + Memory 80'527.75k + OS Memory 101'924.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + B) Array of size 10'000'000 with + random values from 0 to 99'999'999 range (dense) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 0.6s + Memory 78'463.65k + OS Memory 101'924.00k + + Call sortn: + Sorting time 1.51s + Memory 1'120'253.53k + OS Memory 1'244'968.00k + + Call quickSortHash: + Sorting time 6.28s + Memory 1'222'241.93k(*) + OS Memory 1'402'920.00k(*) + + Call quickSortLight: + Sorting time 0.78s + Memory 80'669.28k + OS Memory 102'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + C) Array of size 250'000'000 with + random values from 0 to 999'999'999 range (dense) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 15.34s + memory 1'953'471.81k + OS Memory 1'977'436.00k + + Call sortn: + FATAL: Insufficient memory to execute DATA step program. + Aborted during the COMPILATION phase. + ERROR: The SAS System stopped processing this step + because of insufficient memory. + + Call quickSortHash: + Sorting time 124.68s + Memory 7'573'720.34k(*) + OS Memory 8'388'448.00k(*) + + Call quickSortLight: + Sorting time 72.41s + Memory 1'955'520.78k + OS Memory 1'977'180.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + D) Array of size 250'000'000 with + random values from 0 to 99'999 range (sparse) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 16.07 + Memory 1'953'469.78k + OS Memory 1'977'180.00k + + Call sortn: + FATAL: Insufficient memory to execute DATA step program. + Aborted during the COMPILATION phase. + ERROR: The SAS System stopped processing this step + because of insufficient memory. + + Call quickSortHash: + Sorting time 123.5s + Memory 7'573'722.03k + OS Memory 8'388'448.00k + + Call quickSortLight: + Sorting time 1'338.25s + Memory 1'955'529.90k + OS Memory 1'977'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +(*) When using hash tables in `Proc FCMP` the RAM + usage is not indicated properly. The memory + allocation is reported up to the session limit + and then reused if needed. The really required + memory is in fact much less then reported. + +--- + + +--- + +## `%date()` macro ###### + +## >>> `%date()` macro: <<< ####################### + +The date() macro function is a "lazy typer" wrapping up `%sysfunc(date())`. + +See examples below for the details. + +The `%date()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%date() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + + - `format` - *Optional*, if a value is provided + it should be a valid SAS format capable of handling + values produced by the `date()` function. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get value of `date()`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %date(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Get value of `date()` with a format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %date(date11.); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- + +--- + +## `%datetime()` macro ###### + +## >>> `%datetime()` macro: <<< ####################### + +The datetime() macro function is a "lazy typer" wrapping up `%sysfunc(datetime())`. + +See examples below for the details. + +The `%datetime()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%datetime() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + + - `format` - *Optional*, if a value is provided + it should be a valid SAS format capable of handling + values produced by the `datetime()` function. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get value of `datetime()`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %datetime(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 1.** Get value of `datetime()` as "long" and "short" ISO-8601: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %datetime(e8601dt.); + %put %datetime(b8601dt.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 1.** Get value of `datetime()` with user defined format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + proc format; + picture myCrazyDT (default=50) + other='%0Ssec. %0Mmin. %0Hhour %0dday %0mmonth %Yyear' (datatype=datetime) + ; + run; + + %put %datetime(myCrazyDT.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%downloadfilesto()` macro ###### + ## >>> `%downloadFilesTo()` macro: <<< ####################### The downloadFilesTo() macro copy files (in binary mode @@ -4145,6 +5398,7 @@ The basic syntax is the following, the `<...>` means optional parameters: --- + ### EXAMPLES AND USECASES: #################################################### **EXAMPLE 1.** Download data from web with diect list and then copy between directories: @@ -4183,1121 +5437,11 @@ run; --- -## >>> `%LDSN()` macro: <<< ####################### - -The LDSN (Long DataSet Names) macro function -allows to use an "arbitrary" text string to name a dataset. - -The LDSN macro has some limitation described below, to overcome them -another macro, with different name: LDSNM (Long DataSet Names Modified) -was created. See its description to learn how to use it. - ---- - -The idea for the macro came from the following story: - -Good friend of mine, who didn't use SAS for quite some time, -told me that he lost a few hours for debugging because -he forgot that the SAS dataset name limitation is 32 bytes. - -I replied that it shouldn't be a problem to do a workaround -for this inconvenience with a macro and the `MD5()` hashing function. - -I said: *The macro should take an "arbitrary string" for a dataset -name, convert it, with help of `MD5()`, to a hash digest, and -create a dataset with an "artificial" `hex16.` formated name.* - -Starting with something like this: - -~~~~~~~~~~~~~~~~~~~~~~~sas -data %LDSN(work. peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s (drop = sex rename=(name=first_name) where = (age in (12,13,14))) ); - set sashelp.class; -run; -~~~~~~~~~~~~~~~~~~~~~~~ - -the macro would do: - -~~~~~~~~~~~~~~~~~~~~~~~sas -%sysfunc(MD5(peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s), hex16.) -~~~~~~~~~~~~~~~~~~~~~~~ - -and (under the hood) return and execute the following code: - -~~~~~~~~~~~~~~~~~~~~~~~sas -data work.DSN_41D599EF51FBA58_(drop = sex rename=(name=first_name) where = (age in (12,13,14))) ; - set sashelp.class; -run; -~~~~~~~~~~~~~~~~~~~~~~~ - -Also in the next data step user should be able to do: - -~~~~~~~~~~~~~~~~~~~~~~~sas -data my_next_data_step; - set %DSN(work. peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s); -run; -~~~~~~~~~~~~~~~~~~~~~~~ - -and work without the "dataset-name-length-limitation" issue. - ---- - -See examples below for the details. - -The `%LDSN()` macro executes like a pure macro code. - -**Known "Limitations":** - -- dataset name _cannot_ contain dots (`.`) since they are used as separators! - -- dataset name _cannot_ contain round brackets(`(` and `)`) since they are used as separators - (but `[]` and `{}` are allowed)! - -- dataset name _cannot_ contain unpaired quotes (`'` and `"`), - text: `a "hot-dog"` is ok, but `John's dog` is not! - -**Behaviour:** - -- dataset name text is *converted to upcase* - -- dataset name text *leading and trailing spaces are ignored*, - e.g. the following will give the same hash digest: - `%ldsn(work.test)`, `%ldsn( work.test)`, `%ldsn(work.test )`, - `%ldsn(work .test)`, `%ldsn(work. test)`, `%ldsn(work . test)`. - -- macro calls of the form: - `data %LDSN(); run;`, `data %LDSN( ); run;`, `data %LDSN( . ); run;` or even - `data %LDSN( . (keep=x)); run;` are resolved to empty string, so the result is - equivalent to `data; run;` - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%LDSN( - arbitrary text string (in line with limitations) -) -~~~~~~~~~~~~~~~~~~~~~~~ - -The text string is concider as *"fully qualified dataset name"*, i.e. macro -assumes it may contain library as prefix and data set options as sufix. -See the `%LDsNm()` macro for comparison. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -options nomprint source nomlogic nosymbolgen ls = max ps = max; - -data %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s (drop = sex rename=(name=first_name) where = (age in (12,13,14))) ); - set sashelp.class; -run; - -proc print data = %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ); -run; - -data MyNextDataset; - set %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ); - where age > 12; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%LDSNM()` macro: <<< ####################### - -The LDSNM (Long DataSet Names Modified) macro function -allows to use an "arbitrary" text string to name a dataset. - -The LDSN macro had some limitation (see its documentation), to overcome them -another `%LDSNM()` (Long DataSet Names Modified) macro was created. - -The main idea behind the `%LDSNM()` is the same as for `%LDSN()` - see the description there. - ---- - -The `%LDSNM()` macro works differently than the `%LDSN()` macro. - -The `%LDSN()` macro assumes that *both* libname and dataset options *are* -be passed as elements **inside** the macro argument, together with the data set name. E.g. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data %LDSN( WORK.peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s (drop = sex) ); - set sashelp.class; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The `%LDSNM()` macro, in contrary, assumes that both libname and dataset options are -passed **outside** the macro parameter, i.e. - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data WORK.%LDSNM( peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s ) (drop = sex); - set sashelp.class; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This approach overcomes some limitations the LDSN has. - -The **additional** feature of the `%LDSNM()` is that when the macro is called, -a global macrovariable is created. -The macro variable name is the text of the hashed data set name. -The macro variable value is the text of the unhashed data set name (i.e. the argument of the macro). -For example the following macro call: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data %LDSNM(John "x" 'y' dog); - set sashelp.class; - where name = 'John'; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -creates macro variable with name `DSN_BF1F8C4D6495B34A_` and with value: `JOHN "X" 'Y' DOG`. - -The macrovariable is useful when combined with `symget()` function and -the `indsname=` option to get the original text string value back, -like in this example: - -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data test; - set %LDSNM(John "x" 'y' dog) indsname = i; - indsname = symget(scan(i,-1,".")); -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -See examples below for the details. - ---- - -The `%LDSN()` macro executes like a pure macro code. - -**Known "Limitations":** - -- dataset name _cannot_ contain _unpaired_ round brackets(`(` and `)`) - (but unmatched `[]` and `{}` are allowed)! - -- dataset name _cannot_ contain _unpaired_ quotes (`'` and `"`), - text: `a "hot-dog"` is ok, but `John's dog` is not! - -**Behaviour:** - -- dataset name text is *converted to upcase* - -- dataset name text *leading and trailing spaces are ignored*, - e.g. the following will give the same hash digest: - `%ldsn(test)`, `%ldsn( test)`, `%ldsn(test )`. - -- macro calls of the form: - `data %LDSN(); run;` or `data %LDSN( ); run;` are resolved - to empty string, so the result is equivalent to `data; run;` - -- created macrovariable is _global_ in scope. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%LDSNM( - arbitrary text string (in line with limitations) -) -~~~~~~~~~~~~~~~~~~~~~~~ - -The text string is considered as *"only dataset name"*, i.e. the macro does not -assume it contains library as prefix or data set options as suffix. -See the `%LDSN()` macro for comparison. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data %LDSNM(John "x" 'y' & dog); - set sashelp.class; - where name = 'John'; -run; - -data %LDSNM(John "x"[ 'y' & dog); - set sashelp.class; - where name = 'John'; -run; - -data %LDSNM(John "x" 'y'} & dog); - set sashelp.class; - where name = 'John'; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data work.%LDsNm( peanut butter & jelly, a hot-dog in [a box], and s(*)t(*)a(*)r(*)s!! ) (drop = sex rename=(name=first_name) where = (age in (12,13,14))) -; - set sashelp.class; -run; - -data test; - set work.%LDsNm( peanut butter & jelly, a hot-dog in [a box], and s(*)t(*)a(*)r(*)s!! ) indsname=i; - - indsname=symget(scan(i,-1,".")); -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data work.%LDsNm( . ); - set sashelp.class; -run; - -data %LDsNm( ); - set sashelp.class; -run; - - -data %LDsNm(); - set sashelp.class; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%LVarNm()` macro: <<< ####################### - -The LVarNm() macro function works like the LDSN() macro function, but for variables. -Supported by LVarNmLab() macro function which allows to remember "user names" in labels. - -The motivation for the macro was similar to that for the LDSN() macro. - ---- - -See examples below for the details. - -The `%LVarNm()` macro executes like a pure macro code. - -**Known "Limitations":** - -- variable name _cannot_ contain unpaired quotes (`'` and `"`), - text: `a "hot-dog"` is ok, but `John's dog` is not! - -**Behaviour:** - -- variable name text is *converted to upcase* - -- variable name text *leading and trailing spaces are ignored*, - e.g. the following will give the same hash digest: - `%LVarNm(test)`, `%LVarNm( test)`, `%LVarNm(test )`. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%LVarNm( - arbitrary text string (in line with limitations) -) -~~~~~~~~~~~~~~~~~~~~~~~ - ---- - - -### EXAMPLES AND USE CASES: #################################################### - -**EXAMPLE 1.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -options ls=max; -data test; - %LVarNmLab( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) - - do %LVarNm( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) = 1 to 10; - - y = 5 + %LVarNm( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) * 17; - output; - end; -run; - -data test2; - set test; - where %LVarNm( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) < 5; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data test3; - %LVarNmLab() = 17; - - %LVarNm() = 17; - - %LVarNm( ) = 42; - - %LVarNm( ) = 303; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data test3; - %LVarNm(test) = 1; - - %LVarNm( test) = 2; - - %LVarNm(test ) = 3; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 4.** -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data test4; - array X[*] %LVarNm(some strange! name)_0 - %LVarNm(some strange! name)_10; - - do i = lbound(X) to hbound(X); - X[i] = 2**(i-1); - put X[i]=; - end; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## >>> `%LVarNmLab()` macro: <<< ####################### - -The LVarNmLab() macro function supports LVarNm() and allows to remember "user names" in labels. - -The motivation for the macro was similar one as for the LDSN() macro. - ---- - -See examples in LVarNm() documentation for the details. - -The `%LVarNmLab()` macro executes like a pure macro code. - -**Known "Limitations":** - -- variable name _cannot_ contain unpaired quotes (`'` and `"`), - text: `a "hot-dog"` is ok, but `John's dog` is not! - -**Behaviour:** - -- variable name text is *converted to upcase* - -- variable name text *leading and trailing spaces are ignored*, - e.g. the following will give the same hash digest: - `%LVarNmLab(test)`, `%LVarNmLab( test)`, `%LVarNmLab(test )`. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%LVarNmLab( - arbitrary text string (in line with limitations) -) -~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%bpPIPE()` macro: <<< ####################### - -The bpPIPE() [Base Plus PIPE] macro executes OS command -and print to the log output of the execution. - -Under the hood it uses `_` filename reference to PIPE device. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%bpPIPE( ) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -* **NO Arguments** - Everything inside brackets is treated as an OS command. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** List, to the log, content of D and C drives: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %bpPIPE(D: & dir & dir "C:\") -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** List, to the log, content of `home` directory: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %bpPIPE(ls -halt ~/) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%dirsAndFiles()` macro: <<< ####################### - -The `%dirsAndFiles()` macro allows to extract info about all files -and subdirectories of a given `root` directory. - -The extracted info may be just a list of files and subdirectories or, if -the `details=` parameter is set to 1, additional operating system information -is extracted (information is OSS dependent and gives different results for Linux -and for Windows) - -The extracted info can be narrowed down to files (`keepFiles=1`) or to -directories (`keepDirs=1`) if need be. - -The extracted info can be presented in wide or long format (`longFormat=1`). - -The extracted info for files can be narrowed down to only files with particular -extension, for example: `fileExt=sas7bdat`. - -The extracted info can be narrowed down maximal path depth -by setting up the `maxDepth=` parameter. - -See examples below for the details. - -### REFERENCES: ################################################################### - -The macro is based on Kurt Bremser's "*Talking to Your Host*" article -presented at WUSS 2022 conference. - -The article is available [here](https://communities.sas.com/t5/SAS-User-Groups-Library/WUSS-Presentation-Talking-to-Your-Host/ta-p/838344) -and also as an additional content of this package. -The paper was awarded the "Best Paper Award - Programming". - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles( - root - <,ODS=> - <,details=> - <,keepDirs=> - <,keepFiles=> - <,longFormat=> - <,fileExt=> - <,maxDepth=> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `root` - *Required*, path to be searched - for information. - -* `ODS=work.dirsAndFilesInfo` - *Optional*, output data set, - name of a dataset to store information. - -* `details=0` - *Optional*, indicates if detailed info - will be collected, `1` = yes, `0` = no. - -* `keepDirs=1` - *Optional*, indicates if directories info - will be collected, `1` = yes, `0` = no. - -* `keepFiles=1` - *Optional*, indicates if files info - will be collected, `1` = yes, `0` = no. - -* `longFormat=0` - *Optional*, indicates if output be - in long format, `1` = yes, `0` = no. - -* `fileExt=` - *Optional*, if not missing then indicates - file extension to filter out results. - -* `maxDepth=0` - *Optional*, if not zero then indicates - maximum depth of search in the root path. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Get list of files and directories: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles(C:\SAS_WORK\,ODS=work.result1) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** Get detailed info: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles(C:\SAS_WORK\,ODS=work.result2,details=1) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Get only files info: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles(C:\SAS_WORK\,ODS=work.result3,keepDirs=0) - -%dirsAndFiles(C:\SAS_WORK\,ODS=work.result5,keepDirs=0,details=1) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 4.** Get only directories info: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles(C:\SAS_WORK\,ODS=work.result4,keepFiles=0) - -%dirsAndFiles(C:\SAS_WORK\,ODS=work.result6,keepFiles=0,details=1) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 5.** Filter out by `sas` extension: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles(~/,ODS=work.result7,fileExt=sas) - -%dirsAndFiles(~/,ODS=work.result8,fileExt=sas,details=1) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 6.** Keep result in the long format: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles(~/,ODS=work.result9,details=1,longFormat=1) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 7.** Get info for maximum depth of 2: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles(C:\SAS_WORK\,ODS=work.result10,details=1,maxDepth=2) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 8.** How locked/unavailable files are handled: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles(%sysfunc(pathname(WORK)),ODS=work.result11,details=1) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 9.** Not existing directory: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%dirsAndFiles(%sysfunc(pathname(WORK))/noSuchDir,ODS=work.result12,details=1) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%repeatTxt()` macro: <<< ####################### - -The repeatTxt() macro function allows to repeat `n` -times a `text` string separated by string `s=`. - -The repeatTxt() returns unquoted value [by %unquote()]. - -See examples below for the details. - -The `%repeatTxt()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%repeatTxt( - text - <,n> - <,s=> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `text` - *Required*, a text to be repeated. - -2. `n` - *Required/Optional*, the number of repetitions. - If missing then set to `1`; - -* `s = %str( )` - *Optional*, it is a separator between - repeated elements. Default value is space. ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Simple repetition of dataset name: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -options mprint; -data work.test5; - set - %repeatTxt(sashelp.cars, 5) - ; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** Simple repetition of data step: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -options mprint; -%repeatTxt(data _null_; set sashelp.cars; run;, 3) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** "Nice" output: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put %repeatTxt(#,15,s=$) HELLO SAS! %repeatTxt(#,15,s=$); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 4.** Macroquote a text with commas: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%repeatTxt( - %str(proc sql; create table wh as select weight,height from sashelp.class; quit;) - ,3 -) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**EXAMPLE 5.** Empty `n` repeats `text` one time: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -options mprint; -data work.test1; - set - %repeatTxt(sashelp.cars) - ; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**EXAMPLE 6.** Dynamic "formatting": -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%macro printWork(); - %let work=%sysfunc(pathname(work)); - %put +%repeatTxt(~,%length(&work.)+5,s=)+; - %put {&=work.}; - %put +%repeatTxt(~,%length(&work.)+5,s=)+; -%mend printWork; - -%printWork() -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%repList()` macro: <<< ####################### - -The repList() macro function allows to repeat `T` -times elements of a `L` list, possibly `E` times each element, -separated by string `S`. - -See examples below for the details. - -The `%repList()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%repList( - list - <,times=> - <,each=> - <,lenghtOut=> - <,sep=> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `list` - *Required*, a list of elements to be repeated. - List can be space or comma separated. - Elements can be in quotes. - For comma separated list add brackets - e.g., `%repList((A,B,C,D),times=5)`. - The list separators are: `<{[( ,;)]}>`. - -* `times=` - *Optional*, An integer indicating - the number of repetitions. - By default set to `1`. - - -* `each=` - *Optional*, A list of integers indicating - the number of repetitions of each element of the list - e.g., for a list `A B C` and the `each=2 4` the result - is `A A B B B B C C`. If the number of integers is less - then the length of the list values are recycled from - the beginning. - By default set to `1`. - -* `lenghtOut=` - *Optional*, An integer indicating - after what the number of repetitions process will stop. - By default set to `0` which means "do not stop". - -* `sep=` - *Optional*, it is a separator printed between - repeated elements. Mnemonics for *space* is `s`, - for *comma* is `c`, and for semicolon in `q`. - Default value is a single space. - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Simple repetition of all elements: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put %repList((A,B,C,D), times=3); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** Simple repetition of each element: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put %repList(("A",'B',"C",'D'), each=3); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Simple repetition with a separator: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put %repList(A10;B20;C30, times=3, each=2, sep=Q); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 4.** Recycle elements up to 8 with a comma as a separator: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put %repList(1 2 3, lenghtOut=8, sep=c); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**EXAMPLE 5.** Separate number of repetitions for each element: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put [%repList([D][C][B][A], each = 2 3 5 7, sep=] [)]; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**EXAMPLE 6.** "ASCII art" butterflies: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put {>%repList(! $ |, times = 2, each =2 1, sep=<} ... {>)<}; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 7.** Data repeating: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -data A; - x=17; -data B; - x=42; -data C; - x=303; -run; - -data Times2_A10B11C12; - set - %repList(A B C, times = 2, each =10 11 12) - ; -run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - ---- - - -## >>> `%intsList()` macro: <<< ####################### - -The intsList() macro function allows to print a list of -integers starting from `start` up to `end` incremented by `by` -and separated by `sep=`. - -If `start`, `end` or `by` are non-integers the are converted to integers. - -See examples below for the details. - -The `%intsList()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%intsList( - start - <,end> - <,by> - <,sep=> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `start` - *Required*, the first value of the list. - If `end` is missing then the list is generated - from 1 to `start` by 1. - -2. `end` - *Required/Optional*, the last value of the list. - -3. `by` - *Required/Optional*, the increment of the list. - If missing then set to `1`. - *Cannot* be equal to `0`. - -* `s = %str( )` - *Optional*, it is a separator between - elements of the list. Default value is space. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Simple list of integers from 1 to 10 by 1: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %intsList(10); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** Ten copies of `sashelp.class` in `test11` to `test20`: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - data - %zipEvalf(test, %intsList(11,20)) - ; - set sashelp.class; - run; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Non-integers are converted to integers, the list is `1 3 5`: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %intsList(1.1,5.2,2.3); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 4.** A list with a separator: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %intsList(1,5,2,sep=+); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%letters()` macro: <<< ####################### - -The letters() macro function allows to print a list of Roman -letters starting from `start` up to `end` incremented by `by`. -The letters list can be uppercases or lowercase (parameter `c=U` or `c=L`), -can be quoted (e.g. `q=""` or `q=[]`), and can be separated by `s=`. - -Values of `start`, `end`, and `by` have to be integers in range between 1 ad 26. - -See examples below for the details. - -The `%letters()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%letters( - range - <,c=> - <,q=> - <,s=> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `range` - *Required*, letters selector in form `start:end:by`. - Lists letters from `start` to `end` by `by`. - Values of `start`, `end`, and `by` are separated by - colon and must be between 1 ad 26. - If value is outside range it is set to - `start=1`, `en=26`, and `by=1`. If `end` is missing - then is set to value of `start`. - If `end` is smaller than `start` list is reversed - -* `c = U` - *Optional*, it is a lowercase letters indicator. - Select `L` or `l`. Default value is `U` for upcase. - -* `q = ` - *Optional*, it is a quite around elements of the list. - Default value is empty. Use `%str()` for one quote symbol. - If there are multiple symbols, only the first and the - second are selected as a preceding and trailing one, - e.g. `q=[]` gives `[A] [B] ... [Z]`. - -* `s = %str( )` - *Optional*, it is a separator between - elements of the list. Default value is space. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Space separated list of capital letters from A to Z: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %letters(1:26:1); - - %put %letters(); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** First, thirteenth, and last letter: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %letters(1) %letters(13) %letters(26); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Every third lowercase letter, i.e. `a d g j m p s v y`: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %letters(1:26:3,c=L); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 4.** Lists with separators: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %letters(1:26:2,s=#); - %put %letters(1:26:3,s=%str(;)); - %put %letters(1:26:4,s=%str(,)); - %put %letters(1:26,s=); - %put %letters(1:26,s==); - %put %letters(1:26,s=/); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 5.** Every second letter with quotes: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %letters(1:26:2,q=%str(%')); - %put %letters(2:26:2,q=%str(%")); - - %put %letters(1:26:2,q=''); - %put %letters(2:26:2,q=""); - - %put %letters(1:26:2,q=<>); - %put %letters(2:26:2,q=\/); - - %put %letters(1:26:2,q=()); - %put %letters(2:26:2,q=][); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 6.** Mix of examples 4, 5, and 6: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %letters(1:26,c=L,q='',s=%str(, )); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 7.** If `end` is smaller than `start` list is reversed: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %letters(26:1:2,q=''); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%splitDSIntoBlocks()` macro: <<< ####################### - -The splitDSIntoBlocks() macro allows to split the `set` dataset into blocks -of size `blockSize` in datasets: `prefix1` to `prefixN`. - -The last dataset may have less observations then the `blockSize`. - -Macro covers `BASE` engine (`v9`, `v8`, `v7`, `v6`) and `SPDE` engine datasets. - -See examples below for the details. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%splitDSIntoBlocks( - blockSize - <,set> - <,prefix> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `blockSize` - *Required*, the size of the block of data, - in other words number of observations in - one block of split data. - Block size must be positive integer. - -2. `set` - *Required/Optional*, the name of the dataset to split. - If empty then `&syslast.` is used. - -3. `prefix` - *Required/Optional*, the name-prefix for new datasets. - If missing then set to `part`. - --- -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Split `sashelp.class` into 5 elements datasets ABC1 to ABC4: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %splitDSIntoBlocks(5,sashelp.class,ABC) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** By default splits the `_last_` dataset into `part1` to `partN` datasets: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - data lastData; - set sashelp.cars; - run; - - %splitDSIntoBlocks(123) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Works with `SPDE` engine too: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - options dlcreatedir; - libname test "%sysfunc(pathname(work))/testSPDE"; - libname test; - libname test SPDE "%sysfunc(pathname(work))/testSPDE"; - - data test.test; - set sashelp.cars; - run; - - %splitDSIntoBlocks(100,test.test,work.spde) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%splitDSIntoParts()` macro: <<< ####################### - -The splitDSIntoParts() macro allows to split the `set` dataset into `parts` parts -of approximately `NOBS/parts` size in datasets: `prefix1` to `prefixN`. - -The splitDSIntoParts() macro internally runs the splitDSIntoBlocks() macro. - -Macro covers `BASE` engine (`v9`, `v8`, `v7`, `v6`) and `SPDE` engine datasets. - -See examples below for the details. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%splitDSIntoParts( - parts - <,set> - <,prefix> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `parts` - *Required*, the number of parts to split data into. - Number of parts must be positive integer. - -2. `set` - *Required/Optional*, the name of the dataset to split. - If empty then `&syslast.` is used. - -3. `prefix` - *Required/Optional*, the name-prefix for new datasets. - If missing then set to `part`. - ---- +## `%filepath()` macro ###### -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Split `sashelp.cars` into 7 parts: datasets carsInParts1 to carsInParts7: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %splitDSIntoParts(7,sashelp.cars, carsInParts) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** By default splits the `_last_` dataset into `part1` to `part3` datasets: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - data lastData; - set sashelp.cars; - run; - - %splitDSIntoBlocks(3) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Works with `SPDE` engine too: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - options dlcreatedir; - libname test "%sysfunc(pathname(work))/testSPDE"; - libname test; - libname test SPDE "%sysfunc(pathname(work))/testSPDE"; - - data test.test; - set sashelp.cars; - run; - - %splitDSIntoParts(3,test.test,work.spde) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - ## >>> `%filePath()` macro: <<< ####################### The filePath() macro function returns path to a file, @@ -5322,6 +5466,7 @@ The basic syntax is the following, the `<...>` means optional parameters: --- + ### EXAMPLES AND USECASES: #################################################### **EXAMPLE 1.** Return path to temporary file: @@ -5332,465 +5477,11 @@ The basic syntax is the following, the `<...>` means optional parameters: --- -## >>> `%libPath()` macro: <<< ####################### - -The libPath() macro function returns path to a library, -it is a wrapper to `pathname()` function for libraries. - -See examples below for the details. - -The `%libPath()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%libPath( - libref -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `libref` - *Required*, a libref from the `libname` statement. - + --- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Return path to `WORK` library: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %libPath(WORK); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** Return path to `SASHELP` library: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %libPath(SASHELP); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%workPath()` macro: <<< ####################### - -The workPath() macro function returns path to the `WORK` library, -it is a wrapper to `pathname("work", "L")` function. - -See examples below for the details. - -The `%workPath()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%workPath() -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -*) No arguments. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Create new library inside `WORK` library: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - options dlCreateDir; - libname NEW "%workPath()/new"; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%date()` macro: <<< ####################### - -The date() macro function is a "lazy typer" wrapping up `%sysfunc(date())`. - -See examples below for the details. - -The `%date()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%date() -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - - No arguments. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Get value of `date()`: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %date(); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - +## `%finddswithvarval()` macro ###### -## >>> `%today()` macro: <<< ####################### - -The today() macro function is a "lazy typer" wrapping up `%sysfunc(today())`. - -See examples below for the details. - -The `%today()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%today() -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - - No arguments. - ---- - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Get value of `today()`: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %today(); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - - -## >>> `%time()` macro: <<< ####################### - -The time() macro function is a "lazy typer" wrapping up `%sysfunc(time())`. - -See examples below for the details. - -The `%time()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%time() -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - - No arguments. - ---- - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Get value of `time()`: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %time(); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - - -## >>> `%datetime()` macro: <<< ####################### - -The datetime() macro function is a "lazy typer" wrapping up `%sysfunc(datetime())`. - -See examples below for the details. - -The `%datetime()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%datetime() -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - - No arguments. - ---- - - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Get value of `datetime()`: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %datetime(); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%monthShift()` macro: <<< ####################### - -The monthShift() macro is a utility macro -which allows to shift "year-month" period by -a given number of "periods" (months). - -The result is in the `YYYYMM` format but can be altered. - -See examples below for the details. - -The `%monthShift()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%monthShift( - < Y> - <,M> - <,shift> - <,ofmt=> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `Y` - *Optional*, a year from which counting starts. - If null the value is set to *system year*. - -2. `M` - *Optional*, a month from which counting starts. - If null the value is set to `1`. Can be a number - (`1` to `12`) or a name (`June`, `OCTOBER`) or - a three letters short (`JAN`, `apr`). - -3. `shift` - *Optional*, number of periods to shift. - If null the value is set to `0`. - Positive value shifts to the "future", - negative value shifts to the "past", - Can be an expression (e.g. `1+2*3`, see examples). - -* `ofmt=YYMMn6.` - *Optional*, it is a format name used to - display the result. Default value is `YYMMn6.` - See examples. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Shift one up and one down: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas -%put - Past: %monthShift(2023, 1, -1) - Current: %monthShift(2023, 1 ) - Future: %monthShift(2023, 1, +1) -; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** Shift by expression: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %let n = 2; - %put - %monthShift(2023, 1, +1 + &n.*3) - ; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Shift with default values: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %monthShift(); - %put %monthShift(2023); - %put %monthShift(2023,Jan); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 4.** Shift with months names: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put - %monthShift(2023,Jan,-1) - %monthShift(2023,Feb,-2) - %monthShift(2023,Mar,-3) - %monthShift(2023,Apr,-4) - %monthShift(2023,May,-5) - %monthShift(2023,Jun,-6) - %monthShift(2023,Jul,-7) - %monthShift(2023,Aug,-8) - %monthShift(2023,Sep,-9) - %monthShift(2023,Oct,-10) - %monthShift(2023,Nov,-11) - %monthShift(2023,Dec,-12) - ; - - %put - %monthShift(2023,January,12) - %monthShift(2023,February,11) - %monthShift(2023,March,10) - %monthShift(2023,April,9) - %monthShift(2023,May,8) - %monthShift(2023,June,7) - %monthShift(2023,July,6) - %monthShift(2023,August,5) - %monthShift(2023,September,4) - %monthShift(2023,October,3) - %monthShift(2023,November,2) - %monthShift(2023,December,1) - ; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 5.** Play with formatting: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put - %monthShift(2023, 1, +1 ) - %monthShift(2023, 1, +1, ofmt=yymm7. ) - %monthShift(2023, 1, +1, ofmt=yymmd7.) - %monthShift(2023, 1, +1, ofmt=yymms7.) - ; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 6.** Read monthly data with `noDSNFERR` option: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - data - A202210 A202211 A202212 - A202301 A202302 A202303 - A202304 A202305 A202306 - A202307 A202308 A202309 - ; - set sashelp.class; - run; - - - options noDSNFERR; - data ALL; - set - A%monthShift(2023, 9, -12) - A%monthShift(2023, 9) - ; - run; - options DSNFERR; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - - -## >>> `%translate()` macro: <<< ####################### - -The translate() macro function allows to replace bytes with bytes in text string. - -See examples below for the details. - -The `%translate()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%translate( - string - ,from - ,to -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `string` - *Required*, string to modify. - -2. `from` - *Required*, list of bytes to be replaced with - corresponding bytes from `to`. - -3. `to` - *Required*, list of bytes replacing - corresponding bytes from `from`. - ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Replace quotes and commas with apostrophes and spaces: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %translate(%str("A", "B", "C"),%str(%",),%str(%' )); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** Unify all brackets; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %translate(%str([A] {B} (C) ),{[(<>)]},(((())))); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Replace all digits with `*`: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %translate(QAZ1WSSX2EDC3RFV4TGB5YHN6UJM7IK8OL9P0,1234567890,**********); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 4.** Letters change: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %translate(%str(A=B),AB,BA); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ---- - -## >>> `%tranwrd()` macro: <<< ####################### - -The tranwrd() macro function allows to replace substrings -with other substrings in text string. - -Returned string is unquoted by `%unquote()`. - -See examples below for the details. - -The `%tranwrd()` macro executes like a pure macro code. - -### SYNTAX: ################################################################### - -The basic syntax is the following, the `<...>` means optional parameters: -~~~~~~~~~~~~~~~~~~~~~~~sas -%tranwrd( - string - ,from - ,to - <,repeat> -) -~~~~~~~~~~~~~~~~~~~~~~~ - -**Arguments description**: - -1. `string` - *Required*, string to modify. - -2. `from` - *Required*, substring replaced with - corresponding string from `to`. - -3. `to` - *Required*, substring replacing - corresponding substring from `from`. - -4. `repeat` - *Optional*, number of times the replacing - should be repeated, default is 1. - Useful while removing multiple adjacent - characters, e.g. compress all multiple - spaces (see example 2). ---- - -### EXAMPLES AND USECASES: #################################################### - -**EXAMPLE 1.** Simple text replacement: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %tranwrd(Miss Joan Smith,Miss,Ms.); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 2.** Delete multiple spaces; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %tranwrd(%str(A B C),%str( ),%str( ),5); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - -**EXAMPLE 3.** Remove substring: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %tranwrd(ABCxyzABCABCxyzABC,ABC); -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ## >>> `%findDSwithVarVal()` macro: <<< ####################### The findDSwithVarVal() macro searches for all @@ -5869,6 +5560,7 @@ The basic syntax is the following, the `<...>` means optional parameters: --- + ### EXAMPLES AND USECASES: #################################################### **EXAMPLE 1.** Search variable `NAME` containing value `John`: @@ -5913,6 +5605,80 @@ The basic syntax is the following, the `<...>` means optional parameters: --- + +--- + +## `%fmt()` macro ###### + +## >>> `%fmt()` macro: <<< ####################### + +The fmt() macro function returns a `value` formatted by a `format`, +it is a wrapper to `putN()` and `putC()` functions. + +See examples below for the details. + +The `%fmt()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%fmt( + value + ,format + ,align + <,type=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `value` - *Required*, a value to be formatted. + +2. `format` - *Required*, a name of a format to be used, + character format should be preceded by the `$`. + +3. `align` - *Optional*, allows to use the `-L`, `-R` and `-C` modifiers. + +* `type=n` - *Optional*, defines type of the format. If the format + name is preceded by the `$` then C is set automatically. + If the character format name is without `$` then set + value to `C` yourself. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Formatting values: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %fmt(111, 7.2); + + %put %fmt(111, dollar10.2); + + %put %fmt(abc, $upcase.); + + %put %fmt(12345, date9.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Align values (compare different results!): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%fmt(ABC, $char9., -L)*; + %put *%fmt(ABC, $char9., -R)*; + %put *%fmt(ABC, $char9., -C)*; + + %put %fmt(ABC, $char9., -L); + %put %fmt(ABC, $char9., -R); + %put %fmt(ABC, $char9., -C); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%gettitle()` macro ###### + ## >>> `%getTitle()` macro: <<< ####################### The getTitle() macro extract text of titles or footnotes @@ -5962,6 +5728,7 @@ The basic syntax is the following, the `<...>` means optional parameters: --- + ### EXAMPLES AND USECASES: #################################################### **EXAMPLE 1.** Get titles in different forms: @@ -5993,6 +5760,231 @@ The basic syntax is the following, the `<...>` means optional parameters: --- + +--- + +## `%infmt()` macro ###### + +## >>> `%infmt()` macro: <<< ####################### + +The infmt() macro function returns a `value` read in by an `informat`, +it is a wrapper to `inputN()` and `inputC()` functions. + +See examples below for the details. + +The `%infmt()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%infmt( + value + ,informat + <,type=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `value` - *Required*, a value to be formatted. + +2. `informat` - *Required*, a name of a format to be used, + character format should be preceded by the `$`. + +* `type=n` - *Optional*, defines type of the informat. If the informat + name is preceded by the `$` then C is set automatically. + If the character format name is without `$` then set + value to `C` yourself. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Informatting values: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %infmt(111, 7.2); + %put %infmt(111.234, 7.2); + + %put %infmt($111, dollar10.2); + %put %infmt($111.234, dollar10.2); + + %put %infmt(abc, $upcase.); + + %put %infmt(12mar45, date9.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%letters()` macro ###### + +## >>> `%letters()` macro: <<< ####################### + +The letters() macro function allows to print a list of Roman +letters starting from `start` up to `end` incremented by `by`. +The letters list can be uppercases or lowercase (parameter `c=U` or `c=L`), +can be quoted (e.g. `q=""` or `q=[]`), and can be separated by `s=`. + +Values of `start`, `end`, and `by` have to be integers in range between 1 ad 26. + +See examples below for the details. + +The `%letters()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%letters( + range + <,c=> + <,q=> + <,s=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `range` - *Required*, letters selector in form `start:end:by`. + Lists letters from `start` to `end` by `by`. + Values of `start`, `end`, and `by` are separated by + colon and must be between 1 ad 26. + If value is outside range it is set to + `start=1`, `en=26`, and `by=1`. If `end` is missing + then is set to value of `start`. + If `end` is smaller than `start` list is reversed + +* `c = U` - *Optional*, it is a lowercase letters indicator. + Select `L` or `l`. Default value is `U` for upcase. + +* `q = ` - *Optional*, it is a quite around elements of the list. + Default value is empty. Use `%str()` for one quote symbol. + If there are multiple symbols, only the first and the + second are selected as a preceding and trailing one, + e.g. `q=[]` gives `[A] [B] ... [Z]`. + +* `s = %str( )` - *Optional*, it is a separator between + elements of the list. Default value is space. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Space separated list of capital letters from A to Z: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26:1); + + %put %letters(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** First, thirteenth, and last letter: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1) %letters(13) %letters(26); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Every third lowercase letter, i.e. `a d g j m p s v y`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26:3,c=L); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Lists with separators: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26:2,s=#); + %put %letters(1:26:3,s=%str(;)); + %put %letters(1:26:4,s=%str(,)); + %put %letters(1:26,s=); + %put %letters(1:26,s==); + %put %letters(1:26,s=/); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Every second letter with quotes: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26:2,q=%str(%')); + %put %letters(2:26:2,q=%str(%")); + + %put %letters(1:26:2,q=''); + %put %letters(2:26:2,q=""); + + %put %letters(1:26:2,q=<>); + %put %letters(2:26:2,q=\/); + + %put %letters(1:26:2,q=()); + %put %letters(2:26:2,q=][); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** Mix of examples 4, 5, and 6: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26,c=L,q='',s=%str(, )); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** If `end` is smaller than `start` list is reversed: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(26:1:2,q=''); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%libpath()` macro ###### + +## >>> `%libPath()` macro: <<< ####################### + +The libPath() macro function returns path to a library, +it is a wrapper to `pathname()` function for libraries. + +See examples below for the details. + +The `%libPath()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%libPath( + libref +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `libref` - *Required*, a libref from the `libname` statement. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Return path to `WORK` library: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %libPath(WORK); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Return path to `SASHELP` library: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %libPath(SASHELP); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%minclude()` macro ###### + ## >>> `%mInclude()` macro: <<< ####################### The mInclude() macro is a macrolanguage version of the SAS `%include` statement. @@ -6042,6 +6034,7 @@ The basic syntax is the following, the `<...>` means optional parameters: --- + ### EXAMPLES AND USECASES: #################################################### **EXAMPLE 1.** Embedding text in statements (the `%include` won't work here): @@ -6200,128 +6193,535 @@ quit; --- -## >>> `%fmt()` macro: <<< ####################### + +--- + +## `%monthshift()` macro ###### + +## >>> `%monthShift()` macro: <<< ####################### -The fmt() macro function returns a `value` formatted by a `format`, -it is a wrapper to `putN()` and `putC()` functions. +The monthShift() macro is a utility macro +which allows to shift "year-month" period by +a given number of "periods" (months). + +The result is in the `YYYYMM` format but can be altered. See examples below for the details. -The `%fmt()` macro executes like a pure macro code. +The `%monthShift()` macro executes like a pure macro code. ### SYNTAX: ################################################################### The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -%fmt( - value - ,format - ,align - <,type=> +%monthShift( + < Y> + <,M> + <,shift> + <,ofmt=> ) ~~~~~~~~~~~~~~~~~~~~~~~ **Arguments description**: -1. `value` - *Required*, a value to be formatted. +1. `Y` - *Optional*, a year from which counting starts. + If null the value is set to *system year*. -2. `format` - *Required*, a name of a format to be used, - character format should be preceded by the `$`. +2. `M` - *Optional*, a month from which counting starts. + If null the value is set to `1`. Can be a number + (`1` to `12`) or a name (`June`, `OCTOBER`) or + a three letters short (`JAN`, `apr`). -3. `align` - *Optional*, allows to use the `-L`, `-R` and `-C` modifiers. +3. `shift` - *Optional*, number of periods to shift. + If null the value is set to `0`. + Positive value shifts to the "future", + negative value shifts to the "past", + Can be an expression (e.g. `1+2*3`, see examples). -* `type=n` - *Optional*, defines type of the format. If the format - name is preceded by the `$` then C is set automatically. - If the character format name is without `$` then set - value to `C` yourself. +* `ofmt=YYMMn6.` - *Optional*, it is a format name used to + display the result. Default value is `YYMMn6.` + See examples. --- + ### EXAMPLES AND USECASES: #################################################### -**EXAMPLE 1.** Formatting values: +**EXAMPLE 1.** Shift one up and one down: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %fmt(111, 7.2); - - %put %fmt(111, dollar10.2); - - %put %fmt(abc, $upcase.); - - %put %fmt(12345, date9.); +%put + Past: %monthShift(2023, 1, -1) + Current: %monthShift(2023, 1 ) + Future: %monthShift(2023, 1, +1) +; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**EXAMPLE 2.** Align values (compare different results!): -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put *%fmt(ABC, $char9., -L)*; - %put *%fmt(ABC, $char9., -R)*; - %put *%fmt(ABC, $char9., -C)*; - %put %fmt(ABC, $char9., -L); - %put %fmt(ABC, $char9., -R); - %put %fmt(ABC, $char9., -C); +**EXAMPLE 2.** Shift by expression: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let n = 2; + %put + %monthShift(2023, 1, +1 + &n.*3) + ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Shift with default values: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %monthShift(); + %put %monthShift(2023); + %put %monthShift(2023,Jan); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Shift with months names: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put + %monthShift(2023,Jan,-1) + %monthShift(2023,Feb,-2) + %monthShift(2023,Mar,-3) + %monthShift(2023,Apr,-4) + %monthShift(2023,May,-5) + %monthShift(2023,Jun,-6) + %monthShift(2023,Jul,-7) + %monthShift(2023,Aug,-8) + %monthShift(2023,Sep,-9) + %monthShift(2023,Oct,-10) + %monthShift(2023,Nov,-11) + %monthShift(2023,Dec,-12) + ; + + %put + %monthShift(2023,January,12) + %monthShift(2023,February,11) + %monthShift(2023,March,10) + %monthShift(2023,April,9) + %monthShift(2023,May,8) + %monthShift(2023,June,7) + %monthShift(2023,July,6) + %monthShift(2023,August,5) + %monthShift(2023,September,4) + %monthShift(2023,October,3) + %monthShift(2023,November,2) + %monthShift(2023,December,1) + ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Play with formatting: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put + %monthShift(2023, 1, +1 ) + %monthShift(2023, 1, +1, ofmt=yymm7. ) + %monthShift(2023, 1, +1, ofmt=yymmd7.) + %monthShift(2023, 1, +1, ofmt=yymms7.) + ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** Read monthly data with `noDSNFERR` option: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data + A202210 A202211 A202212 + A202301 A202302 A202303 + A202304 A202305 A202306 + A202307 A202308 A202309 + ; + set sashelp.class; + run; + + + options noDSNFERR; + data ALL; + set + A%monthShift(2023, 9, -12) - A%monthShift(2023, 9) + ; + run; + options DSNFERR; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- -## >>> `%infmt()` macro: <<< ####################### + +--- + +## `%replist()` macro ###### -The infmt() macro function returns a `value` read in by an `informat`, -it is a wrapper to `inputN()` and `inputC()` functions. +## >>> `%repList()` macro: <<< ####################### + +The repList() macro function allows to repeat `T` +times elements of a `L` list, possibly `E` times each element, +separated by string `S`. See examples below for the details. -The `%infmt()` macro executes like a pure macro code. +The `%repList()` macro executes like a pure macro code. ### SYNTAX: ################################################################### The basic syntax is the following, the `<...>` means optional parameters: ~~~~~~~~~~~~~~~~~~~~~~~sas -%infmt( - value - ,informat - <,type=> +%repList( + list + <,times=> + <,each=> + <,lenghtOut=> + <,sep=> ) ~~~~~~~~~~~~~~~~~~~~~~~ **Arguments description**: -1. `value` - *Required*, a value to be formatted. +1. `list` - *Required*, a list of elements to be repeated. + List can be space or comma separated. + Elements can be in quotes. + For comma separated list add brackets + e.g., `%repList((A,B,C,D),times=5)`. + The list separators are: `<{[( ,;)]}>`. -2. `informat` - *Required*, a name of a format to be used, - character format should be preceded by the `$`. +* `times=` - *Optional*, An integer indicating + the number of repetitions. + By default set to `1`. -* `type=n` - *Optional*, defines type of the informat. If the informat - name is preceded by the `$` then C is set automatically. - If the character format name is without `$` then set - value to `C` yourself. ---- +* `each=` - *Optional*, A list of integers indicating + the number of repetitions of each element of the list + e.g., for a list `A B C` and the `each=2 4` the result + is `A A B B B B C C`. If the number of integers is less + then the length of the list values are recycled from + the beginning. + By default set to `1`. + +* `lenghtOut=` - *Optional*, An integer indicating + after what the number of repetitions process will stop. + By default set to `0` which means "do not stop". + +* `sep=` - *Optional*, it is a separator printed between + repeated elements. Mnemonics for *space* is `s`, + for *comma* is `c`, and for semicolon in `q`. + Default value is a single space. + ### EXAMPLES AND USECASES: #################################################### -**EXAMPLE 1.** Informatting values: +**EXAMPLE 1.** Simple repetition of all elements: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas - %put %infmt(111, 7.2); - %put %infmt(111.234, 7.2); +%put %repList((A,B,C,D), times=3); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - %put %infmt($111, dollar10.2); - %put %infmt($111.234, dollar10.2); - %put %infmt(abc, $upcase.); +**EXAMPLE 2.** Simple repetition of each element: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repList(("A",'B',"C",'D'), each=3); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - %put %infmt(12mar45, date9.); + +**EXAMPLE 3.** Simple repetition with a separator: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repList(A10;B20;C30, times=3, each=2, sep=Q); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Recycle elements up to 8 with a comma as a separator: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repList(1 2 3, lenghtOut=8, sep=c); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 5.** Separate number of repetitions for each element: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put [%repList([D][C][B][A], each = 2 3 5 7, sep=] [)]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 6.** "ASCII art" butterflies: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put {>%repList(! $ |, times = 2, each =2 1, sep=<} ... {>)<}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** Data repeating: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data A; + x=17; +data B; + x=42; +data C; + x=303; +run; + +data Times2_A10B11C12; + set + %repList(A B C, times = 2, each =10 11 12) + ; +run; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- + --- + +## `%time()` macro ###### + +## >>> `%time()` macro: <<< ####################### + +The time() macro function is a "lazy typer" wrapping up `%sysfunc(time())`. + +See examples below for the details. + +The `%time()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%time() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + + - `format` - *Optional*, if a value is provided + it should be a valid SAS format capable of handling + values produced by the `time()` function. --- -## License #################################################################### + +### EXAMPLES AND USECASES: #################################################### -Copyright (c) since 2020 Bartosz Jablonski +**EXAMPLE 1.** Get value of `time()`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %time(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Get value of `time()` with a format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %time(time8.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%today()` macro ###### + +## >>> `%today()` macro: <<< ####################### + +The today() macro function is a "lazy typer" wrapping up `%sysfunc(today())`. + +See examples below for the details. + +The `%today()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%today() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + + - `format` - *Optional*, if a value is provided + it should be a valid SAS format capable of handling + values produced by the `today()` function. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get value of `today()`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %today(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Get value of `today()` with a format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %today(yymmdd10.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%translate()` macro ###### + +## >>> `%translate()` macro: <<< ####################### + +The translate() macro function allows to replace bytes with bytes in text string. + +See examples below for the details. + +The `%translate()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%translate( + string + ,from + ,to +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `string` - *Required*, string to modify. + +2. `from` - *Required*, list of bytes to be replaced with + corresponding bytes from `to`. + +3. `to` - *Required*, list of bytes replacing + corresponding bytes from `from`. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Replace quotes and commas with apostrophes and spaces: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %translate(%str("A", "B", "C"),%str(%",),%str(%' )); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Unify all brackets; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %translate(%str([A] {B} (C) ),{[(<>)]},(((())))); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Replace all digits with `*`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %translate(QAZ1WSSX2EDC3RFV4TGB5YHN6UJM7IK8OL9P0,1234567890,**********); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Letters change: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %translate(%str(A=B),AB,BA); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%tranwrd()` macro ###### + +## >>> `%tranwrd()` macro: <<< ####################### + +The tranwrd() macro function allows to replace substrings +with other substrings in text string. + +Returned string is unquoted by `%unquote()`. + +See examples below for the details. + +The `%tranwrd()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%tranwrd( + string + ,from + ,to + <,repeat> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `string` - *Required*, string to modify. + +2. `from` - *Required*, substring replaced with + corresponding string from `to`. + +3. `to` - *Required*, substring replacing + corresponding substring from `from`. + +4. `repeat` - *Optional*, number of times the replacing + should be repeated, default is 1. + Useful while removing multiple adjacent + characters, e.g. compress all multiple + spaces (see example 2). +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple text replacement: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %tranwrd(Miss Joan Smith,Miss,Ms.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Delete multiple spaces; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %tranwrd(%str(A B C),%str( ),%str( ),5); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Remove substring: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %tranwrd(ABCxyzABCABCxyzABC,ABC); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%workpath()` macro ###### + +## >>> `%workPath()` macro: <<< ####################### + +The workPath() macro function returns path to the `WORK` library, +it is a wrapper to `pathname("work", "L")` function. + +See examples below for the details. + +The `%workPath()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%workPath() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +*) No arguments. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Create new library inside `WORK` library: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + options dlCreateDir; + libname NEW "%workPath()/new"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + + +--- + +# License ###### + +Copyright (c) 2020 - 2023 Bartosz Jablonski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -6340,5 +6740,6 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - + --- + diff --git a/baseplus.zip b/baseplus.zip index 2780194bb2b951a183036e11e2948c6486eaadde..a062d6d27508c072d1002296fc55f82468932a54 100644 GIT binary patch delta 22284 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 eDUuCQf1rS7-Jmr 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 J)BuQ%{0E`z0x$po diff --git a/hist/1.36.0/baseplus.md b/hist/1.36.0/baseplus.md new file mode 100644 index 0000000..d03dcdb --- /dev/null +++ b/hist/1.36.0/baseplus.md @@ -0,0 +1,6745 @@ +# Documentation for the `BasePlus` package. + +--- + +### Version information: + + *The BASE SAS plus a bunch of functionalities I am missing in BASE SAS* + +- Package: BasePlus +- Version: 1.36.0 +- Generated: 2024-01-12T10:32:46 +- Author(s): Bartosz Jablonski (yabwon@gmail.com), Quentin McMullen (qmcmullen@gmail.com) +- Maintainer(s): Bartosz Jablonski (yabwon@gmail.com) +- License: MIT +- File SHA256: `F*B9F1B3243FD3956F0B68652C21EA1EBC19F3EB0931774A57FECE1F02A9448108` for this version +- Content SHA256: `C*5A51FA3E5B3A6E9AE2AF37D6604B49B8656D4CC50AFF1F975E546D4419AA0461` for this version + +--- + +# The `BasePlus` package, version: `1.36.0`; + +--- + + +# The BasePlus package [ver. 1.36.0] ############################################### + +The **BasePlus** package implements useful +functions and functionalities I miss in the BASE SAS. + +It is inspired by various people, e.g. +- at the SAS-L discussion list +- at the communities.sas.com (SASware Ballot Ideas) +- at StackOverflow +- at the Office... +- etc. + +Kudos to all who inspired me to generate this package: +*Mark Keintz*, +*Paul Dorfman*, +*Richard DeVenezia*, +*Christian Graffeuille*, +*Allan Bowe*, +*Anamaria Calai*, +*Michal Ludwicki*, +*Quentin McMullen*, +*Kurt Bremser*, +*Leonid Batkhan*, +*Louise Hadden*. + +--- + +### BASIC EXAMPLES AND USECASES: #################################################### + +**Example 1**: One-dimensional array functions. + Array parameters to subroutine + calls must be 1-based. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data _null_; + array X[4] _temporary_ (. 1 . 2); + + call arrMissToRight(X); + do i = 1 to 4; + put X[i]= @; + end; + put; + + call arrFillMiss(17, X); + do i = 1 to 4; + put X[i]= @; + end; + put; + + call arrFill(42, X); + do i = 1 to 4; + put X[i]= @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2**: Delete dataset by name. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data toDrop; + x = 17; + run; + data _null_; + p = delDataset("toDrop"); + put p=; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 3**: Strings concatenation with format. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data test; + x = 1 ; y = . ; z = 3 ; + t = "t"; u = " "; v = "v"; + + array a[*] x y z; + array b[*] t u v; + + length s1 s2 s3 s4 $ 17; + s1 = catXFn("z5.", "#", A); + s2 = catXFi("z5.", "#", A); + s3 = catXFc("upcase.", "*", B); + s4 = catXFj("upcase.", "*", B); + + put (_all_) (=); + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 4**: Useful formats. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data _null_; + input x @@; + put @1 x= @11 x= bool. @21 x= int. @31 x= ceil. @41 x= floor.; + cards; + . ._ .A -10 -3.14 0 3.14 10 + ; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 5**: Getting variables names from datasets. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(sashelp.class + ,pattern = ght$ + ,sep = + + ,varRange = _numeric_)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 6**: Quick sort as an alternative to call sortn() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data _null_; + array test[25000000] _temporary_ ; + + t = time(); + call streaminit(123); + do _N_ = 25000000 to 1 by -1; + test[_N_] = rand("uniform"); + end; + t = time() - t; + put "Array population time: " t; + + t = time(); + call quickSortLight (test); + t = time()-t; + put "Sorting time: " / t=; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 7**: De-duplicate values from a space separated list. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4 5 6 1 2 3 1 2 3 4 5 6; + %put *%dedupListS(&list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 8**: Zip elements of two space separated list. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let x = %zipEvalf(1 2 3 4 5 6, 2018 2019 2020, argMd=5, function=MDY, format=date11.); +%put &=x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 9**: Simple Rain Cloud plot. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%rainCloudPlot(sashelp.cars,DriveTrain,Invoice) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 10**: Zip SAS library. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(sashelp, libOut=work) + +%unzipLibrary(%sysfunc(pathname(work)), zip=sashelp, mode=S, clean=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 11**: Long dataset names. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s (drop = sex rename=(name=first_name) where = (age in (12,13,14))) ); + set sashelp.class; +run; + +proc print data = %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ); +run; + +data MyNextDataset; + set %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ); + where age > 12; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 12**: List, to the log, content of `home` directory. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%bpPIPE(ls -la ~/) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 13** Get list of all files and directories from `C:\SAS_WORK\`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 14** Text repetition: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repeatTxt(#,15,s=$) HELLO SAS! %repeatTxt(#,15,s=$); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 15** Integer list: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %intsList(42); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 16** Split dataset into blocks of 5 observations: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%splitDSIntoBlocks(5, sashelp.class, classBlock) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 17** Split dataset into 7 parts: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%splitDSIntoParts(7, sashelp.cars, carsPart) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 18** Return path to temporary file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + filename f temp; + %put %filePath(f); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 19** Get titles: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + title1 j=c "Hi Roger" ; + title2 j=l "Good Morning" ; + title3 "How are you?" ; + title4 ; + title5 "Bye bye!" ; + + %put %GetTitle(1 2 3 5, dlm=s, qt='') ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 20** Format and informat macro variables values: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %fmt(12345, date9.) %fmt(12345, yymmdd10.); + + %put %infmt($111234, dollar10.2); + %put %infmt($111.234, dollar10.2); + + %let text = ##%fmt(ABC, $char9., -C)##; + %put &text.; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 21** "Macro including" a text file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + filename f "%workpath()/testFile1.txt"; + data _null_; + file f; + put "13 14 15"; + run; + + data testDataset; + set sashelp.class; + where age in ( %mInclude(f) ); + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 22** Repeating texts and lists: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options mprint; + +data work.A; + x=17; +data work.B; + x=42; +data work.C; + x=303; +run; + +data work.test5; + set + %repeatTxt(work.A work.B work.C, 5) + ; +run; + + +data Times2_A3B4C5; + set + %repList(work.A work.B work.C, times = 2, each = 3 4 5) + ; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 23** Date and time one-liners: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put #%today()#%date()#%time()#%datetime()#; + +%put @%today(yymmdd10.)@%date(date11.)@%time(time8.)@%datetime(e8601dt.)@; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 24** Months shifting: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put + Past: %monthShift(2023, 1, -1) + Current: %monthShift(2023, 1 ) + Future: %monthShift(2023, 1, +1) +; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 25** Zipping and unzipping directories: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options dlCreateDir; +libname arch1 "%workPath()/testArch1"; +libname arch2 "%workPath()/testArch2"; + +filename arch1 "%workPath()/testArch1"; + +data _null_; + file arch1(test1.txt); + put "text for test file 1"; +data _null_; + file arch1(test2.txt); + put "text for test file 2"; +data _null_; + file arch1(test3.txt); + put "text for test file 3"; +run; + +data arch1.class(index=(name)); + set sashelp.class; +run; +data arch1.cars(index=(model)); + set sashelp.cars; +run; + +%zipArch( + archName2.zip +, pathRef = arch1 +, target = %workPath()/testArch2 +, list = 1 +, overwrite = 1 +) + +%unzipArch( + archName2.zip +, path = %workPath()/testArch2 +, target = %workPath()/testArch2 +, clean=1 +, list=1 +); + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 26** Downloading data from the internet to a local dirrectory: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%downloadFilesTo(~/directoryA) +datalines4; +https://www.lexjansen.com/wuss/2023/WUSS-2023-Paper-189.pdf +https://www.lexjansen.com/wuss/2023/WUSS-2023-Paper-189.zip +;;;; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + +--- + + +--- + + +--- + +Package contains additional content, run: `%loadPackageAddCnt(BasePlus)` to load it +or look for the `baseplus_AdditionalContent` directory in the `packages` fileref +localization (only if additional content was deployed during the installation process). + +-------------------------------------------------------------------- + +*SAS package generated by SAS Package Framework, version `20231210`* + +-------------------------------------------------------------------- + +# The `BasePlus` package content +The `BasePlus` package consists of the following content: +1. [`%bppipe()` macro ](#bppipe-macro-1 ) +2. [`%deduplistc()` macro ](#deduplistc-macro-2 ) +3. [`%deduplistp()` macro ](#deduplistp-macro-3 ) +4. [`%deduplists()` macro ](#deduplists-macro-4 ) +5. [`%deduplistx()` macro ](#deduplistx-macro-5 ) +6. [`%dirsandfiles()` macro ](#dirsandfiles-macro-6 ) +7. [`%functionexists()` macro ](#functionexists-macro-7 ) +8. [`%getvars()` macro ](#getvars-macro-8 ) +9. [`%intslist()` macro ](#intslist-macro-9 ) +10. [`%ldsn()` macro ](#ldsn-macro-10 ) +11. [`%ldsnm()` macro ](#ldsnm-macro-11 ) +12. [`%lvarnm()` macro ](#lvarnm-macro-12 ) +13. [`%lvarnmlab()` macro ](#lvarnmlab-macro-13 ) +14. [`%qdeduplistx()` macro ](#qdeduplistx-macro-14 ) +15. [`%qgetvars()` macro ](#qgetvars-macro-15 ) +16. [`%qzipevalf()` macro ](#qzipevalf-macro-16 ) +17. [`%raincloudplot()` macro ](#raincloudplot-macro-17 ) +18. [`%repeattxt()` macro ](#repeattxt-macro-18 ) +19. [`%splitdsintoblocks()` macro ](#splitdsintoblocks-macro-19 ) +20. [`%splitdsintoparts()` macro ](#splitdsintoparts-macro-20 ) +21. [`%symdelglobal()` macro ](#symdelglobal-macro-21 ) +22. [`%unziparch()` macro ](#unziparch-macro-22 ) +23. [`%unziplibrary()` macro ](#unziplibrary-macro-23 ) +24. [`%ziparch()` macro ](#ziparch-macro-24 ) +25. [`%zipevalf()` macro ](#zipevalf-macro-25 ) +26. [`%ziplibrary()` macro ](#ziplibrary-macro-26 ) +27. [`$bool.` format/informat ](#bool-format-27 ) +28. [`$boolz.` format/informat ](#boolz-format-28 ) +29. [`$ceil.` format/informat ](#ceil-format-29 ) +30. [`$floor.` format/informat ](#floor-format-30 ) +31. [`$int.` format/informat ](#int-format-31 ) +32. [`arrfill()` function ](#arrfill-functions-32 ) +33. [`arrfillc()` function ](#arrfillc-functions-33 ) +34. [`arrmissfill()` function ](#arrmissfill-functions-34 ) +35. [`arrmissfillc()` function ](#arrmissfillc-functions-35 ) +36. [`arrmisstoleft()` function ](#arrmisstoleft-functions-36 ) +37. [`arrmisstoleftc()` function ](#arrmisstoleftc-functions-37 ) +38. [`arrmisstoright()` function ](#arrmisstoright-functions-38 ) +39. [`arrmisstorightc()` function ](#arrmisstorightc-functions-39 ) +40. [`bracketsc()` function ](#bracketsc-functions-40 ) +41. [`bracketsn()` function ](#bracketsn-functions-41 ) +42. [`catxfc()` function ](#catxfc-functions-42 ) +43. [`catxfi()` function ](#catxfi-functions-43 ) +44. [`catxfj()` function ](#catxfj-functions-44 ) +45. [`catxfn()` function ](#catxfn-functions-45 ) +46. [`deldataset()` function ](#deldataset-functions-46 ) +47. [`semicolonc()` function ](#semicolonc-functions-47 ) +48. [`semicolonn()` function ](#semicolonn-functions-48 ) +49. [`$brackets.` format/informat ](#brackets-format-49 ) +50. [`$semicolon.` format/informat ](#semicolon-format-50 ) +51. [`qsortincbyprocproto()` proto ](#qsortincbyprocproto-proto-51 ) +52. [`frommissingtonumberbs()` function ](#frommissingtonumberbs-functions-52 ) +53. [`fromnumbertomissing()` function ](#fromnumbertomissing-functions-53 ) +54. [`quicksort4notmiss()` function ](#quicksort4notmiss-functions-54 ) +55. [`quicksorthash()` function ](#quicksorthash-functions-55 ) +56. [`quicksorthashsddv()` function ](#quicksorthashsddv-functions-56 ) +57. [`quicksortlight()` function ](#quicksortlight-functions-57 ) +58. [`%date()` macro ](#date-macro-58 ) +59. [`%datetime()` macro ](#datetime-macro-59 ) +60. [`%downloadfilesto()` macro ](#downloadfilesto-macro-60 ) +61. [`%filepath()` macro ](#filepath-macro-61 ) +62. [`%finddswithvarval()` macro ](#finddswithvarval-macro-62 ) +63. [`%fmt()` macro ](#fmt-macro-63 ) +64. [`%gettitle()` macro ](#gettitle-macro-64 ) +65. [`%infmt()` macro ](#infmt-macro-65 ) +66. [`%letters()` macro ](#letters-macro-66 ) +67. [`%libpath()` macro ](#libpath-macro-67 ) +68. [`%minclude()` macro ](#minclude-macro-68 ) +69. [`%monthshift()` macro ](#monthshift-macro-69 ) +70. [`%replist()` macro ](#replist-macro-70 ) +71. [`%time()` macro ](#time-macro-71 ) +72. [`%today()` macro ](#today-macro-72 ) +73. [`%translate()` macro ](#translate-macro-73 ) +74. [`%tranwrd()` macro ](#tranwrd-macro-74 ) +75. [`%workpath()` macro ](#workpath-macro-75 ) + + +93. [License note](#license) + +--- + +## `%bppipe()` macro ###### + +## >>> `%bpPIPE()` macro: <<< ####################### + +The bpPIPE() [Base Plus PIPE] macro executes OS command +and print to the log output of the execution. + +Under the hood it uses `_` filename reference to PIPE device. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%bpPIPE( ) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +* **NO Arguments** - Everything inside brackets is treated as an OS command. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** List, to the log, content of D and C drives: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %bpPIPE(D: & dir & dir "C:\") +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** List, to the log, content of `home` directory: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %bpPIPE(ls -halt ~/) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%deduplistc()` macro ###### + +## >>> `%dedupListC()` macro: <<< ####################### + +The `%dedupListC()` macro deletes duplicated values from +a *COMMA separated* list of values. List, including separators, +can be no longer than a value carried by a single macrovariable. + +Returned value is *unquoted*. Leading and trailing spaces are ignored. + +The `%dedupListC()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dedupListC( + list,of,comma,separated,values +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - A list of *comma separated* values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListC(a,b,c,b,c)*; + + %put *%dedupListC(a,b c,b c)*; + + %put *%dedupListC(%str(a,b,c,b,c))*; + + %put *%dedupListC(%str(a),%str(b),%str(c),b,c)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Leading and trailing spaces are ignored. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListC( a , b b , c , b b, c )*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** Macro variable as an argument. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4, 5, 6, 1, 2, 3, 1, 2, 3, 4, 5, 6; + %put *%dedupListC(&list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%deduplistp()` macro ###### + +## >>> `%dedupListP()` macro: <<< ####################### + +The `%dedupListP()` macro deletes duplicated values from +a *PIPE(`|`) separated* list of values. List, including separators, +can be no longer than a value carried by a single macrovariable. + +Returned value is *unquoted*. Leading and trailing spaces are ignored. + +The `%dedupListP()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dedupListP( + list|of|pipe|separated|values +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - A list of *pipe separated* values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListP(a|b|c|b|c)*; + + %put *%dedupListP(a|b c|b c)*; + + %put *%dedupListP(%str(a|b|c|b|c))*; + + %put *%dedupListP(%str(a)|%str(b)|%str(c)|b|c)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Leading and trailing spaces are ignored. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListP( a | b b | c | b b| c )*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** Macro variable as an argument. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4|5|6|1|2|3|1|2|3|4|5|6; + %put *%dedupListP(&list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%deduplists()` macro ###### + +## >>> `%dedupListS()` macro: <<< ####################### + +The `%dedupListS()` macro deletes duplicated values from +a *SPACE separated* list of values. List, including separators, +can be no longer than a value carried by a single macrovariable. + +Returned value is *unquoted*. + +The `%dedupListS()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dedupListS( + list of space separated values +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - A list of *space separated* values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListS(a b c b c)*; + + %put *%dedupListS(a b,c b,c)*; + + %put *%dedupListS(%str(a b c b c))*; + + %put *%dedupListS(%str(a) %str(b) %str(c) b c)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Macro variable as an argument. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4 5 6 1 2 3 1 2 3 4 5 6; + %put *%dedupListS(&list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%deduplistx()` macro ###### + +## >>> `%dedupListX()` macro: <<< ####################### + +The `%dedupListX()` macro deletes duplicated values from +a *X separated* list of values, where the `X` represents +a *single character* separator. List, including separators, +can be no longer than a value carried by a single macrovariable. + +**Caution.** The value of `X` *has to be* in **the first** byte of the list, + just after the opening bracket, i.e. `(X...)`. + +Returned value is *unquoted*. Leading and trailing spaces are ignored. + +The `%dedupListX()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dedupListX( +XlistXofXxXseparatedXvalues +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - A list of *X separated* values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListX(|a|b|c|b|c)*; + + %put *%dedupListX( a b c b c)*; + + %put *%dedupListX(,a,b,c,b,c)*; + + %put *%dedupListX(XaXbXcXbXc)*; + + %put *%dedupListX(/a/b/c/b/c)*; + + data _null_; + x = "%dedupListX(%str(;a;b;c;b;c))"; + put x=; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Leading and trailing spaces are ignored. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%dedupListX(| a | b.b | c | b.b| c )*; + + %put *%dedupListX(. a . b b . c . b b. c )*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** Macro variable as an argument. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4$5.5$6$1$2$3$1$2$3$4$5.5$6; + %put *%dedupListX($&list.)*; + + %let list = 4$ 5.5$ 6$ 1$ 2$ 3$ 1$ 2$ 3$ 4$ 5.5$ 6$; + %put *%dedupListX( &list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%dirsandfiles()` macro ###### + +## >>> `%dirsAndFiles()` macro: <<< ####################### + +The `%dirsAndFiles()` macro allows to extract info about all files +and subdirectories of a given `root` directory. + +The extracted info may be just a list of files and subdirectories or, if +the `details=` parameter is set to 1, additional operating system information +is extracted (information is OSS dependent and gives different results for Linux +and for Windows) + +The extracted info can be narrowed down to files (`keepFiles=1`) or to +directories (`keepDirs=1`) if need be. + +The extracted info can be presented in wide or long format (`longFormat=1`). + +The extracted info for files can be narrowed down to only files with particular +extension, for example: `fileExt=sas7bdat`. + +The extracted info can be narrowed down maximal path depth +by setting up the `maxDepth=` parameter. + +See examples below for the details. + +### REFERENCES: ################################################################### + +The macro is based on Kurt Bremser's "*Talking to Your Host*" article +presented at WUSS 2022 conference. + +The article is available [here](https://communities.sas.com/t5/SAS-User-Groups-Library/WUSS-Presentation-Talking-to-Your-Host/ta-p/838344) +and also as an additional content of this package. +The paper was awarded the "Best Paper Award - Programming". + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles( + root + <,ODS=> + <,details=> + <,keepDirs=> + <,keepFiles=> + <,longFormat=> + <,fileExt=> + <,maxDepth=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `root` - *Required*, path to be searched + for information. + +* `ODS=work.dirsAndFilesInfo` - *Optional*, output data set, + name of a dataset to store information. + +* `details=0` - *Optional*, indicates if detailed info + will be collected, `1` = yes, `0` = no. + +* `keepDirs=1` - *Optional*, indicates if directories info + will be collected, `1` = yes, `0` = no. + +* `keepFiles=1` - *Optional*, indicates if files info + will be collected, `1` = yes, `0` = no. + +* `longFormat=0` - *Optional*, indicates if output be + in long format, `1` = yes, `0` = no. + +* `fileExt=` - *Optional*, if not missing then indicates + file extension to filter out results. + +* `maxDepth=0` - *Optional*, if not zero then indicates + maximum depth of search in the root path. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get list of files and directories: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Get detailed info: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result2,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Get only files info: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result3,keepDirs=0) + +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result5,keepDirs=0,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Get only directories info: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result4,keepFiles=0) + +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result6,keepFiles=0,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Filter out by `sas` extension: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(~/,ODS=work.result7,fileExt=sas) + +%dirsAndFiles(~/,ODS=work.result8,fileExt=sas,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** Keep result in the long format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(~/,ODS=work.result9,details=1,longFormat=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** Get info for maximum depth of 2: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(C:\SAS_WORK\,ODS=work.result10,details=1,maxDepth=2) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 8.** How locked/unavailable files are handled: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(%sysfunc(pathname(WORK)),ODS=work.result11,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 9.** Not existing directory: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%dirsAndFiles(%sysfunc(pathname(WORK))/noSuchDir,ODS=work.result12,details=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +--- + +## `%functionexists()` macro ###### + +## >>> `%functionExists()` macro: <<< ####################### + +The functionExists() macro function tests +if given funcion exists in the SAS session. +The `sashelp.vfunc` view is used. + +See examples below for the details. + +The `%functionExists()` macro executes like a pure macro code. + +The function is a result of cooperation with [Allan Bowe](https://www.linkedin.com/in/allanbowe/) + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%functionExists( + funName +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `funName` - *Required*, the name of the function + existence of which you are testing. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Test if function exists: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %functionExists(HASHING); + + %put %functionExists(COSsinLOG); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%getvars()` macro ###### + +## >>> `%getVars()` macro: <<< ####################### + +The getVars() and QgetVars() macro functions +allow to extract variables names form a dataset +according to a given pattern into a list. + +The getVars() returns unquoted value [by %unquote()]. +The QgetVars() returns quoted value [by %superq()]. + +See examples below for the details. + +The `%getVars()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%getVars( + ds + <,sep=> + <,pattern=> + <,varRange=> + <,quote=> + <,mcArray=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `ds` - *Required*, the name of the dataset from + which variables are to be taken. + +* `sep = %str( )` - *Optional*, default value `%str( )`, + a variables separator on the created list. + +* `pattern = .*` - *Optional*, default value `.*` (i.e. any text), + a variable name regexp pattern, case INSENSITIVE! + +* `varRange = _all_` - *Optional*, default value `_all_`, + a named range list of variables. + +* `quote =` - *Optional*, default value is blank, a quotation + symbol to be used around values. + +* `mcArray=` - *Optional*, default value is blank. + 1) When *null* - the macro behaves like a macro function + and returns a text string with variables list. + 2) When *not null* - behaviour of the macro is altered. + In such case a macro array of selected variables, named + with `mcArray` value as a prefix, is created. + Furthermore a macro named as `mcArray` value is generated. + (see the macroArray package for the details). + When `mcArray=` parameter is active the `getVars` macro + cannot be called within the `%put` statement. Execution like: + `%put %getVars(..., mcArray=XXX);` will result with + an Explicit & Radical Refuse Of Run (aka ERROR). + + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** A list of all variables from the + sashelp.class dataset: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(sashelp.class)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** A list of all variables from the + sashelp.class dataset separated + by backslash: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let x = %getVars(sashelp.class, sep=\); + %put &=x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Use of regular expressions: + a) A list of variables which name contains "i" or "a" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(sashelp.class, pattern=i|a)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + b) A list of variables which name starts with "w" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(sashelp.class, pattern=^w)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + c) A list of variables which name ends with "ght" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(sashelp.class, pattern=ght$)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** A list of numeric variables which name + starts with "w" or "h" or ends with "x" +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(sashelp.class, sep=+, pattern=^(w|h)|x$, varRange=_numeric_)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data test; + array x[30]; + array y[30] $ ; + array z[30]; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + a) A list of variables separated by a comma: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(test, sep=%str(,))*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + b) A list of variables separated by a comma + with suffix 5 or 7: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(test, sep=%str(,), pattern=(5|7)$)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + c) A list of variables separated by a comma + with suffix 5 or 7 from a given variables range: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(test, sep=%str(,), varRange=x10-numeric-z22 y6-y26, pattern=(5|7)$)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** Case of quotes and special characters + when the quote= parameter is _not_ used: + + a) one single or double qiote: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%bquote(%getVars(sashelp.class, sep=%str(%")))*; + %put *%bquote(%getVars(sashelp.class, sep=%str(%')))*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + b) two single or double qiotes: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *"%bquote(%getVars(sashelp.class,sep=""))"*; + %put *%str(%')%bquote(%getVars(sashelp.class,sep=''))%str(%')*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + c) coma separated double quote list: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *"%getVars(sashelp.class,sep=%str(", "))"*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + d) coma separated single quote list: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%str(%')%getVars(sashelp.class,sep=', ')%str(%')*; + %let x = %str(%')%getVars(sashelp.class,sep=', ')%str(%'); + + %put *%str(%')%QgetVars(sashelp.class,sep=', ')%str(%')*; + %let y = %str(%')%QgetVars(sashelp.class,sep=', ')%str(%'); + %let z = %unquote(&y.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + e) ampersand (&) as a separator [compare behaviour]: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(sashelp.class,sep=&)*; + %let x = %getVars(sashelp.class,sep=&); + + %put *%getVars(sashelp.class,sep=%str( & ))*; + %let x = %getVars(sashelp.class,sep=%str( & )); + + %put *%QgetVars(sashelp.class,sep=&)*; + %let y = %QgetVars(sashelp.class,sep=&); + %let z = %unquote(&y.); + + %put *%QgetVars(sashelp.class,sep=%str( & ))*; + %let y = %QgetVars(sashelp.class,sep=%str( & )); + %let z = %unquote(&y.); + + %put *%getVars(sashelp.class,sep=&)*; + %let x = %getVars(sashelp.class,sep=&); + + %put *%getVars(sashelp.class,sep=%str( & ))*; + %let x = %getVars(sashelp.class,sep=%str( & )); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + f) percent (%) as a separator [compare behaviour]: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%QgetVars(sashelp.class,sep=%)*; + %let y = %QgetVars(sashelp.class,sep=%); + %let z = %unquote(&y.); + + %put *%QgetVars(sashelp.class,sep=%str( % ))*; + %let y = %QgetVars(sashelp.class,sep=%str( % )); + %let z = %unquote(&y.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** Case of quotes and special characters + when the quote= parameter is used: + +a) one single or double qiote: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(sashelp.class, quote=%str(%"))*; + %put *%getVars(sashelp.class, quote=%str(%'))*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + b) two single or double quotes: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %* this gives an error: ; + %* %put *%getVars(sashelp.class,quote="")*; + %* %put *%getVars(sashelp.class,quote='')*; + + %* this does not give an error: ; + %put *%QgetVars(sashelp.class,quote="")*; + %put *%QgetVars(sashelp.class,quote='')*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + c) coma separated double quote list: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%getVars(sashelp.class,sep=%str(,),quote=%str(%"))*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + d) coma separated single quote list: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let x = %getVars(sashelp.class,sep=%str(,),quote=%str(%')); + %put &=x.; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 8.** Variables that start with `A` and do not end with `GHT`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data class; + set sashelp.class; + Aeight = height; +run; + +%put *%getVars(class, pattern = ^A(.*)(? ###### + +## >>> `%intsList()` macro: <<< ####################### + +The intsList() macro function allows to print a list of +integers starting from `start` up to `end` incremented by `by` +and separated by `sep=`. + +If `start`, `end` or `by` are non-integers the are converted to integers. + +See examples below for the details. + +The `%intsList()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%intsList( + start + <,end> + <,by> + <,sep=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `start` - *Required*, the first value of the list. + If `end` is missing then the list is generated + from 1 to `start` by 1. + +2. `end` - *Required/Optional*, the last value of the list. + +3. `by` - *Required/Optional*, the increment of the list. + If missing then set to `1`. + *Cannot* be equal to `0`. + +* `s = %str( )` - *Optional*, it is a separator between + elements of the list. Default value is space. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple list of integers from 1 to 10 by 1: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %intsList(10); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Ten copies of `sashelp.class` in `test11` to `test20`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data + %zipEvalf(test, %intsList(11,20)) + ; + set sashelp.class; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Non-integers are converted to integers, the list is `1 3 5`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %intsList(1.1,5.2,2.3); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** A list with a separator: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %intsList(1,5,2,sep=+); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%ldsn()` macro ###### + +## >>> `%LDSN()` macro: <<< ####################### + +The LDSN (Long DataSet Names) macro function +allows to use an "arbitrary" text string to name a dataset. + +The LDSN macro has some limitation described below, to overcome them +another macro, with different name: LDSNM (Long DataSet Names Modified) +was created. See its description to learn how to use it. + +--- + +The idea for the macro came from the following story: + +Good friend of mine, who didn't use SAS for quite some time, +told me that he lost a few hours for debugging because +he forgot that the SAS dataset name limitation is 32 bytes. + +I replied that it shouldn't be a problem to do a workaround +for this inconvenience with a macro and the `MD5()` hashing function. + +I said: *The macro should take an "arbitrary string" for a dataset +name, convert it, with help of `MD5()`, to a hash digest, and +create a dataset with an "artificial" `hex16.` formated name.* + +Starting with something like this: + +~~~~~~~~~~~~~~~~~~~~~~~sas +data %LDSN(work. peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s (drop = sex rename=(name=first_name) where = (age in (12,13,14))) ); + set sashelp.class; +run; +~~~~~~~~~~~~~~~~~~~~~~~ + +the macro would do: + +~~~~~~~~~~~~~~~~~~~~~~~sas +%sysfunc(MD5(peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s), hex16.) +~~~~~~~~~~~~~~~~~~~~~~~ + +and (under the hood) return and execute the following code: + +~~~~~~~~~~~~~~~~~~~~~~~sas +data work.DSN_41D599EF51FBA58_(drop = sex rename=(name=first_name) where = (age in (12,13,14))) ; + set sashelp.class; +run; +~~~~~~~~~~~~~~~~~~~~~~~ + +Also in the next data step user should be able to do: + +~~~~~~~~~~~~~~~~~~~~~~~sas +data my_next_data_step; + set %DSN(work. peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s); +run; +~~~~~~~~~~~~~~~~~~~~~~~ + +and work without the "dataset-name-length-limitation" issue. + +--- + +See examples below for the details. + +The `%LDSN()` macro executes like a pure macro code. + +**Known "Limitations":** + +- dataset name _cannot_ contain dots (`.`) since they are used as separators! + +- dataset name _cannot_ contain round brackets(`(` and `)`) since they are used as separators + (but `[]` and `{}` are allowed)! + +- dataset name _cannot_ contain unpaired quotes (`'` and `"`), + text: `a "hot-dog"` is ok, but `John's dog` is not! + +**Behaviour:** + +- dataset name text is *converted to upcase* + +- dataset name text *leading and trailing spaces are ignored*, + e.g. the following will give the same hash digest: + `%ldsn(work.test)`, `%ldsn( work.test)`, `%ldsn(work.test )`, + `%ldsn(work .test)`, `%ldsn(work. test)`, `%ldsn(work . test)`. + +- macro calls of the form: + `data %LDSN(); run;`, `data %LDSN( ); run;`, `data %LDSN( . ); run;` or even + `data %LDSN( . (keep=x)); run;` are resolved to empty string, so the result is + equivalent to `data; run;` + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%LDSN( + arbitrary text string (in line with limitations) +) +~~~~~~~~~~~~~~~~~~~~~~~ + +The text string is concider as *"fully qualified dataset name"*, i.e. macro +assumes it may contain library as prefix and data set options as sufix. +See the `%LDsNm()` macro for comparison. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options nomprint source nomlogic nosymbolgen ls = max ps = max; + +data %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s (drop = sex rename=(name=first_name) where = (age in (12,13,14))) ); + set sashelp.class; +run; + +proc print data = %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ); +run; + +data MyNextDataset; + set %LDSN( work. peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ); + where age > 12; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%ldsnm()` macro ###### + +## >>> `%LDSNM()` macro: <<< ####################### + +The LDSNM (Long DataSet Names Modified) macro function +allows to use an "arbitrary" text string to name a dataset. + +The LDSN macro had some limitation (see its documentation), to overcome them +another `%LDSNM()` (Long DataSet Names Modified) macro was created. + +The main idea behind the `%LDSNM()` is the same as for `%LDSN()` - see the description there. + +--- + +The `%LDSNM()` works differently then the `%LDSN()`. + +The `%LDSN()` assumed that *both* libname and dataset options *could* +be passed as elements in macro argument, e.g. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data %LDSN( WORK.peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s (drop = sex) ); + set sashelp.class; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `%LDSNM()`, in contrary, assumes that both libname and dataset options are +passed **outside** the macro, i.e. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data WORK.%LDSNM( peanut butter & jelly with a hot-dog in [a box] and s*t*a*r*s ) (drop = sex); + set sashelp.class; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This approach reduces some limitations the LDSN has. + +The **additional** feature of the `%LDSNM()` is that when the macro is called +a global macrovariable, which name is the same as hashed dataset name, is created. +The macrovariable value is the text of the argument of the macro. For example +the following macro call: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data %LDSNM(John "x" 'y' dog); + set sashelp.class; + where name = 'John'; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +creates `DSN_BF1F8C4D6495B34A_` macrovariable with value: `JOHN "X" 'Y' DOG`. + +The macrovariable is useful when combined with `symget()` function and +the `indsname=` option to get the original text string value back, +like in this example: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data test; + set %LDSNM(John "x" 'y' dog) indsname = i; + + indsname = symget(scan(i,-1,".")); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See examples below for the details. + +--- + +The `%LDSN()` macro executes like a pure macro code. + +**Known "Limitations":** + +- dataset name _cannot_ contain _unpaired_ round brackets(`(` and `)`) + (but unmatched `[]` and `{}` are allowed)! + +- dataset name _cannot_ contain _unpaired_ quotes (`'` and `"`), + text: `a "hot-dog"` is ok, but `John's dog` is not! + +**Behaviour:** + +- dataset name text is *converted to upcase* + +- dataset name text *leading and trailing spaces are ignored*, + e.g. the following will give the same hash digest: + `%ldsn(test)`, `%ldsn( test)`, `%ldsn(test )`. + +- macro calls of the form: + `data %LDSN(); run;` or `data %LDSN( ); run;` are resolved + to empty string, so the result is equivalent to `data; run;` + +- created macrovariable is _global_ in scope. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%LDSNM( + arbitrary text string (in line with limitations) +) +~~~~~~~~~~~~~~~~~~~~~~~ + +The text string is concider as *"only dataset name"*, i.e. macro does not +assume it contain library as prefix or data set options as sufix. +See the `%LDSN()` macro for comparison. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data %LDSNM(John "x" 'y' & dog); + set sashelp.class; + where name = 'John'; +run; + +data %LDSNM(John "x"[ 'y' & dog); + set sashelp.class; + where name = 'John'; +run; + +data %LDSNM(John "x" 'y'} & dog); + set sashelp.class; + where name = 'John'; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data work.%LDsNm( peanut butter & jelly, a hot-dog in [a box], and s(*)t(*)a(*)r(*)s!! ) (drop = sex rename=(name=first_name) where = (age in (12,13,14))) +; + set sashelp.class; +run; + +data test; + set work.%LDsNm( peanut butter & jelly, a hot-dog in [a box], and s(*)t(*)a(*)r(*)s!! ) indsname=i; + + indsname=symget(scan(i,-1,".")); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data work.%LDsNm( . ); + set sashelp.class; +run; + +data %LDsNm( ); + set sashelp.class; +run; + + +data %LDsNm(); + set sashelp.class; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%lvarnm()` macro ###### + +## >>> `%LVarNm()` macro: <<< ####################### + +The LVarNm() macro function works like the LDSN() macro function, but for variables. +Supported by LVarNmLab() macro function which allows to remember "user names" in labels. + +The motivation for the macro was similar one as for the LDSN() macro. + +--- + +See examples below for the details. + +The `%LVarNm()` macro executes like a pure macro code. + +**Known "Limitations":** + +- variable name _cannot_ contain unpaired quotes (`'` and `"`), + text: `a "hot-dog"` is ok, but `John's dog` is not! + +**Behaviour:** + +- variable name text is *converted to upcase* + +- variable name text *leading and trailing spaces are ignored*, + e.g. the following will give the same hash digest: + `%LVarNm(test)`, `%LVarNm( test)`, `%LVarNm(test )`. + +- if the user want to add an extra suffix to the variable, + e.g. to get a numerical suffix, the `%LVarNm()` macro + **has** to be wrapped inside the `%unquote()` macro function. +~~~~~~~~~~~~~~~~~~~~~~~sas +data test4; + array X[*] %unquote(%LVarNm(some strange! name))_0 - %unquote(%LVarNm(some strange! name))_10; + + do i = lbound(X) to hbound(X); + X[i] = 2**(i-1); + put X[i]=; + end; +run; +~~~~~~~~~~~~~~~~~~~~~~~ + The reason for this is a "bug" like behaviour of SAS tokenizer/macroprocesor. + See the following SAS-L discussion thread: + `https://listserv.uga.edu/scripts/wa-UGA.exe?A2=SAS-L;4b2bcf80.2205A&S=` + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%LVarNm( + arbitrary text string (in line with limitations) +) +~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options ls=max; +data test; + %LVarNmLab( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) + + do %LVarNm( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) = 1 to 10; + + y = 5 + %LVarNm( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) * 17; + output; + end; +run; + +data test2; + set test; + where %LVarNm( peanut butter & jelly with a "Hot-Dog" in [a box], popcorn, and s*t*a*r*s ) < 5; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data test3; + %LVarNmLab() = 17; + + %LVarNm() = 17; + + %LVarNm( ) = 42; + + %LVarNm( ) = 303; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data test3; + %LVarNm(test) = 1; + + %LVarNm( test) = 2; + + %LVarNm(test ) = 3; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data test4; + array X[*] %LVarNm(some strange! name)_0 - %LVarNm(some strange! name)_10; + + do i = lbound(X) to hbound(X); + X[i] = 2**(i-1); + put X[i]=; + end; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%lvarnmlab()` macro ###### + +## >>> `%LVarNmLab()` macro: <<< ####################### + +The LVarNmLab() macro function supports LVarNm() and allows to remember "user names" in labels. + +The motivation for the macro was similar one as for the LDSN() macro. + +--- + +See examples in LVarNm() documentation for the details. + +The `%LVarNmLab()` macro executes like a pure macro code. + +**Known "Limitations":** + +- variable name _cannot_ contain unpaired quotes (`'` and `"`), + text: `a "hot-dog"` is ok, but `John's dog` is not! + +**Behaviour:** + +- variable name text is *converted to upcase* + +- variable name text *leading and trailing spaces are ignored*, + e.g. the following will give the same hash digest: + `%LVarNmLab(test)`, `%LVarNmLab( test)`, `%LVarNmLab(test )`. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%LVarNmLab( + arbitrary text string (in line with limitations) +) +~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%qdeduplistx()` macro ###### + +## >>> `%QdedupListX()` macro: <<< ####################### + +The `%QdedupListX()` macro deletes duplicated values from +a *X separated* list of values, where the `X` represents +a *single character* separator. List, including separators, +can be no longer than a value carried by a single macrovariable. + +**Caution.** The value of `X` *has to be* in **the first** byte of the list, + just after the opening bracket, i.e. `(X...)`. + +Returned value is **quoted** with `%superq()`. Leading and trailing spaces are ignored. + +The `%QdedupListX()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%QdedupListX( +XlistXofXxXseparatedXvalues +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - A list of *X separated* values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%QdedupListX(|a|b|c|b|c)*; + + %put *%QdedupListX( a b c b c)*; + + %put *%QdedupListX(,a,b,c,b,c)*; + + %put *%QdedupListX(XaXbXcXbXc)*; + + %put *%QdedupListX(/a/b/c/b/c)*; + + %put *%QdedupListX(%str(;a;b;c;b;c))*; + + %put *%QdedupListX(%nrstr(&a&b&c&b&c))*; + + %put *%QdedupListX(%nrstr(%a%b%c%b%c))*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Leading and trailing spaces are ignored. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%QdedupListX(| a | b.b | c | b.b| c )*; + + %put *%QdedupListX(. a . b b . c . b b. c )*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 3.** Macro variable as an argument. + Delete duplicated values from a list. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let list = 4$5.5$6$1$2$3$1$2$3$4$5.5$6; + %put *%QdedupListX($&list.)*; + + %let list = 4$ 5.5$ 6$ 1$ 2$ 3$ 1$ 2$ 3$ 4$ 5.5$ 6$; + %put *%QdedupListX( &list.)*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%qgetvars()` macro ###### + +## >>> `%QgetVars()` macro: <<< ####################### + +The getVars() and QgetVars() macro functions +allow to extract variables names form a dataset +according to a given pattern into a list. + +The getVars() returns unquoted value [by %unquote()]. +The QgetVars() returns quoted value [by %superq()]. + +The `%QgetVars()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%QgetVars( + ds + <,sep=> + <,pattern=> + <,varRange=> + <,quote=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `ds` - *Required*, the name of the dataset from + which variables are to be taken. + +* `sep = %str( )` - *Optional*, default value `%str( )`, + a variables separator on the created list. + +* `pattern = .*` - *Optional*, default value `.*` (i.e. any text), + a variable name regexp pattern, case INSENSITIVE! + +* `varRange = _all_` - *Optional*, default value `_all_`, + a named range list of variables. + +* `quote =` - *Optional*, default value is blank, a quotation + symbol to be used around values. + + +### EXAMPLES AND USECASES: #################################################### + +See examples in `%getVars()` help for the details. + +--- + + +--- + +## `%qzipevalf()` macro ###### + +## >>> `%QzipEvalf()` macro: <<< ####################### + +The zipEvalf() and QzipEvalf() macro functions +allow to use a function on elements of pair of +space separated lists. + +For two space separated lists of text strings 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 lists is shorter then elements are "reused" starting +from the beginning. + +The zipEvalf() returns unquoted value [by %unquote()]. +The QzipEvalf() returns quoted value [by %superq()]. + +See examples below for the details. + +The `%QzipEvalf()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%QzipEvalf( + first + ,second + <,function=> + <,operator=> + <,argBf=> + <,argMd=> + <,argAf=> + <,format=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**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. + + +### EXAMPLES AND USECASES: #################################################### + +See examples in `%zipEvalf()` help for the details. + +--- + + +--- + +## `%raincloudplot()` macro ###### + +## >>> `%RainCloudPlot()` macro: <<< ####################### + +The RainCloudPlot() macro allow to plot Rain Cloud plots, i.e. pots of +kernel density estimates, jitter data values, and box-and-whiskers plot. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%RainCloudPlot( + DS + ,gr + ,vars + + <,WidthPX=> + <,HeightPX=> + <,boxPlot=> + <,roundFactor=> + <,rainDropSize=> + <,boxPlotSymbolSize=> + <,colorsList=> + <,monochrome=> + <,antialiasMax=> + <,title=> + <,footnote=> + <,catLabels=> + <,xLabels=> + <,catLabelPos=> + <,xLabelPos=> + <,catLabelAttrs=> + <,xLabelAttrs=> + <,formated=> + <,y2axis=> + <,y2axisLevels=> + <,y2axisValueAttrs=> + <,catAxisValueAttrs=> + <,xaxisValueAttrs=> + <,xaxisTickstyle=> + <,sganno=> + <,odsGraphicsOptions=> + <,sgPlotOptions=> + + <,VSCALE=> + <,KERNEL_K=> + <,KERNEL_C=> + + <,cleanTempData=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `DS` - *Required*, name of the dataset from + which variables are to be taken. + +2. `gr` - *Required*, name of the grouping variable. + When more than one variable is specified + separate plots are rendered. + Can be numeric or character. + +3. `vars` - *Required*, name of the aggregated numeric variable. + When more than one variable is specified + separate plots are rendered. + +***Plot related options***: + +* `WidthPX` - *Optional*, default value `1200`. + Total width of the plot in pixels. + +* `HeightPX` - *Optional*, default value `220`. + Partial height of the plot in pixels. + Total height is calculated as `#GROUPS x HeightPX`. + +* `boxPlot` - *Optional*, default value `1`. + Indicates if the Box Plot should be added. + +* `roundFactor` - *Optional*, default value `0.000001`. + Rounding level when calculating maximum value + of the cloud chart. Should be adjusted to data + granularity level, e.g. for data with value + around `1e-8` should be decreased. + +* `rainDropSize` - *Optional*, default value `5px`. + Size of data points in the "rain" plot. + +* `boxPlotSymbolSize` - *Optional*, default value `8px`. + Size of symbols on the box plot. + +* `colorsList` - *Optional*, default value is empty. + List of colours for plotting. + Empty indicates that the default list will be used. + +* `monochrome` - *Optional*, default value `0`. + Indicates if the default list of colours should be gray-scale. + +* `antialiasMax` - *Optional*, default value is empty. + Sets a value to the ODS graphics `ANTIALIASMAX` option. + When empty the value is calculated from data. + +* `title` - *Optional*, default value - see notes below. + Provides a list of titles printed on the plot. + For details see notes below. + +* `footnote` - *Optional*, default value - see notes below. + Provides a list of titles printed on the plot. + For details see notes below. + +* `catLabels` - *Optional*, default value is empty. + List of values for group axix labels (vertical). + When empty a grouping variable name is used. + For details see notes below. + +* `xLabels` - *Optional*, default value is empty. + List of values for data variable axix labels (horizontal). + When empty a data variable name is used. + For details see notes below. + +* `catLabelPos` - *Optional*, default value `DATACENTER`. + Indicates position of the label on group axix (vertical). + Allowed values are `BOTTOM`, `CENTER`, `DATACENTER`, and `TOP`. + +* `xLabelPos` - *Optional*, default value `DATACENTER`. + Indicates position of the label on data axix (horizontal). + Allowed values are `LEFT`, `CENTER`, `DATACENTER`, and `RIGHT`. + +* `catLabelAttrs` - *Optional*, default value is empty. + List of attributes for group axix labels (vertical). + For details see notes below. + +* `xLabelAttrs` - *Optional*, default value is empty. + List of attributes for data variable axix labels (horizontal). + For details see notes below. + +* `formated` - *Optional*, default value `0`. + Indicates if values of the grouping variable should be formated. + +* `y2axis` - *Optional*, default value `1`. + Indicates if the right vertical axix should be displayed. + +* `y2axisLevels` - *Optional*, default value `4`. + Indicates if the number of expected levels of values printed + on the right vertical axix. + +* `y2axisValueAttrs` - *Optional*, default value `Color=Grey`. + Allows to modify Y2 axis values attributes. + +* `catAxisValueAttrs` - *Optional*, default value `Color=Black`. + Allows to modify category (Y) axis values attributes. + +* `xaxisValueAttrs` - *Optional*, default value `Color=Grey`. + Allows to modify X axis values attributes. + +* `xaxisTickstyle` - *Optional*, default value `INSIDE`. + Allows to modify X axis tick style. + Allowed values are `OUTSIDE`, `INSIDE`, `ACROSS`, and `INBETWEEN`. + *For SAS previous to* **9.4M5** *set to missing!* + +* `sganno` - *Optional*, default value is empty. + keeps name of a data set for the `sganno=` option + of the SGPLOT procedure. + +* `sgPlotOptions` - *Optional*, default value is `noautolegend noborder`. + List of additional options values for SGPLOT procedure. + +* `odsGraphicsOptions` - *Optional*, default value is empty. + List of additional options values for `ODS Graphics` statement. + By default only the: `width=`, `height=`, and `antialiasmax=` + are modified. + + +***Stat related options***: + +* `VSCALE` - *Optional*, default value `Proportion`. + Specifies the scale of the vertical axis. + Allowed values are `PROPORTION`, `PERCENT`, and `COUNT`. + `PROPORTION` scales the data in units of proportion of observations per data unit. + `PERCENT` scales the data in units of percent of observations per data unit. + `COUNT` scales the data in units of the number of observations per data unit. + +* `KERNEL_K` - *Optional*, default value `NORMAL`. + Specifies type of kernel function to compute kernel density estimates. + Allowed values are `NORMAL`, `QUADRATIC`, and `TRIANGULAR`. + + +* `KERNEL_C` - *Optional*, default value `1`. + Specifies standardized bandwidth parameter *C* to compute kernel density estimates. + Allowed values are between `0` and `1`, + +***Other options***: + +* `cleanTempData` - *Optional*, default value `1`. + Indicates if temporary data sets should be deleted. + +--- + +### NOTES: ################################################################### + +* Default value of the `title` option is: + `%nrstr(title1 JUSTIFY=C "Rain Cloud plot for &list_g. by " %unquote(&xLabel.);)` + Use the `%str()` or `%nrstr()` macro-function to handle special characters. + The `%unquote()` is used when resolving the parameter. + +* Default value of the `footnote` option is: + `%nrstr(footnote1 JUSTIFY=L COLOR=lightGray HEIGHT=1 "by RainCloudPlot macro from the BasePlus package";)` + Use the `%str()` or `%nrstr()` macro-function to handle special characters. + The `%unquote()` is used when resolving the parameter. + +* The `catLabels` and `xLabels` should be quoted comma separated lists enclosed with brackets, + e.g. `catLabels=("Continent of Origin", "Car Type")`, see Example below. + +* The `catLabelAttrs` and `xLabelAttrs` should be space separated lists of `key=value` pairs, + e.g. `xLabelAttrs=size=12 color=Pink weight=bold`, see Example below. + +* Kernel density estimates and basic statistics are calculated with `PROC UNIVARIATE`. + +* Plot is generated by `PROC SGPLOT` with `BAND`, `SCATTE`, and `POLYGON` plots. + +* After execution the ODS graphics dimension parameters are set to `800px` by `600px`. + +* SAS notes (`NOTE:`) are disabled for the execution time. + +* List of predefined colours is: + `BlueViolet`, `RoyalBlue`, `OliveDrab`, `Gold`, `HotPink`, `Crimson`, + `MediumPurple`, `CornflowerBlue`, `YellowGreen`, `Goldenrod`, `Orchid`, `IndianRed`. + +### BOX-AND-WHISKERS PLOT: ################################################################### + +The box-and-whiskers plot has the following interpretation: +- left vertical bar indicates the minimum, +- left whisker line starts at `max(Q1 - 1.5IQR, minimum)` and ends at lower quartile (Q1), +- diamond indicates mean, +- vertical bar inside of the box indicates median, +- right whisker line starts at upper quartile (Q3) and ends at `min(Q3 + 1.5IQR, maximum)`, +- right vertical bar indicates the maximum. + +With above setup it may happen that +there is a gap between the minimum marker and the beginning of the left whisker +or +there is a gap between the end of the right whisker and the maximum marker. +See examples below. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple Rain Cloud Plot for a `have` dataset: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + + data have; + g = "Aaa"; + do _N_ = 1 to 50; + x = rannor(42); + output; + end; + + g = "Bb"; + do _N_ = 1 to 120; + select (mod(_N_,9)); + when(1,2,3,4,5) x = 0.5*rannor(42)+1; + when(6,7,8) x = 0.5*rannor(42)+3; + otherwise x = 0.5*rannor(42)+5; + end; + output; + end; + + g = "C"; + do _N_ = 1 to 60; + x = 3*rannor(42)+7; + output; + end; + run; + + %RainCloudPlot(have, g, x) + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The output: +![Example 1](./baseplus_RainCloudPlot_Ex1.png) + +**EXAMPLE 2.** Rain Cloud plot for `sashelp.cars` dataset + with groups by Origin or Type + for Invoice variables: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + + %RainCloudPlot( + sashelp.cars(where=(Type ne "Hybrid")) + , Origin Type + , Invoice + , HeightPX=300 + , y2axisLevels=3 + , catLabels=("Continent of Origin", "Car Type") + , xLabels="Invoice, [$]" + , xLabelAttrs=size=12 color=Pink weight=bold + ) + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The output: +![Example 2a](./baseplus_RainCloudPlot_Ex2a.png) +![Example 2b](./baseplus_RainCloudPlot_Ex2b.png) + +**EXAMPLE 3.** Rain Cloud plot with formated groups: + and annotations. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + + data annotation; + function="text"; + label="This graph is full(*ESC*){sup '2'} of annotations!"; + drawspace="graphpercent"; + rotate=30; + anchor="center"; + textsize=32; + x1=50; + y1=50; + textcolor="red"; + justify="center"; + textweight="bold"; + width=100; + widthunit="percent"; + run; + + proc format; + value system + 1="Windows" + 2="MacOS" + 3="Linux" + ; + run; + + data test; + do system = 1 to 3; + do i = 1 to 50; + x = rannor(123)/system; + output; + end; + end; + format system system.; + run; + + + %RainCloudPlot(test, system, x + , colorslist=CX88CCEE CX44AA99 CX117733 + , formated=1 + , sganno=annotation + , sgPlotOptions=noborder + , WidthPX=1000 + , HeightPX=320 + , catAxisValueAttrs=Color=Green weight=bold + ) + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The output: +![Example 3](./baseplus_RainCloudPlot_Ex3.png) + +--- + + +--- + +## `%repeattxt()` macro ###### + +## >>> `%repeatTxt()` macro: <<< ####################### + +The repeatTxt() macro function allows to repeat `n` +times a `text` string separated by string `s=`. + +The repeatTxt() returns unquoted value [by %unquote()]. + +See examples below for the details. + +The `%repeatTxt()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%repeatTxt( + text + <,n> + <,s=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `text` - *Required*, a text to be repeated. + +2. `n` - *Required/Optional*, the number of repetitions. + If missing then set to `1`; + +* `s = %str( )` - *Optional*, it is a separator between + repeated elements. Default value is space. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple repetition of dataset name: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options mprint; +data work.test5; + set + %repeatTxt(sashelp.cars, 5) + ; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Simple repetition of data step: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options mprint; +%repeatTxt(data _null_; set sashelp.cars; run;, 3) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** "Nice" output: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repeatTxt(#,15,s=$) HELLO SAS! %repeatTxt(#,15,s=$); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Macroquote a text with commas: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%repeatTxt( + %str(proc sql; create table wh as select weight,height from sashelp.class; quit;) + ,3 +) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 5.** Empty `n` repeats `text` one time: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +options mprint; +data work.test1; + set + %repeatTxt(sashelp.cars) + ; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 6.** Dynamic "formatting": +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%macro printWork(); + %let work=%sysfunc(pathname(work)); + %put +%repeatTxt(~,%length(&work.)+5,s=)+; + %put {&=work.}; + %put +%repeatTxt(~,%length(&work.)+5,s=)+; +%mend printWork; + +%printWork() +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%splitdsintoblocks()` macro ###### + +## >>> `%splitDSIntoBlocks()` macro: <<< ####################### + +The splitDSIntoBlocks() macro allows to split the `set` dataset into blocks +of size `blockSize` in datasets: `prefix1` to `prefixN`. + +The last dataset may have less observations then the `blockSize`. + +Macro covers `BASE` engine (`v9`, `v8`, `v7`, `v6`) and `SPDE` engine datasets. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%splitDSIntoBlocks( + blockSize + <,set> + <,prefix> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `blockSize` - *Required*, the size of the block of data, + in other words number of observations in + one block of split data. + Block size must be positive integer. + +2. `set` - *Required/Optional*, the name of the dataset to split. + If empty then `&syslast.` is used. + +3. `prefix` - *Required/Optional*, the name-prefix for new datasets. + If missing then set to `part`. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Split `sashelp.class` into 5 elements datasets ABC1 to ABC4: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %splitDSIntoBlocks(5,sashelp.class,ABC) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** By default splits the `_last_` dataset into `part1` to `partN` datasets: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data lastData; + set sashelp.cars; + run; + + %splitDSIntoBlocks(123) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Works with `SPDE` engine too: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + options dlcreatedir; + libname test "%sysfunc(pathname(work))/testSPDE"; + libname test; + libname test SPDE "%sysfunc(pathname(work))/testSPDE"; + + data test.test; + set sashelp.cars; + run; + + %splitDSIntoBlocks(100,test.test,work.spde) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%splitdsintoparts()` macro ###### + +## >>> `%splitDSIntoParts()` macro: <<< ####################### + +The splitDSIntoParts() macro allows to split the `set` dataset into `parts` parts +of approximately `NOBS/parts` size in datasets: `prefix1` to `prefixN`. + +The splitDSIntoParts() macro internally runs the splitDSIntoBlocks() macro. + +Macro covers `BASE` engine (`v9`, `v8`, `v7`, `v6`) and `SPDE` engine datasets. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%splitDSIntoParts( + parts + <,set> + <,prefix> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `parts` - *Required*, the number of parts to split data into. + Number of parts must be positive integer. + +2. `set` - *Required/Optional*, the name of the dataset to split. + If empty then `&syslast.` is used. + +3. `prefix` - *Required/Optional*, the name-prefix for new datasets. + If missing then set to `part`. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Split `sashelp.cars` into 7 parts: datasets carsInParts1 to carsInParts7: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %splitDSIntoParts(7,sashelp.cars, carsInParts) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** By default splits the `_last_` dataset into `part1` to `part3` datasets: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data lastData; + set sashelp.cars; + run; + + %splitDSIntoBlocks(3) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Works with `SPDE` engine too: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + options dlcreatedir; + libname test "%sysfunc(pathname(work))/testSPDE"; + libname test; + libname test SPDE "%sysfunc(pathname(work))/testSPDE"; + + data test.test; + set sashelp.cars; + run; + + %splitDSIntoParts(3,test.test,work.spde) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%symdelglobal()` macro ###### + +## >>> `%symdelGlobal()` macro: <<< ####################### + +The `%symdelGlobal()` macro deletes all global macrovariables +created by the user. The only exceptions are read only variables +and variables the one which starts with SYS, AF, or FSP. +In that case a warning is printed in the log. + +One temporary global macrovariable `________________98_76_54_32_10_` +and a dataset, in `work` library, named `_%sysfunc(datetime(),hex7.)` +are created and deleted during the process. + +The `%symdelGlobal()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%symdelGlobal( + info +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `info` - *Optional*, default value should be empty, + if set to `NOINFO` or `QUIET` then infos and + warnings about variables deletion are suspended. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Basic use-case one. + Delete global macrovariables, info notes + and warnings are printed in the log. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let a = 1; + %let b = 2; + %let c = 3; + %let sys_my_var = 11; + %let af_my_var = 22; + %let fsp_my_var = 33; + %global / readonly read_only_x = 1234567890; + + %put _user_; + + %symdelGlobal(); + + %put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Basic use-case two. + Delete global macrovariables in quite mode + No info notes and warnings are printed in the log. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let a = 1; + %let b = 2; + %let c = 3; + %let sys_my_var = 11; + %let af_my_var = 22; + %let fsp_my_var = 33; + %global / readonly read_only_x = 1234567890; + + %put _user_; + %put *%symdelGlobal(NOINFO)*; + %put _user_; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- + + +--- + +## `%unziparch()` macro ###### + +## >>> `%unzipArch()` macro: <<< ####################### + +The unzipArch() macro allows to unzip content of a ZIP archive. +Macro is OS independent, the `XCMD` option is not required. + +The `dlCreateDir` option is used under the hood. + +Content of unzipped archive can be listed in the log. + +Source files can be deleted after decompression. +Errors of decompression and are reported. If any occur +the deletion is suspended. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%unzipArch( + archName + <,path=> + <,pathRef=> + <,target=> + <,targetRef=> + <,list=> + <,clean=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `archName` - *Required*, name of the ZIP archive to be extracted. + Name should be full, i.e., with the extension! + +* `path=` - *Optional*, a path pointing to zipped file location. + The path should be provided unquoted. + Default value is `WORK` location. + +* `pathRef=` - *Optional*, a fileref to path pointing to zipped file location. + The `path`, if not null, has precedense over the `pathRef`. + +* `target=` - *Optional*, a path pointing to target location where + files will be extracted. + The path should be provided unquoted. + Default value is `WORK` location. + +* `target=` - *Optional*, a fileref to path pointing to target location where + files will be extracted. + The `target`, if not null, has precedense over the `targetRef`. + +* `list = 0` - *Optional*, default value is `0`, + indicates if zip content should be listed in the log. + `1` means *yes*, `0` means *no*. + +* `clean = 0` - *Optional*, default value is `0`, + indicates if zip file should be deleted after unzipping. + `1` means *yes*, `0` means *no*. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Unzip compressed archive. Example requires the `basePlus` package. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +filename arch ZIP "%workPath()/testArch.zip"; + +data _null_; + file arch(abc/test1.txt); + put "text for test file 1"; +data _null_; + file arch(abc/subdir/test2.txt); + put "text for test file 2"; +data _null_; + file arch(abc/subdir/test3.txt); + put "text for test file 3"; +run; + +%unzipArch( + testArch.zip +, path = %workPath() +, target = %workPath() +, list=1 +); + + + +filename pR "%workPath()"; + +%unzipArch( + testArch.zip +, pathRef = pR +, targetRef = pR +, clean=1 +); + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%unziplibrary()` macro ###### + +## >>> `%unzipLibrary()` macro: <<< ####################### + +The unzipLibrary() macro allows to unzip content of a SAS library. +It is a *counterpart* to the `%zipLibrary()` macro and is *not* intended to work +with zip files generated by other software (though it may in some cases). + +Files can be unzipped from a single file +or from multiple files (named e.g. "dataset.sas7bdat.zip"). +If a file is indexed also the index file is unzipped. + +Source files can be deleted after decompression. + +Status of decompression and processing time is reported. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%unzipLibrary( + path + <,zip=> + <,mode=> + <,clean=> + <,libOut=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `path` - *Required*, a path pointing to zipped file(s) location. + The path should be unquoted, e.g. `%unzipLibrary(/some/dir, ...)`. + +* `zip =` - *Optional*, When `mode=S` a name of the + zip file containing SAS files to be unzipped. + +* `mode = S` - *Optional*, default value is `S`, + indicates mode of decompression + read from a single zip file (`SINGLE/S`) + or from multiple files (`MULTI/M`) + +* `clean = 0` - *Optional*, default value is `0`, + should zip files be deleted after unzipping? + `1` means *yes*, `0` means *no*. + +* `libOut =` - *Optional*, default value is empty, + output library for a single zip file + decompression. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Generate data: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +options dlcreatedir; + libname test1 "%sysfunc(pathname(work))/test1"; + libname test2 "%sysfunc(pathname(work))/test2"; + libname test3 (test1 test2); + libname test4 "%sysfunc(pathname(work))/test4"; +options nodlcreatedir; + +%put %sysfunc(pathname(test3)); +%put %sysfunc(pathname(test4)); + +data + test1.A(index=(model)) + test1.B + test2.C + test2.D(index=(model make io=(invoice origin))) +; + set sashelp.cars; +run; + +data test1.B2 / view=test1.B2; + set test1.B; + output; + output; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Use data from Example 1. + First zip content of the `test3` library + to `test4` location into one zip file + and delete source files. + Next unzip `test3.zip` library into the + `test4` location and delete the zip file. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(test3, clean=1, libOut=test4) + + +%unzipLibrary(%sysfunc(pathname(test4)), zip=test3, clean=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Use data from Example 1. + First zip content of the `test1` library + into multiple zip files and delete source files. + Next unzip `*.zip` files in `test1` + location and delete zipped files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(test1, mode=M, clean=1) + + +%unzipLibrary(%sysfunc(pathname(test1)), mode=M, clean=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** First zip content of the `sashelp` library + into `work` library. + Next unzip `sashelp.zip` file in `work` + location and delete zip file. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(sashelp, mode=S, clean=0, libOut=work) + + +%unzipLibrary(%sysfunc(pathname(work)), zip=sashelp, mode=S, clean=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +--- + +## `%ziparch()` macro ###### + +## >>> `%zipArch()` macro: <<< ####################### + +The zipArch() macro allows to ZIP content of a directory. +Macro is OS independent, the `XCMD` option is not required. + +Content of zipped archive can be listed in the log. + +Errors of decompression and are reported. + +Macro **does not** include hidden files. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%zipArch( + archName + ,path = + <,pathRef=> + <,target=> + <,targetRef=> + <,list=> + <,overwrite=> + <,dropList=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `archName` - *Required*, name of the archive to be generated. + Name should be full, i.e., with the extension! + +2. `path=` - *Required/Optional*, location of a directory to ZIP. + The path should be provided unquoted. + Has priority over the `pathRef` parameter. + +* `pathRef=` - *Required/Optional*, fileref to location of a directory to ZIP. + The reference **has** to be pointing to single directory. + If provided with `path` - the `path` takes + priority over the `pathRef` parameter. + +* `target=` - *Optional*, a path pointing to target location where + the archive will be generated. + The path should be provided unquoted. + Default value is `WORK` location. + Has priority over the `targetRef` parameter. + +* `targetRef=` - *Optional*, fileref to a path pointing to target location + where the archive will be generated. + The reference **has** to be pointing to single directory. + If provided with `target` - the `target` takes + priority over the `targetRef` parameter. + +* `list = 0` - *Optional*, default value is `0`, + indicates if zip content should be listed in the log. + `1` means *yes*, `0` means *no*. + +* `overwrite = 0` - *Optional*, default value is `0`, + indicates if existing archive file should be overwritten. + `1` means *yes*, `0` means *no*. + +* `overwrite = 1` - *Technical*, default value is `1`, + indicates if the "to-be-zipped-files-list" + data set should be deleted. + `1` means *yes*, `0` means *no*. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Zip a directory . Example requires the `basePlus` package. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +options dlCreateDir; +libname arch1 "%workPath()/testArch1"; +libname arch2 "%workPath()/testArch2"; + +filename arch1 "%workPath()/testArch1"; + +data _null_; + file arch1(test1.txt); + put "text for test file 1"; +data _null_; + file arch1(test2.txt); + put "text for test file 2"; +data _null_; + file arch1(test3.txt); + put "text for test file 3"; +run; + +data arch1.class(index=(name)); + set sashelp.class; +run; +data arch1.cars(index=(model)); + set sashelp.cars; +run; + + + +%zipArch( + archName1.zip +, path = %workPath()/testArch1 +, list = 1 +, overwrite = 1 + +) + +%zipArch( + archName2.zip +, pathRef = arch1 +, target = %workPath()/testArch2 +, list = 1 +, overwrite = 1 +) + + +%unzipArch( + archName2.zip +, path = %workPath()/testArch2 +, target = %workPath()/testArch2 +, clean=1 +, list=1 +); + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%zipevalf()` macro ###### + +## >>> `%zipEvalf()` macro: <<< ####################### + +The zipEvalf() and QzipEvalf() macro functions +allow to use a function on elements of pair of +space separated lists. + +For two space separated lists of text strings 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 lists is shorter then elements are "reused" starting +from the beginning. + +The zipEvalf() returns unquoted value [by %unquote()]. +The QzipEvalf() returns quoted value [by %superq()]. + +See examples below for the details. + +The `%zipEvalf()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%zipEvalf( + first + ,second + <,function=> + <,operator=> + <,argBf=> + <,argMd=> + <,argAf=> + <,format=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**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. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple concatenation of elements: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y); +%put &=x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Shorter list is "reused": +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let x = %zipEvalf(1 2 3 4 5 6, a b c); +%put &=x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Use of the `operator=`, shorter list is "reused": +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let y = %zipEvalf(1 2 3 4 5 6, 100 200, operator = +); +%put &=y; + +%let z = %zipEvalf(1 2 3 4 5 6 8 9 10, 1 2 3 4 5 6 8 9 10, operator = **); +%put &=z; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Format result: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let x = %zipEvalf(1 2 3 4 5 6, q w e r t y, format=$upcase.); +%put &=x; + +%put * +%zipEvalf( + ą ż ś ź ę ć ń ó ł +,Ą Ż Ś Ź Ę Ć Ń Ó Ł +,format = $brackets. +) +*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Use with macrovariables: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let abc = 10 100 1000; +%put * +%zipEvalf( +%str(1 2 3 4 5 6 7 8 9) +,&abc. +,function = sum +) +*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** If one of elements is empty: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put * +%zipEvalf( + abc efg +, +) +*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** Use of the `function=`, shorter list is "reused": +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put * +%zipEvalf( + a b c +,efg +,function = catx +,argBf = %str(,) +,format = $brackets. +) +*; + +%put * +%zipEvalf( + a b c +,efg +,function = catx +,argBf = %str( ) +,format = $upcase. +) +*; + +%put * +%zipEvalf( + %str(! @ # $ [ ] % ^ & * ) +,1 2 3 4 5 6 7 8 9 +,function = catx +,argBf = %str( ) +,format = $quote. +) +*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 8.** Use inside resolve: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; +z = resolve(' +%zipEvalf( + %nrstr(! @ # $ [ ] % ^ & *) +,1 2 3 4 5 6 7 8 9 +,function = catx +,argBf = %str(.) +,format = $quote. +)'); +put z=; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 9.** Use in data step: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data test; + %zipEvalf( + a b c d e f g + ,1 2 3 4 5 6 7 + ,function = catx + ,argBf = = + ,format = $semicolon. + ) +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 10.** With 9.4M6 hashing() function: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %zipEvalf(MD5 SHA1 SHA256 SHA384 SHA512 CRC32, abcd, function = HASHING); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 11.** Use middle argument: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%let x = %zipEvalf(1 2 3 4 5 6, 2020, argMd=5, function=MDY, format=date11.); +%put &=x; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%ziplibrary()` macro ###### + +## >>> `%zipLibrary()` macro: <<< ####################### + +The zipLibrary() macro allows to zip content of a SAS library. + +Files can be zipped into a single file (named as the input library) +or into multiple files (named as "dataset.sas7bdat.zip"). +If a file is indexed also the index file is zipped. + +Source files can be deleted after compression. + +Status of compression and processing time is reported. + +See examples below for the details. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary( + lib + <,mode=> + <,clean=> + <,libOut=> + <,compression=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `lib` - *Required*, a name of the library to be zipped. + Must be a valid SAS V7, V8, or V9 library. + + +* `mode = S` - *Optional*, default value is `S`, + indicates mode of compression + generates single zip file (`SINGLE/S`) + or multiple files (`MULTI/M`) + +* `clean = 0` - *Optional*, default value is `0`, + should datasets be deleted after zipping? + `1` means *yes*, `0` means *no*. + +* `libOut =` - *Optional*, default value is empty, + output library for a single zip file. + +* `compression =` - *Optional*, default value is `6`, + specifies the compression level + `0` to `9`, where `0` is no compression + and `9` is maximum compression. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Generate data: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +options dlcreatedir; + libname test1 "%sysfunc(pathname(work))/test1"; + libname test2 "%sysfunc(pathname(work))/test2"; + libname test3 (test1 test2); + libname test4 "%sysfunc(pathname(work))/test4"; +options nodlcreatedir; + +%put %sysfunc(pathname(test3)); +%put %sysfunc(pathname(test4)); + +data + test1.A(index=(model)) + test1.B + test2.C + test2.D(index=(model make io=(invoice origin))) +; + set sashelp.cars; +run; + +data test1.B2 / view=test1.B2; + set test1.B; + output; + output; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Zip content of test3 library + into the same location in one zip file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(test3) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Zip content of test3 library + into the same location in multiple zip files: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(test3, mode=MULTI) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Zip content of test3 library + with maximum compression level + into different location in one zip file + and delete source files: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%zipLibrary(test3, clean=1, libOut=test4, compression=9) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +--- + +## `$bool.` format/informat ###### + +## >>> `bool.` format: <<< ####################### + +The **bool** format returns: +*zero* for 0 or missing, +*one* for other values. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 & 4.2, boolean)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), bool.)`] + +--- + + +--- + +## `$boolz.` format/informat ###### + +## >>> `boolz.` format: <<< ####################### + +The **boolz** format returns: +*zero* for 0 or missing, +*one* for other values. + +*Fuzz* value is 0. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 & 4.2, boolean)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), boolz.)`] + +--- + + +--- + +## `$ceil.` format/informat ###### + +## >>> `ceil.` format: <<< ####################### + +The **ceil** format is a "wrapper" for the `ceil()` function. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 + 4.2, ceil)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), ceil.)`] + +--- + + +--- + +## `$floor.` format/informat ###### + +## >>> `floor.` format: <<< ####################### + +The **floor** format is a "wrapper" for the `floor()` function. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 + 4.2, floor)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), floor.)`] + +--- + + +--- + +## `$int.` format/informat ###### + +## >>> `int.` format: <<< ####################### + +The **int** format is a "wrapper" for the `int()` function. + +### EXAMPLES AND USECASES: #################################################### + +It allows for a %sysevalf()'ish +conversion-type [i.e. `%sysevalf(1.7 + 4.2, integer)`] +inside the `%sysfunc()` [e.g. `%sysfunc(aFunction(), int.)`] + +--- + + +--- + +## `arrfill()` function ###### + +## >>> `arrFill()` subroutine: <<< ####################### + +The **arrFill()** subroutine is a wrapper +for the Call Fillmatrix() [a special FCMP subroutine]. + +A numeric array is filled with selected numeric value, e.g. + +for array `A = [. . . .]` the subroutine +`call arrFill(42, A)` returns `A = [42 42 42 42]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrFill(N ,A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `N` - Numeric value. + +2. `A` - Numeric array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + array X[*] a b c; + + put "before: " (_all_) (=); + call arrFill(42, X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrfillc()` function ###### + +## >>> `arrFillC()` subroutine: <<< ####################### + +The **arrFillC()** subroutine fills +a character array with selected character value, e.g. + +for array `A = [" ", " ", " "]` the subroutine +`call arrFillC("B", A)` returns `A = ["B", "B", "B"]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrFillC(C ,A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `C` - Character value. + +2. `A` - Character array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + array X[*] $ a b c; + + put "before: " (_all_) (=); + call arrFillC("ABC", X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmissfill()` function ###### + +## >>> `arrMissFill()` subroutine: <<< ####################### + +The **arrMissFill()** subroutine fills +all missing values (i.e. less or equal than `.Z`) +of a numeric array with selected numeric value, e.g. + +for array `A = [1 . . 4]` the subroutine +`call arrMissFill(42, A)` returns `A = [1 42 42 4]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissFill(N ,A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `N` - Numeric value. + +2. `A` - Numeric array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + input a b c; +cards4; +1 . 3 +. 2 . +. . 3 +;;;; +run; + +data _null_; + set have ; + array X[*] a b c; + + put "before: " (_all_) (=); + call arrMissFill(42, X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmissfillc()` function ###### + +## >>> `arrMissFillC()` subroutine: <<< ####################### + +The **arrMissFillC()** subroutine fills +all missing values of a character array +with selected character value, e.g. + +for array `A = ["A", " ", "C"]` the subroutine +`call arrMissFillC("B", A)` returns `A = ["A", "B", "C"]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissFillC(C, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `C` - Character value. + +2. `A` - Character array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + infile cards dsd dlm="," missover; + input (a b c) (: $ 1.); +cards4; +A, ,C + ,B, + , ,C +;;;; +run; + +data _null_; + set have ; + array X[*] $ a b c; + + put "before: " (_all_) (=); + call arrMissFillC("X", X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmisstoleft()` function ###### + +## >>> `arrMissToLeft()` subroutine: <<< ####################### + +The **arrMissToLeft()** subroutine shifts +all non-missing (i.e. greater than `.Z`) +numeric elements to the right side of an array +and missing values to the left, e.g. + +for array `A = [1 . 2 . 3]` the subroutine +`call arrMissToLeft(A)` returns `A = [. . 1 2 3]` + +All missing values are replaced with the dot (`.`) + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissToLeft(A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Numeric array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + input a b c; +cards4; +1 . 3 +. 2 . +. . 3 +;;;; +run; + +data _null_; + set have ; + array X[*] a b c; + + put "before: " (_all_) (=); + call arrMissToLeft(X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmisstoleftc()` function ###### + +## >>> `arrMissToLeftC()` subroutine: <<< ####################### + +The **arrMissToLeftC()** subroutine shifts +all non-missing (i.e. different than empty string) +character elements to the right side of an array +and all missing values to the left, e.g. + +for array `A = ["A", " ", "B", " ", "C"]` the subroutine +`call arrMissToLeftC(A)` returns `A = [" ", " ", "A", "B", "C"]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissToLeftC(A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Character array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + infile cards dsd dlm="," missover; + input (a b c) (: $ 1.); +cards4; +A, ,C + ,B, + , ,C +;;;; +run; + +data _null_; + set have ; + array X[*] $ a b c; + + put "before: " (_all_) (=); + call arrMissToLeftC(X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmisstoright()` function ###### + +## >>> `arrMissToRight()` subroutine: <<< ####################### + +The **arrMissToRight()** subroutine shifts +all non-missing (i.e. greater than `.Z`) +numeric elements to the left side of an array +and missing values to the right, e.g. + +for array `A = [1 . 2 . 3]` the subroutine +`call arrMissToRight(A)` returns `A = [1 2 3 . .]` + +All missing values are replaced with the dot (`.`) + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissToRight(A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Numeric array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + input a b c; +cards4; +1 . 3 +. 2 . +. . 3 +;;;; +run; + +data _null_; + set have ; + array X[*] a b c; + + put "before: " (_all_) (=); + call arrMissToRight(X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `arrmisstorightc()` function ###### + +## >>> `arrMissToRightC()` subroutine: <<< ####################### + +The **arrMissToRightC()** subroutine shifts +all non-missing (i.e. different than empty string) +character elements to the left side of an array +and missing values to the right, e.g. + +for array `A = ["A", " ", "B", " ", "C"]` the subroutine +`call arrMissToRightC(A)` returns `A = ["A", "B", "C", " ", " "]` + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +call arrMissToRightC(A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Character array. + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data have; + infile cards dsd dlm="," missover; + input (a b c) (: $ 1.); +cards4; +A, ,C + ,B, + , ,C +;;;; +run; + +data _null_; + set have ; + array X[*] $ a b c; + + put "before: " (_all_) (=); + call arrMissToRightC(X); + put "after: " (_all_) (=); + +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `bracketsc()` function ###### + +## >>> `bracketsC()` function: <<< ####################### + +The **bracketsC()** function is internal function used by the *brackets* format. +Returns character value of length 32767. + +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~sas +bracketsC(X) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `X` - Character value. + +--- + +--- + +## `bracketsn()` function ###### + +## >>> `bracketsN()` function: <<< ####################### + +The **bracketsN()** function is internal function used by the *brackets* format. +Returns character value of length 34. + +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~sas +bracketsN(X) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `X` - Numeric value. + +--- + +--- + +## `catxfc()` function ###### + +## >>> `catXFc()` function: <<< ####################### + +The **catXFc()** function is a wrapper +of the `catX()` function but with ability +to format character values. + +For array `A = ["a", " ", "c"]` the +`catXFc("upcase.", "*", A)` returns `"A*C"`. + +If format does not handle nulls they are ignored. + +*Caution!* Array parameters to function calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +catXFc(format, delimiter, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `format` - A name of the *character* format to be used. + +2. `delimiter` - A delimiter string to be used. + +3. `A` - Character array + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + t = "t"; + u = " "; + v = "v"; + + array b[*] t u v; + + length s $ 17; + s = catXFc("upcase.", "*", B); + put (_all_) (=); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `catxfi()` function ###### + +## >>> `catXFi()` function: <<< ####################### + +The **catXFi()** function is a wrapper +of the `catX()` function but with ability +to format numeric values but +IGNORES missing values (i.e. `._`, `.`, `.a`, ..., `.z`). + +For array `A = [0, ., 2]` the +`catXFi("date9.", "#", A)` returns +`"01JAN1960#03JAN1960"` + +*Caution!* Array parameters to function calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +catXFi(format, delimiter, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `format` - A name of the *numeric* format to be used. + +2. `delimiter` - A delimiter string to be used. + +3. `A` - Numeric array + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + x = 1; + y = .; + z = 3; + + array a[*] x y z; + + length s $ 17; + s = catXFi("z5.", "#", A); + put (_all_) (=); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `catxfj()` function ###### + +## >>> `catXFj()` function: <<< ####################### + +The **catXFj()** function is a wrapper +of the catX() function but with ability +to format character values. + +For array `A = ["a", " ", "c"]` the +`catXFj("upcase.", "*", A)` returns `"A**C"` + +If format does not handle nulls they are +printed as an empty string. + +*Caution!* Array parameters to function calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +catXFj(format, delimiter, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `format` - A name of the *character* format to be used. + +2. `delimiter` - A delimiter string to be used. + +3. `A` - Character array + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + t = "t"; + u = " "; + v = "v"; + + array b[*] t u v; + + length s $ 17; + s = catXFj("upcase.", "*", B); + put (_all_) (=); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `catxfn()` function ###### + +## >>> `catXFn()` function: <<< ####################### + +The **catXFn()** function is a wrapper +of the `catX()` function but with ability +to format numeric values. + +For array `A = [0, 1, 2]` the +`catXFn("date9.", "#", A)` returns +`"01JAN1960#02JAN1960#03JAN1960"` + +*Caution!* Array parameters to function calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +catXFn(format, delimiter, A) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `format` - A name of the *numeric* format to be used. + +2. `delimiter` - A delimiter string to be used. + +3. `A` - Numeric array + + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + x = 1; + y = .; + z = 3; + + array a[*] x y z; + + length s $ 17; + s = catXFn("z5.", "#", A); + put (_all_) (=); +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `deldataset()` function ###### + +## >>> `delDataset()` function: <<< ####################### + +The **delDataset()** function is a "wrapper" +for the `Fdelete()` function. +`delDataset()` function uses a text string with +a dataset name as an argument. + +Function checks for `*.sas7bdat`, `*.sas7bndx`, +and `*.sas7bvew` files and delete them. +Return code of 0 means dataset was deleted. + +For compound library files are +deleted from _ALL_ locations! + + +*Note:* +Currently only the BASE SAS engine datasets/views are deleted. + +Tested on Windows and Linux. Not tested on Z/OS. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +delDataset(lbds_) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `lbds_` - *Required*, character argument containing + name of the dataset/view to be deleted. + The `_last_` special name is honored. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data TEST1 TEST2(index=(x)); + x = 17; + run; + + data TEST3 / view=TEST3; + set test1; + run; + + data _null_; + p = delDataset("WORK.TEST1"); + put p=; + + p = delDataset("TEST2"); + put p=; + + p = delDataset("WORK.TEST3"); + put p=; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data TEST4; + x=42; + run; + data _null_; + p = delDataset("_LAST_"); + put p=; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 3.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + options dlcreatedir; + libname user "%sysfunc(pathname(work))/user"; + + data TEST5; + x=42; + run; + + data _null_; + p = delDataset("test5"); + put p=; + run; + + libname user clear; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 4.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data TEST6; + x=42; + run; + + %put *%sysfunc(delDataset(test6))*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 5.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + options dlcreatedir; + libname L1 "%sysfunc(pathname(work))/L)1"; + libname L2 "%sysfunc(pathname(work))/L(2"; + libname L3 "%sysfunc(pathname(work))/L'3"; + + data L1.TEST7 L2.TEST7 L3.TEST7; + x=42; + run; + + libname L12 ("%sysfunc(pathname(work))/L(1" "%sysfunc(pathname(work))/L)2"); + libname L1L2 (L2 L3); + + %put *%sysfunc(delDataset(L12.test7))*; + %put *%sysfunc(delDataset(L1L2.test7))*; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `semicolonc()` function ###### + +## >>> `semicolonC()` function: <<< ####################### + +The **semicolonC()** function is internal function used by the *semicolon* format. +Returns character value of length 32767. + +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~sas +semicolonC(X) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `X` - Character value. + +--- + + +--- + +## `semicolonn()` function ###### + +## >>> `semicolonN()` function: <<< ####################### + +The **semicolonN()** function is internal function used by the *semicolon* format. +Returns character value of length 33. + +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~sas +semicolonN(X) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `X` - Numeric value. + +--- + + +--- + +## `$brackets.` format/informat ###### + +## >>> `brackets.` format: <<< ####################### + +The **brackets** format adds brackets around a text or a number. +Leading and trailing spaces are dropped before adding brackets. + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + input x; + if x < 0 then put x= brackets.; + else put x= best32.; +cards; +2 +1 +0 +-1 +-2 +; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `$semicolon.` format/informat ###### + +## >>> `semicolon.` format: <<< ####################### + +The **semicolon** format adds semicolon after text or number. +Leading and trailing spaces are dropped before adding semicolon. + +### EXAMPLES AND USECASES: #################################################### + +**Example 1.** + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data _null_; + x = 1; + y = "A"; + put x= semicolon. y= $semicolon.; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `qsortincbyprocproto()` proto ###### + +## >>> `qsortInCbyProcProto()` proto function: <<< ####################### + +The **qsortInCbyProcProto()** is external *C* function, +this is the implementation of the *Quick Sort* algorithm. + +The function is used **internally** by +functions in the *BasePlus* package. + +Asumptions: +- smaller subarray is sorted first, +- subarrays of *size < 11* are sorted by *insertion sort*, +- pivot is selected as median of low index value, + high index value, and (low+high)/2 index value. + +`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`
+`!CAUTION! Sorted array CANNOT contains SAS missing values !`
+`!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`
+ +### SYNTAX: ################################################################### + +The basic syntax is the following: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +qsortInCbyProcProto(arr, low, high) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `arr` - An array of double type to be sorted. + +2. `low` - An integer low index of starting position (from which the sorting is done). + +3. `high` - An integer high index of ending position (up to which the sorting is done). + + +### REFERENCES: #################################################### + +*Reference 1.* + +Insertion sort for arrays smaller then 11 elements: + +Based on the code from the following WikiBooks page [2020.08.14]: + +[https://pl.wikibooks.org/wiki/Kody_%C5%BAr%C3%B3d%C5%82owe/Sortowanie_przez_wstawianie](https://pl.wikibooks.org/wiki/Kody_%C5%BAr%C3%B3d%C5%82owe/Sortowanie_przez_wstawianie) + + +*Reference 2.* + +Iterative Quick Sort: + +Based on the code from the following pages [2020.08.14]: + +[https://www.geeksforgeeks.org/iterative-quick-sort/](https://www.geeksforgeeks.org/iterative-quick-sort/) + +[https://www.geeksforgeeks.org/c-program-for-iterative-quick-sort/](https://www.geeksforgeeks.org/c-program-for-iterative-quick-sort/) + +--- + + +--- + +## `frommissingtonumberbs()` function ###### + +## >>> `fromMissingToNumberBS()` function: <<< ####################### + +The **fromMissingToNumberBS()** function +gets numeric missing value or a number +as an argument and returns an integer +from 1 to 29. + +For a numeric missing argument +the returned values are: +- 1 for `._` +- 2 for `.` +- 3 for `.a` +- ... +- 28 for `.z` and +- 29 for *all other*. + +The function is used **internally** by +functions in the *BasePlus* package. + +For *missing value arguments* the function +is an inverse of the `fromNumberToMissing()` function. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +fromMissingToNumberBS(x) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `x` - A numeric missing value or a number. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data _null_; + do x = ._, ., .a, .b, .c, 42; + y = fromMissingToNumberBS(x); + put x= y=; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `fromnumbertomissing()` function ###### + +## >>> `fromNumberToMissing()` function: <<< ####################### + +The **fromNumberToMissing()** function +gets a number as an argument and returns +a numeric missing value or zero. + +For a numeric argument +the returned values are: +- `._` for 1 +- `.` for 2 +- `.a` for 3 +- ... +- `.z` for 28 and +- `0` for *all other*. + +The function is used **internally** by +functions in the *BasePlus* package. + +For arguments 1,2,3, ..., and 28 the function +is an inverse of the `fromMissingToNumberBS()` function. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +fromNumberToMissing(x) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `x` - A numeric value. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data _null_; + do x = 1 to 29; + y = fromNumberToMissing(x); + put x= y=; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `quicksort4notmiss()` function ###### + +## >>> `quickSort4NotMiss()` subroutine: <<< ####################### + +The **quickSort4NotMiss()** subroutine is an alternative to the +`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) +when memory used by `call sortn()` may be an issue. +For smaller arrays the memory footprint is not significant. + +The subroutine is based on an iterative quick sort algorithm +implemented in the `qsortInCbyProcProto()` *C* prototype function. + + +**Caution 1!** Array _CANNOT_ contains missing values! + +**Caution 2!** Array parameters to subroutine calls must be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +call quickSort4NotMiss(A) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Argument is a 1-based array of NOT missing numeric values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** For session with 8GB of RAM, + array of size 250'000'000 with values in range + from 0 to 99'999'999 and _NO_ missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + test[_N_] = int(100000000*rand("uniform")); + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSort4NotMiss (test); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** Resources comparison for + session with 8GB of RAM. + + Array of size 250'000'000 with random values + from 0 to 999'999'999 and _NO_ missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 8.82s + memory 1'953'470.62k + OS Memory 1'977'436.00k + + Call quickSort4NotMiss: + Sorting time 66.92s + Memory 1'954'683.06k + OS Memory 1'977'436.00k + + Call quickSortLight: + Sorting time 70.98s + Memory 1'955'479.71k + OS Memory 1'977'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `quicksorthash()` function ###### + +## >>> `quickSortHash()` subroutine: <<< ####################### + +The **quickSortHash()** subroutine is an alternative to the +`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) +when memory used by `call sortn()` may be an issue. +For smaller arrays the memory footprint is not significant. + +The subroutine is based on an iterative quick sort algorithm +implemented in the `qsortInCbyProcProto()` *C* prototype function. + +The number of "sparse distinct data values" is set to `100'000` to +use the hash sort instead of the quick sort. + E.g. when number of unique values for sorting is less then + 100'000 then an ordered hash table is used to store the data + and their count and sort them. + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +*Note!* Due to improper memory reporting/releasing for hash + tables in FCMP procedure the reported memory used after running + the function may not be in line with the RAM memory required + for processing. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +call quickSortHash(A) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Argument is a 1-based array of numeric values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 99'999'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(100000000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortHash (test); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 9'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(10000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortHash (test); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 3.** Resources comparison for + session with 8GB of RAM + + A) Array of size 10'000'000 with + random values from 0 to 9'999 range (sparse) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 0.61s + Memory 78'468.50k + OS Memory 101'668.00k + + Call sortn: + Sorting time 0.87s + Memory 1'120'261.53k + OS Memory 1'244'968.00k + + Call quickSortHash: + Sorting time 6.76s + Memory 1'222'242.75k(*) + OS Memory 1'402'920.00k(*) + + Call quickSortLight: + Sorting time 23.45s + Memory 80'527.75k + OS Memory 101'924.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + B) Array of size 10'000'000 with + random values from 0 to 99'999'999 range (dense) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 0.6s + Memory 78'463.65k + OS Memory 101'924.00k + + Call sortn: + Sorting time 1.51s + Memory 1'120'253.53k + OS Memory 1'244'968.00k + + Call quickSortHash: + Sorting time 6.28s + Memory 1'222'241.93k(*) + OS Memory 1'402'920.00k(*) + + Call quickSortLight: + Sorting time 0.78s + Memory 80'669.28k + OS Memory 102'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + C) Array of size 250'000'000 with + random values from 0 to 999'999'999 range (dense) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 15.34s + memory 1'953'471.81k + OS Memory 1'977'436.00k + + Call sortn: + FATAL: Insufficient memory to execute DATA step program. + Aborted during the COMPILATION phase. + ERROR: The SAS System stopped processing this step + because of insufficient memory. + + Call quickSortHash: + Sorting time 124.68s + Memory 7'573'720.34k(*) + OS Memory 8'388'448.00k(*) + + Call quickSortLight: + Sorting time 72.41s + Memory 1'955'520.78k + OS Memory 1'977'180.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + D) Array of size 250'000'000 with + random values from 0 to 99'999 range (sparse) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 16.07 + Memory 1'953'469.78k + OS Memory 1'977'180.00k + + Call sortn: + FATAL: Insufficient memory to execute DATA step program. + Aborted during the COMPILATION phase. + ERROR: The SAS System stopped processing this step + because of insufficient memory. + + Call quickSortHash: + Sorting time 123.5s + Memory 7'573'722.03k + OS Memory 8'388'448.00k + + Call quickSortLight: + Sorting time 1'338.25s + Memory 1'955'529.90k + OS Memory 1'977'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +(*) When using hash tables in `Proc FCMP` the RAM + usage is not indicated properly. The memory + allocation is reported up to the session limit + and then reused if needed. The really required + memory is in fact much less then reported. + +--- + + +--- + +## `quicksorthashsddv()` function ###### + +## >>> `quickSortHashSDDV()` subroutine: <<< ####################### + +The **quickSortHashSDDV()** subroutine is an alternative to the +`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) +when memory used by `call sortn()` may be an issue. +For smaller arrays the memory footprint is not significant. + +The subroutine is based on an iterative quick sort algorithm +implemented in the `qsortInCbyProcProto()` *C* prototype function. + +The number of "sparse distinct data values" (argument `SDDV`) may +be adjusted to use the hash sort instead of the quick sort. + E.g. when number of unique values for sorting is less then + some *N* then an ordered hash table is used to store the data + and their count and sort them. + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +*Note!* Due to improper memory reporting/releasing for hash + tables in FCMP procedure the report memory used after running + the function may not be in line with the RAM memory required + for processing. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +call quickSortHashSDDV(A, SDDV) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Argument is a 1-based array of numeric values. + +2. `SDDV` - A number of distinct data values, e.g. 100'000. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 99'999'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(100000000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortHashSDDV (test, 2e4); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 9'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(10000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortHashSDDV (test, 2e4); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `quicksortlight()` function ###### + +## >>> `quickSortLight()` subroutine: <<< ####################### + +The **quickSortLight()** subroutine is an alternative to the +`CALL SORTN()` subroutine for 1-based big arrays (i.e. `> 10'000'000` elements) +when memory used by `call sortn()` may be an issue. +For smaller arrays the memory footprint is not significant. + +The subroutine is based on an iterative quick sort algorithm +implemented in the `qsortInCbyProcProto()` *C* prototype function. + +*Caution!* Array parameters to subroutine calls *must* be 1-based. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +call quickSortLight(A) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `A` - Argument is a 1-based array of numeric values. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** For session with 8GB of RAM + Array of size 250'000'000 with values in range + from 0 to 99'999'999 and around 10% of various + missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let size = 250000000; + options fullstimer; + + data _null_; + array test[&size.] _temporary_ ; + + array m[0:27] _temporary_ + (._ . .A .B .C .D .E .F .G .H .I .J .K .L + .M .N .O .P .Q .R .S .T .U .V .W .X .Y .Z); + + t = time(); + call streaminit(123); + do _N_ = &size. to 1 by -1; + _I_ + 1; + if rand("uniform") > 0.1 then test[_I_] = int(100000000*rand("uniform")); + else test[_I_] = m[mod(_N_,28)]; + end; + t = time() - t; + put "Array population time: " t; + + put "First 50 elements before sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + + t = time(); + call quickSortLight (test); + t = time()-t; + put "Sorting time: " / t=; + + put; put "First 50 elements after sorting:"; + do _N_ = 1 to 20; + put test[_N_] = @; + end; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**Example 2.** Resources comparison for + session with 8GB of RAM. + + Array of size 250'000'000 with random values + from 0 to 999'999'999 and _NO_ missing values. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 8.82s + memory 1'953'470.62k + OS Memory 1'977'436.00k + + Call quickSort4NotMiss: + Sorting time 66.92s + Memory 1'954'683.06k + OS Memory 1'977'436.00k + + Call quickSortLight: + Sorting time 70.98s + Memory 1'955'479.71k + OS Memory 1'977'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Example 3.** Resources comparison for + session with 8GB of RAM + + A) Array of size 10'000'000 with + random values from 0 to 9'999 range (sparse) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 0.61s + Memory 78'468.50k + OS Memory 101'668.00k + + Call sortn: + Sorting time 0.87s + Memory 1'120'261.53k + OS Memory 1'244'968.00k + + Call quickSortHash: + Sorting time 6.76s + Memory 1'222'242.75k(*) + OS Memory 1'402'920.00k(*) + + Call quickSortLight: + Sorting time 23.45s + Memory 80'527.75k + OS Memory 101'924.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + B) Array of size 10'000'000 with + random values from 0 to 99'999'999 range (dense) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 0.6s + Memory 78'463.65k + OS Memory 101'924.00k + + Call sortn: + Sorting time 1.51s + Memory 1'120'253.53k + OS Memory 1'244'968.00k + + Call quickSortHash: + Sorting time 6.28s + Memory 1'222'241.93k(*) + OS Memory 1'402'920.00k(*) + + Call quickSortLight: + Sorting time 0.78s + Memory 80'669.28k + OS Memory 102'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + C) Array of size 250'000'000 with + random values from 0 to 999'999'999 range (dense) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 15.34s + memory 1'953'471.81k + OS Memory 1'977'436.00k + + Call sortn: + FATAL: Insufficient memory to execute DATA step program. + Aborted during the COMPILATION phase. + ERROR: The SAS System stopped processing this step + because of insufficient memory. + + Call quickSortHash: + Sorting time 124.68s + Memory 7'573'720.34k(*) + OS Memory 8'388'448.00k(*) + + Call quickSortLight: + Sorting time 72.41s + Memory 1'955'520.78k + OS Memory 1'977'180.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + D) Array of size 250'000'000 with + random values from 0 to 99'999 range (sparse) + and around 10% of missing data. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + Array: + Population time 16.07 + Memory 1'953'469.78k + OS Memory 1'977'180.00k + + Call sortn: + FATAL: Insufficient memory to execute DATA step program. + Aborted during the COMPILATION phase. + ERROR: The SAS System stopped processing this step + because of insufficient memory. + + Call quickSortHash: + Sorting time 123.5s + Memory 7'573'722.03k + OS Memory 8'388'448.00k + + Call quickSortLight: + Sorting time 1'338.25s + Memory 1'955'529.90k + OS Memory 1'977'436.00k +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +(*) When using hash tables in `Proc FCMP` the RAM + usage is not indicated properly. The memory + allocation is reported up to the session limit + and then reused if needed. The really required + memory is in fact much less then reported. + +--- + + +--- + +## `%date()` macro ###### + +## >>> `%date()` macro: <<< ####################### + +The date() macro function is a "lazy typer" wrapping up `%sysfunc(date())`. + +See examples below for the details. + +The `%date()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%date() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + + - `format` - *Optional*, if a value is provided + it should be a valid SAS format capable of handling + values produced by the `date()` function. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get value of `date()`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %date(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Get value of `date()` with a format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %date(date11.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%datetime()` macro ###### + +## >>> `%datetime()` macro: <<< ####################### + +The datetime() macro function is a "lazy typer" wrapping up `%sysfunc(datetime())`. + +See examples below for the details. + +The `%datetime()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%datetime() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + + - `format` - *Optional*, if a value is provided + it should be a valid SAS format capable of handling + values produced by the `datetime()` function. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get value of `datetime()`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %datetime(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 1.** Get value of `datetime()` as "long" and "short" ISO-8601: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %datetime(e8601dt.); + %put %datetime(b8601dt.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 1.** Get value of `datetime()` with user defined format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + proc format; + picture myCrazyDT (default=50) + other='%0Ssec. %0Mmin. %0Hhour %0dday %0mmonth %Yyear' (datatype=datetime) + ; + run; + + %put %datetime(myCrazyDT.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%downloadfilesto()` macro ###### + +## >>> `%downloadFilesTo()` macro: <<< ####################### + +The downloadFilesTo() macro copy files (in binary mode +using `filename()` function with options `lrecl=1 recfm=n`) +from list provided by user to a directory indicated +in the macro call. + +Macro can be executed in two possible ways: +1) by providing list of files to download in a `datalines4`(`cards4`) list + directly after macro call: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %downloadFilesTo() + datalines4; + + + ... + + ;;;; + run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +2) by create a dataset with a list of links and use of `DS=` and `DSvar=` parameters. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %downloadFilesTo( + , DS= + , DSvar= + ) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +See examples below for the details. + +The `%downloadFilesTo()` macro **does not** execute as a pure macro code. + +Temporary dataset `work.______locationInfoData` is generated during processing. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%downloadFilesTo( + target + <,DS=> + <,DSvar=link> + <,inDev=URL> + <,outDev=DISK> + <,inOptions=> + <,outOptions=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `target ` - *Required*, a path to target directory. + If empty the `WORK` location is used. + +*. `DS= ` - *Optional*, name of data set with list + of files to download. + +*. `DSvar= ` - *Optional*, name of variable in data set + with list of files to download. + + +*. `inDev=` - *Optional*, type of device used by the + `filename()` function to access incoming files. + Default value is `URL`. + +*. `outDev=` - *Optional*, type of device used by the + `filename()` function to access outgoing files. + Default value is `DISK`. + +*. `inOptions=` - *Optional*, list of additional options for the + `filename()` function to access incoming files. + Default value is empty. + +*. `outOptions=` - *Optional*, list of additional options for the + `filename()` function to access outgoing files. + Default value is empty. + + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Download data from web with diect list and then copy between directories: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +resetline; +%downloadFilesTo(~/directoryA) +datalines4; +https://www.lexjansen.com/wuss/2023/WUSS-2023-Paper-189.pdf +https://www.lexjansen.com/wuss/2023/WUSS-2023-Paper-189.zip +;;;; +run; + +%downloadFilesTo(~/directoryB,inDev=DISK) +datalines4; +~/directoryA/WUSS-2023-Paper-189.pdf +~/directoryA/WUSS-2023-Paper-189.zip +;;;; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Download data from web using data set with list: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +resetline; +data listOfFiles; +infile cards; +input files :$1024.; +cards4; +https://www.lexjansen.com/wuss/2023/WUSS-2023-Paper-201.pdf +https://www.lexjansen.com/wuss/2023/WUSS-2023-Paper-109.pdf +;;;; +run; + +%downloadFilesTo(R:\directoryC, DS=listOfFiles, DSvar=files) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%filepath()` macro ###### + +## >>> `%filePath()` macro: <<< ####################### + +The filePath() macro function returns path to a file, +it is a wrapper to `pathname()` function for files. + +See examples below for the details. + +The `%filePath()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%filePath( + fileref +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `fileref` - *Required*, a fileref from the `filename` statement. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Return path to temporary file: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + filename f temp; + %put %filePath(f); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%finddswithvarval()` macro ###### + +## >>> `%findDSwithVarVal()` macro: <<< ####################### + +The findDSwithVarVal() macro searches for all +datasets (available for a given session) containing +a variable of a given value. + +The value search is case sensitive - but can be altered with `IC=` parameter. +The value search keeps leading blanks - but can be altered with `TB=` parameter. +The value search compares full value - but can be altered with `CTS=` parameter. + +The default variable type is `char`, the `type=` parameter allows +to change it (possible values are `char` and `num`), the parameter is case sensitive. + +Only datasets are searched, views are not included. + +During the process two temporary datasets named: +`WORK._` (single underscore) and `WORK.__` (double underscore) +are generated. The datasets are deleted at the end of the process. + +By default search results are stored in the `WORK.RESULT` dataset. +Name of the dataset can be altered with `result=` parameter. +The dataset with result contains two variables: +`datasetName` - names of datasets, +`firstObservation` - the firs occurrence of the value. + +See examples below for the details. + +The `%findDSwithVarVal()` macro does not execute as a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%findDSwithVarVal( + variable + ,value + <,type=> + <,ic=> + <,tb=> + <,cts=> + <,lib=> + <,result=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `variable` - *Required*, name of variable to be searched. + +2. `value` - *Required*, the value to be searched. + +*. `type` - *Optional*, default value is `char`. + Indicates which type is the searched value. + Possible values are `char` and `num`, + the parameter is case sensitive. + +*. `ic` - *Optional*, "Ignore Cases", default value is `0`. + Indicates should the search ignore cases of the text values. + Possible values are `0` and `1`. + +*. `tb` - *Optional*, "Trim Blanks", default value is `0`. + Indicates should the search trim leading and trailing + blanks of the text values. + Possible values are `0` and `1`. + +*. `cts` - *Optional*, "Compare To Shorter", default value is `0`. + IF set to `1` execute value comparison as `=:` for the text value. + Possible values are `0` and `1`. + See examples. + +*. `lib` - *Optional*, default value is missing. + If not empty narrows the search to a particular library. + +*. `result` - *Optional*, default value is `WORK.RESULT`. + Is the name of the dataset with results. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Search variable `NAME` containing value `John`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %findDSwithVarVal(name, John) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Search numeric variable `AGE` containing value `14`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %findDSwithVarVal(age, 14, type=num) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Search numeric variable `SCORE` with missing value: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data TEST; + score=17; output; + score=42; output; + score=. ; output; + run; + + %findDSwithVarVal(score, ., type=num, result=WORK.MissingScore) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Search library `WORK` for variable `NAME` starting with value `Jo` + ignoring cases and trimming blanks from value: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data A; + name="Joanna"; + data B; + name="john"; + data C; + name=" Joseph"; + data D; + name=" joe"; + run; + + %findDSwithVarVal(name, Jo, ic=1, tb=1, cts=1, lib=WORK) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%fmt()` macro ###### + +## >>> `%fmt()` macro: <<< ####################### + +The fmt() macro function returns a `value` formatted by a `format`, +it is a wrapper to `putN()` and `putC()` functions. + +See examples below for the details. + +The `%fmt()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%fmt( + value + ,format + ,align + <,type=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `value` - *Required*, a value to be formatted. + +2. `format` - *Required*, a name of a format to be used, + character format should be preceded by the `$`. + +3. `align` - *Optional*, allows to use the `-L`, `-R` and `-C` modifiers. + +* `type=n` - *Optional*, defines type of the format. If the format + name is preceded by the `$` then C is set automatically. + If the character format name is without `$` then set + value to `C` yourself. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Formatting values: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %fmt(111, 7.2); + + %put %fmt(111, dollar10.2); + + %put %fmt(abc, $upcase.); + + %put %fmt(12345, date9.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Align values (compare different results!): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put *%fmt(ABC, $char9., -L)*; + %put *%fmt(ABC, $char9., -R)*; + %put *%fmt(ABC, $char9., -C)*; + + %put %fmt(ABC, $char9., -L); + %put %fmt(ABC, $char9., -R); + %put %fmt(ABC, $char9., -C); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%gettitle()` macro ###### + +## >>> `%getTitle()` macro: <<< ####################### + +The getTitle() macro extract text of titles or footnotes +into a delimited list. + +Titles/footnotes numbers can be selected with the `number` argument. +Only the text of a title or footnote is extracted. + +Author of the original code is: Quentin McMullen (`qmcmullen.sas@gmail.com`). + +See examples below for the details. + +The `%getTitle()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%getTitle( + < number> + <,type=> + <,dlm=> + <,qt=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `number` - *Optional*, default value is empty, + indicates numbers of titles to be extracted. + Space separated list is expected. + If empty or `_ALL_` extract all non-missing. + +*. `type` - *Optional*, default value is `T`. + Indicates which type is the searched. + `T` fro title, `F` for footnote. + +*. `dlm` - *Optional*, "DeLiMiter", default value is `|` (pipe). + Possible values are: `| \ / , . ~ * # @ ! + - _ : ?` + or `s` for space, `c` for comma, `d` for semicolon. + +*. `qt` - *Optional*, "QuoTes", default value is empty. + Use `%str()` for single quote symbol (e.g. `%str(%")`). + If there are multiple symbols, only the first and the + second are selected as a leading and trailing one, + e.g. `qt=""` gives `"title1 text" "title2 text" ... `. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get titles in different forms: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + title1 j=c "Hi Roger" ; + title2 j=l "Good Morning" ; + title3 "How are you?" ; + title4 ; + title5 "Bye bye!" ; + + %put %GetTitle() ; + + %put %GetTitle(1 3,dlm=c, qt=[]) ; + + %put %GetTitle(2:4,dlm=s, qt='') ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Get footnotes in different forms: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + footnote1 "First little footnote"; + footnote2 "Second little footnote"; + footnote3 "Third little footnote"; + + %put %GetTitle(1 2,type=f,dlm=s, qt="") ; + %put %GetTitle(2 3,type=f,dlm=c, qt='') ; + %put %GetTitle(1 3,type=f,dlm=d, qt=[]) ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%infmt()` macro ###### + +## >>> `%infmt()` macro: <<< ####################### + +The infmt() macro function returns a `value` read in by an `informat`, +it is a wrapper to `inputN()` and `inputC()` functions. + +See examples below for the details. + +The `%infmt()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%infmt( + value + ,informat + <,type=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `value` - *Required*, a value to be formatted. + +2. `informat` - *Required*, a name of a format to be used, + character format should be preceded by the `$`. + +* `type=n` - *Optional*, defines type of the informat. If the informat + name is preceded by the `$` then C is set automatically. + If the character format name is without `$` then set + value to `C` yourself. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Informatting values: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %infmt(111, 7.2); + %put %infmt(111.234, 7.2); + + %put %infmt($111, dollar10.2); + %put %infmt($111.234, dollar10.2); + + %put %infmt(abc, $upcase.); + + %put %infmt(12mar45, date9.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%letters()` macro ###### + +## >>> `%letters()` macro: <<< ####################### + +The letters() macro function allows to print a list of Roman +letters starting from `start` up to `end` incremented by `by`. +The letters list can be uppercases or lowercase (parameter `c=U` or `c=L`), +can be quoted (e.g. `q=""` or `q=[]`), and can be separated by `s=`. + +Values of `start`, `end`, and `by` have to be integers in range between 1 ad 26. + +See examples below for the details. + +The `%letters()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%letters( + range + <,c=> + <,q=> + <,s=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `range` - *Required*, letters selector in form `start:end:by`. + Lists letters from `start` to `end` by `by`. + Values of `start`, `end`, and `by` are separated by + colon and must be between 1 ad 26. + If value is outside range it is set to + `start=1`, `en=26`, and `by=1`. If `end` is missing + then is set to value of `start`. + If `end` is smaller than `start` list is reversed + +* `c = U` - *Optional*, it is a lowercase letters indicator. + Select `L` or `l`. Default value is `U` for upcase. + +* `q = ` - *Optional*, it is a quite around elements of the list. + Default value is empty. Use `%str()` for one quote symbol. + If there are multiple symbols, only the first and the + second are selected as a preceding and trailing one, + e.g. `q=[]` gives `[A] [B] ... [Z]`. + +* `s = %str( )` - *Optional*, it is a separator between + elements of the list. Default value is space. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Space separated list of capital letters from A to Z: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26:1); + + %put %letters(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** First, thirteenth, and last letter: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1) %letters(13) %letters(26); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Every third lowercase letter, i.e. `a d g j m p s v y`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26:3,c=L); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Lists with separators: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26:2,s=#); + %put %letters(1:26:3,s=%str(;)); + %put %letters(1:26:4,s=%str(,)); + %put %letters(1:26,s=); + %put %letters(1:26,s==); + %put %letters(1:26,s=/); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Every second letter with quotes: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26:2,q=%str(%')); + %put %letters(2:26:2,q=%str(%")); + + %put %letters(1:26:2,q=''); + %put %letters(2:26:2,q=""); + + %put %letters(1:26:2,q=<>); + %put %letters(2:26:2,q=\/); + + %put %letters(1:26:2,q=()); + %put %letters(2:26:2,q=][); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** Mix of examples 4, 5, and 6: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(1:26,c=L,q='',s=%str(, )); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** If `end` is smaller than `start` list is reversed: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %letters(26:1:2,q=''); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%libpath()` macro ###### + +## >>> `%libPath()` macro: <<< ####################### + +The libPath() macro function returns path to a library, +it is a wrapper to `pathname()` function for libraries. + +See examples below for the details. + +The `%libPath()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%libPath( + libref +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `libref` - *Required*, a libref from the `libname` statement. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Return path to `WORK` library: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %libPath(WORK); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Return path to `SASHELP` library: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %libPath(SASHELP); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%minclude()` macro ###### + +## >>> `%mInclude()` macro: <<< ####################### + +The mInclude() macro is a macrolanguage version of the SAS `%include` statement. +But it allows for "embedding any code anywhere into SAS programs". + +Macro was inspired by *Leonid Batkhan* and his blog post: + +"Embedding any code anywhere into SAS programs" from May 30, 2023. + +Link: `https://blogs.sas.com/content/sgf/2023/05/30/embedding-any-code-anywhere-into-sas-programs/` + +The implementation presented, in contrary to inspiration source, is +based on the `doSubL()` function and a list of global +macrovariables of the form `______` (six underscores and a number). + +See examples below for the details. + +The `%mInclude()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%mInclude( + < f> + <,source=> + <,lrecl=> + <,symdel=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `f` - *Required*, a SAS `fileref` or a **quoted** path + to the included file. + +*. `source=0` - *Optional*, default value is `0`. + Set to `1` if the source should be printed in the log. + +*. `lrecl=32767` - *Optional*, default value is `32767`. + Sets the `lrecl` value for the file width. + +*. `symdel=1` - *Optional*, default value is `1`. + Indicates if the global macrovariables + `______1` to `______N` should be deleted + when the macro ends. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Embedding text in statements (the `%include` won't work here): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + resetline; + filename f "%workpath()/testFile1.txt"; + filename f list; + + data _null_; + file f; + put "13 14 15"; + run; + + resetline; + data testDataset; + set sashelp.class; + where age in ( %mInclude(f) ); + run; + + data testDataset2; + set sashelp.class; + where age in ( %mInclude(f,source=1) ); + run; + + filename f clear; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Embedding with direct path (mind those quotes!): +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + resetline; + filename f "%workpath()/testFile2.txt"; + filename f list; + + %let someGlobalMacroVariable=17; + + data _null_; + file f; + put "options mprint;"; + do i=1 to 3; + put "data y; x = " i "; run;"; + put '%macro A' i +(-1) '(); %put ' i ' ** &someGlobalMacroVariable.; %mend; %A' i +(-1) '()'; + end; + put "options nomprint;"; + run; + + resetline; + %mInclude("%workpath()/testFile2.txt") + + %mInclude("%workpath()/testFile2.txt",source=1) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Embedding SQL code inside the pass through execution: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + resetline; + filename f2 "%workpath()/testSql.txt"; + + data _null_; + file f2; + input; + put _infile_; + cards4; + select + c2.make + , c2.model + , c2.type + , c2.invoice + , c2.date + + from + public.CARS_EU c2 + + where + c2.cylinders > 4 + and + c2.date > '2023-04-02' + ;;;; + run; + + + title 'the %include fails'; + proc sql; + connect to POSTGRES as PSGDB ( + server="127.0.0.1" + port=5432 + user="user" + password="password" + database="DB" + ); + + select * from connection to PSGDB + ( + %Include f2 / source2; + ) + ; + + disconnect from PSGDB; + quit; + + title 'the %mInclude works'; + proc sql; + connect to POSTGRES as PSGDB ( + server="127.0.0.1" + port=5432 + user="user" + password="password" + database="DB" + ); + + + select * from connection to PSGDB + ( + %mInclude(f2, source=1) + ) + ; + + disconnect from PSGDB; + quit; + + title; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** In a limited way and with help of the `resolve()` function, + it even works with IML's interface to R: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + +resetline; +filename f3 TEMP; + +data _null_; + file f3; + infile cards4; + input; + put _infile_ ';'; %* a "semicolon" trick for R statements separation *; +cards4; +rModel <- lm(Weight ~ Height, data=Class, na.action="na.exclude") +print (rModel$call) +print (rModel) +;;;; +run; + + +proc iml; + codeText = resolve(' %mInclude(f3, source=1) '); + print codeText; + + call ExportDataSetToR("Sashelp.Class", "Class" ); + submit codeText / R; + &codeText + endsubmit; +quit; + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%monthshift()` macro ###### + +## >>> `%monthShift()` macro: <<< ####################### + +The monthShift() macro is a utility macro +which allows to shift "year-month" period by +a given number of "periods" (months). + +The result is in the `YYYYMM` format but can be altered. + +See examples below for the details. + +The `%monthShift()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%monthShift( + < Y> + <,M> + <,shift> + <,ofmt=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `Y` - *Optional*, a year from which counting starts. + If null the value is set to *system year*. + +2. `M` - *Optional*, a month from which counting starts. + If null the value is set to `1`. Can be a number + (`1` to `12`) or a name (`June`, `OCTOBER`) or + a three letters short (`JAN`, `apr`). + +3. `shift` - *Optional*, number of periods to shift. + If null the value is set to `0`. + Positive value shifts to the "future", + negative value shifts to the "past", + Can be an expression (e.g. `1+2*3`, see examples). + +* `ofmt=YYMMn6.` - *Optional*, it is a format name used to + display the result. Default value is `YYMMn6.` + See examples. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Shift one up and one down: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put + Past: %monthShift(2023, 1, -1) + Current: %monthShift(2023, 1 ) + Future: %monthShift(2023, 1, +1) +; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Shift by expression: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %let n = 2; + %put + %monthShift(2023, 1, +1 + &n.*3) + ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Shift with default values: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %monthShift(); + %put %monthShift(2023); + %put %monthShift(2023,Jan); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Shift with months names: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put + %monthShift(2023,Jan,-1) + %monthShift(2023,Feb,-2) + %monthShift(2023,Mar,-3) + %monthShift(2023,Apr,-4) + %monthShift(2023,May,-5) + %monthShift(2023,Jun,-6) + %monthShift(2023,Jul,-7) + %monthShift(2023,Aug,-8) + %monthShift(2023,Sep,-9) + %monthShift(2023,Oct,-10) + %monthShift(2023,Nov,-11) + %monthShift(2023,Dec,-12) + ; + + %put + %monthShift(2023,January,12) + %monthShift(2023,February,11) + %monthShift(2023,March,10) + %monthShift(2023,April,9) + %monthShift(2023,May,8) + %monthShift(2023,June,7) + %monthShift(2023,July,6) + %monthShift(2023,August,5) + %monthShift(2023,September,4) + %monthShift(2023,October,3) + %monthShift(2023,November,2) + %monthShift(2023,December,1) + ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 5.** Play with formatting: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put + %monthShift(2023, 1, +1 ) + %monthShift(2023, 1, +1, ofmt=yymm7. ) + %monthShift(2023, 1, +1, ofmt=yymmd7.) + %monthShift(2023, 1, +1, ofmt=yymms7.) + ; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 6.** Read monthly data with `noDSNFERR` option: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + data + A202210 A202211 A202212 + A202301 A202302 A202303 + A202304 A202305 A202306 + A202307 A202308 A202309 + ; + set sashelp.class; + run; + + + options noDSNFERR; + data ALL; + set + A%monthShift(2023, 9, -12) - A%monthShift(2023, 9) + ; + run; + options DSNFERR; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%replist()` macro ###### + +## >>> `%repList()` macro: <<< ####################### + +The repList() macro function allows to repeat `T` +times elements of a `L` list, possibly `E` times each element, +separated by string `S`. + +See examples below for the details. + +The `%repList()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%repList( + list + <,times=> + <,each=> + <,lenghtOut=> + <,sep=> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `list` - *Required*, a list of elements to be repeated. + List can be space or comma separated. + Elements can be in quotes. + For comma separated list add brackets + e.g., `%repList((A,B,C,D),times=5)`. + The list separators are: `<{[( ,;)]}>`. + +* `times=` - *Optional*, An integer indicating + the number of repetitions. + By default set to `1`. + + +* `each=` - *Optional*, A list of integers indicating + the number of repetitions of each element of the list + e.g., for a list `A B C` and the `each=2 4` the result + is `A A B B B B C C`. If the number of integers is less + then the length of the list values are recycled from + the beginning. + By default set to `1`. + +* `lenghtOut=` - *Optional*, An integer indicating + after what the number of repetitions process will stop. + By default set to `0` which means "do not stop". + +* `sep=` - *Optional*, it is a separator printed between + repeated elements. Mnemonics for *space* is `s`, + for *comma* is `c`, and for semicolon in `q`. + Default value is a single space. + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple repetition of all elements: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repList((A,B,C,D), times=3); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Simple repetition of each element: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repList(("A",'B',"C",'D'), each=3); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Simple repetition with a separator: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repList(A10;B20;C30, times=3, each=2, sep=Q); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Recycle elements up to 8 with a comma as a separator: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put %repList(1 2 3, lenghtOut=8, sep=c); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 5.** Separate number of repetitions for each element: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put [%repList([D][C][B][A], each = 2 3 5 7, sep=] [)]; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 6.** "ASCII art" butterflies: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +%put {>%repList(! $ |, times = 2, each =2 1, sep=<} ... {>)<}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 7.** Data repeating: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas +data A; + x=17; +data B; + x=42; +data C; + x=303; +run; + +data Times2_A10B11C12; + set + %repList(A B C, times = 2, each =10 11 12) + ; +run; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +--- + + +--- + +## `%time()` macro ###### + +## >>> `%time()` macro: <<< ####################### + +The time() macro function is a "lazy typer" wrapping up `%sysfunc(time())`. + +See examples below for the details. + +The `%time()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%time() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + + - `format` - *Optional*, if a value is provided + it should be a valid SAS format capable of handling + values produced by the `time()` function. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get value of `time()`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %time(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Get value of `time()` with a format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %time(time8.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%today()` macro ###### + +## >>> `%today()` macro: <<< ####################### + +The today() macro function is a "lazy typer" wrapping up `%sysfunc(today())`. + +See examples below for the details. + +The `%today()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%today() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + + - `format` - *Optional*, if a value is provided + it should be a valid SAS format capable of handling + values produced by the `today()` function. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Get value of `today()`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %today(); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**EXAMPLE 2.** Get value of `today()` with a format: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %today(yymmdd10.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%translate()` macro ###### + +## >>> `%translate()` macro: <<< ####################### + +The translate() macro function allows to replace bytes with bytes in text string. + +See examples below for the details. + +The `%translate()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%translate( + string + ,from + ,to +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `string` - *Required*, string to modify. + +2. `from` - *Required*, list of bytes to be replaced with + corresponding bytes from `to`. + +3. `to` - *Required*, list of bytes replacing + corresponding bytes from `from`. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Replace quotes and commas with apostrophes and spaces: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %translate(%str("A", "B", "C"),%str(%",),%str(%' )); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Unify all brackets; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %translate(%str([A] {B} (C) ),{[(<>)]},(((())))); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Replace all digits with `*`: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %translate(QAZ1WSSX2EDC3RFV4TGB5YHN6UJM7IK8OL9P0,1234567890,**********); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 4.** Letters change: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %translate(%str(A=B),AB,BA); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%tranwrd()` macro ###### + +## >>> `%tranwrd()` macro: <<< ####################### + +The tranwrd() macro function allows to replace substrings +with other substrings in text string. + +Returned string is unquoted by `%unquote()`. + +See examples below for the details. + +The `%tranwrd()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%tranwrd( + string + ,from + ,to + <,repeat> +) +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +1. `string` - *Required*, string to modify. + +2. `from` - *Required*, substring replaced with + corresponding string from `to`. + +3. `to` - *Required*, substring replacing + corresponding substring from `from`. + +4. `repeat` - *Optional*, number of times the replacing + should be repeated, default is 1. + Useful while removing multiple adjacent + characters, e.g. compress all multiple + spaces (see example 2). +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Simple text replacement: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %tranwrd(Miss Joan Smith,Miss,Ms.); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 2.** Delete multiple spaces; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %tranwrd(%str(A B C),%str( ),%str( ),5); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +**EXAMPLE 3.** Remove substring: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + %put %tranwrd(ABCxyzABCABCxyzABC,ABC); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + +## `%workpath()` macro ###### + +## >>> `%workPath()` macro: <<< ####################### + +The workPath() macro function returns path to the `WORK` library, +it is a wrapper to `pathname("work", "L")` function. + +See examples below for the details. + +The `%workPath()` macro executes like a pure macro code. + +### SYNTAX: ################################################################### + +The basic syntax is the following, the `<...>` means optional parameters: +~~~~~~~~~~~~~~~~~~~~~~~sas +%workPath() +~~~~~~~~~~~~~~~~~~~~~~~ + +**Arguments description**: + +*) No arguments. + +--- + + +### EXAMPLES AND USECASES: #################################################### + +**EXAMPLE 1.** Create new library inside `WORK` library: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas + options dlCreateDir; + libname NEW "%workPath()/new"; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +--- + + +--- + + +--- + +# License ###### + +Copyright (c) 2020 - 2023 Bartosz Jablonski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + diff --git a/hist/1.36.0/baseplus.zip b/hist/1.36.0/baseplus.zip new file mode 100644 index 0000000000000000000000000000000000000000..a062d6d27508c072d1002296fc55f82468932a54 GIT binary patch literal 299390 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�tZe13XB?W<4(^BI!+(-HzXG! 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%0Ajw%y8&&DDp6zn1 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�R$sfGUiv1D)>Z_>*6*k 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℘{O{#b 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