Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Use -inlist- with local list

    I would like to check whether a string exists in a local. I have:

    Code:
    local X "1" "2" "3" ...
    And would like
    Code:
    inlist("2", "`X'")
    to return 1 (true).

    Could someone offer advice on how to do this properly? Thanks.

    Note:

    Originally had:
    Code:
    local X "1" "2" "3" ...
    local x "1"
    inlist("`x'","`X'")
    Last edited by Allen Sirolly; 03 Nov 2015, 12:28.

  • #2
    Well, one problem is that local X is not comma delimited, whereas inlist() requires a comma-delimited argument list.

    The other problem is that your local macros don't actually contain what I think you think they contain, because quotation binding in local macros is tricky. In order to make all those quotation marks part of the actual content of the local, the whole text of the macro has to be surrounded by compound double quotes. So this works:


    Code:
    local X `""1" "2" "3""'
    local x `""1""'
    
    local X: subinstr local X `"" ""' `"", ""', all
    display `"`X'"'
    display `"`x'"'
    
    display inlist(`x', `X')
    Note: It is difficult to optically parse all those strings of different types of quote marks. Suggest you copy-paste this into your do-file, rather than attempting to retype it.


    Comment


    • #3
      The elements of a list of strings need to be quoted and comma-separated for inlist() to be happy.

      Code:
      . di inlist("2", "1", "2", "3")
      1
      
      . local list `" "1", "2", "3" "'
      
      . di inlist("2", `list')
      1
      In practice I might loop, but everything depends on programming context.

      Code:
      local tocheck "frog" 
      local in = 0 
      
      foreach val in "frog" "toad" "newt" "dragon" { 
          if "`val'" == "`tocheck'" { 
              local in = 1 
              break
          }
      } 
      
      di "`in'"

      Comment


      • #4
        I encountered a similar problem as Allen, in my case with country codes. I tried to implement Clyde's code but I don't manage to make it work. Here is what I have:

        Code:
        local EU `" "AT" "BE" "CY" "CZ" "DE" "DK" "EE" "ES" "FI" "FR" "GR" "HR" "HU" "IE" "IT" "LI" "LT" "LU" "LV" "MT" "NL" "PL" "PT" "RO" "SE" "SI" "SK" "'
        display `EU'
        local EU: subinstr local EU `"" ""' `"", ""', all
        display `EU'
        What this gets me is: AT BE CY CZ DE DK EE ES FI FR GR HR HU IE IT LI LT LU LV MT NL PL PT RO SE SI SK

        What I would like to have is the list with commas in-between in order to use it for the inlist command, i.e. AT, BE, CY, CZ, DE, DK, EE, ES, FI, FR, GR, HR, HU, IE, IT, LI, LT, LU, LV, MT, NL, PL, PT, RO, SE, SI, SK.

        Where is my mistake?

        Comment


        • #5
          You can do it with extended macro functions, but the following is robust to the existence of other non-upper case letter characters.

          Code:
          local EU `" "AT" "BE" "CY" "CZ" "DE" "DK" "EE" "ES" "FI" "FR" "GR" "HR" "HU" "IE" "IT" "LI" "LT" "LU" "LV" "MT" "NL" "PL" "PT" "RO" "SE" "SI" "SK" "'
          local EU= ustrregexra(trim(itrim(ustrregexra(`"`EU'"', "[^\p{LU}]", " "))), " ", ", ")
          display "`EU'"
          Res.:

          Code:
           
          . display "`EU'"
          AT, BE, CY, CZ, DE, DK, EE, ES, FI, FR, GR, HR, HU, IE, IT, LI, LT, LU, LV, MT, NL, PL, PT, RO, SE, SI, SK

          Comment


          • #6
            Generally, #4 is due to not including additional options when using the levelsof command. To avoid cleaning the contents of the local in the first place, consider:

            Code:
            webuse census, clear
            levelsof state2, local(states) sep(,) clean
            di "`states'"
            Res.:

            Code:
            . di "`states'"
            AK,AL,AR,AZ,CA,CO,CT,DE,FL,GA,HI,IA,ID,IL,IN,KS,KY,LA,MA,MD,ME,MI,MN,MO,MS,MT,NC,ND,NE,NH,NJ,NM,NV,NY,OH,OK,OR,PA,RI,SC,SD,TN,TX,UT,VA,V
            > T,WA,WI,WV,WY

            Comment


            • #7
              I thought that much of the point of two-letter identifiers like these was to have standard abbreviations in simple form without extra punctuation or unusual letters, i.e. without accents or other modifiers.


              Code:
              . local in AT BE CY CZ DE DK EE ES FI FR GR HR HU IE IT LI LT LU LV MT NL PL PT RO SE SI SK
              
              . local out : subinstr local in " " ",", all 
              
              . di "`out'"
              AT,BE,CY,CZ,DE,DK,EE,ES,FI,FR,GR,HR,HU,IE,IT,LI,LT,LU,LV,MT,NL,PL,PT,RO,SE,SI,SK

              Comment


              • #8
                Thank you very much for your suggestions.

                It is right that can be handy because they do not contain punctuation etc. I tried to use the list of countries to filter for the ones contained in the list.

                Code:
                keep if inlist(iso_country_code, "`out'")
                However, that way somehow all of my observations are dropped. So in the end I decided to do the other way around and delete the countries by hand that should not be part of the dataset, i.e.

                Code:
                drop if iso_country_code == "CH"
                drop if iso_country_code == "IS"
                drop if iso_country_code == "LI"
                drop if iso_country_code == "MK"
                drop if iso_country_code == "NO"
                drop if iso_country_code == "UK"

                Comment


                • #9
                  Indeed, no individual country has a code that is the concatenation of them all. My comment was drifting away from the question in
                  #5 if only because it now turns that what you asked for there is not what you need!

                  What should work is something more like

                  Code:
                  gen tokeep = 0
                  
                  quietly foreach c in AT BE CY CZ DE DK EE ES FI FR GR HR HU IE IT LI LT LU LV MT NL PL PT RO SE SI SK {
                        replace tokeep = 1 if iso_country_code == "`c'"
                  }
                  
                  keep if tokeep
                  There are other ways to do it but this one gets around a restriction on the number of string arguments to inlist()
                  Last edited by Nick Cox; 07 Jun 2023, 14:15.

                  Comment

                  Working...
                  X