I am trying to write a mini-program that simply takes a list of variables, and returns a sub-list that contains variables that are actually in the dataset.
I do this many times in a do
file (not sequentially, so no loops), so it is easier to just have a quick program rather than effectively duplicate this code every time.
A simplified version of the program is below. The main issue appears to be in line 6. The first argument for the program should be the name of a local macro that contains variable names to compare with those in the dataset. So, for example, if the local macro is list1
, the first argument of the program is list1
, and I want to store a new local macro, vlist
, which contains all the variables in list1
.
But when I try to do this with:
``1''
the resulting local macro vlist
just ends up being empty, while the local allvars
is fine.
My program's code is the following:
clear
cap program drop lm
program define lm
* Create a new local, vlist, that is all the variables in the local macro identified in argument 1
local vlist ``1''
* Create a new local, allvars, that is all variables in teh dataset
qui ds, a
local allvars `r(varlist)'
* Display both macros, to illustrate that vlist is empty, while allvars contains all variables
di as text "vlist: " as result "`vlist'"
di as text "allvars: " as result "`allvars'" _newline
* Create a new local macro that is the intersection of the two lists (if it worked)
local `1'_inter : list vlist & allvars
* Display different messages depending on the outcome (e.g. if a list was created, or empty)
if missing("``1'_inter'") di as error "There are no common variables between `1' and the dataset."
else di as result "`1' intersection with varlist _all now stored as local: `1'_inter"
end
clear
input float(var1 var2 var3 var4 var5) // Input irrelevant data
. . . . .
end
/* Next, create a macro with a list of variables that are in the dataset
(e.g. var1 and var2), and even some that are not in teh data (var7). */
local list1 var1 var2 var7
* Execute the list-match program
lm list1
As you can see, the local vlist
ends up being empty, so there is no intersection between the lists.
Any idea what I am doing wrong here?
I am sure it is the double local macro, but I am not sure how to fix it.
The macro in macro reference
``1''
is evaluated as follows. First, macro 1
is the first thing you typed as argument to the command, which in your example is list1
, so substituting that Stata then sees
`list1'
and the entire command is now evaluated as
local vlist `list1'
which is then evaluated as follows. What is in the local macro list1
? Precisely nothing, or an empty string, as no such local macro exists at that point in the program (and any other local macros created anywhere else are invisible, because that is what local means: local to the program space concerned). So next Stata sees
local vlist
whereby you simultaneously create a local macro and blank it out, thus destroying it.
The solution is that you intended to write
local vlist "`1'"
or perhaps just
args vlist
However, that is not the end of it. The command
lm list1
mentions the name of a local macro you have just created. But passing the name of a local macro to another program is futile, as the other program can't see the contents of the macro. You have to pass the contents.
Yet more: the program then needs to be revised as you may want to pass several names to it and they will only be regarded as a single argument if you bind them in double quotes.
FWIW, the problem you describe seems to be that solved by isvar
from SSC. Here it is:
*! NJC 1.0.0 20 Sept 2005
program isvar, rclass
version 8
syntax anything
foreach v of local anything {
capture unab V : `v'
if _rc == 0 local varlist `varlist' `V'
else local badlist `badlist' `v'
}
di
if "`varlist'" != "" {
local n : word count `varlist'
local what = plural(`n', "variable")
di as txt "{p}`what': " as res "`varlist'{p_end}"
return local varlist "`varlist'"
}
if "`badlist'" != "" {
local n : word count `badlist'
local what = plural(`n', "not variable")
di as txt "{p}`what': " as res "`badlist'{p_end}"
return local badlist "`badlist'"
}
end
I am fond of ds
but I wouldn't use it here. You could be calling up thousands of variable names just to check whether a few names are in a dataset.
A final comment on style
local list1 var1 var2 var7
lm list1
might as well be
lm var1 var2 var7
as there is no value in putting the names into a bag (local macro) and using the name of the bag when you can just pass the names directly. As said, this won't work with your program until you fix it, but it would work with isvar
.