Question
Brian Schoen · Nov 6, 2020

Piece or String function using Matches [A-Z]

I am trying to match on config items that have the same Vendor name in them for custom alerting.

Example:

FromVendornameSiteADT

ToVendornameORM

Where Vendorname could be several systems that have multiple interfaces.

How can I find all config items (interface names) with the same Vendor name, without explicitly naming them?

I was trying to use $Piece to segment out the CamelCase pieces (as the name I want is always the 2nd Capitalized section), but could not find a way to integrate match regex logic (e.g. [A-Z]) inside $Piece or other String extract functions.

00
0 9 94 4

Replies

Hello Brian,

I'm not sure what your other code for this alerting looks like, but would the Ensemble Utility Function "Contains" be useful?

TEST>w ##class(Ens.Util.FunctionSet).Contains("FromVendornameSiteADT","Vendor"))
1

There are also utility functions to convert a string to all upper/lowercase, if you need that also.

Unfortunately, Vendorname is just a placeholder for the section of the String I need to extract.  I'm trying to find a way to pull that section (i.e. the 2nd Capitalized word) from the string without naming it.

i.e.:

FromEpicADT  > "Epic"

ToHealthshareNorthADT  > "Healthshare"

I think this has the same problem I describe in my reply above.  

Hi Brian,

Could you please with below code.

----------------------------------------------------

Class Sample.TempUtil Extends %RegisteredObject
{

ClassMethod GetSecondUpperCase(pInput As %String) As %String
{
 ;w ##class(Sample.TempUtil).GetSecondUpperCase("GetSecondUpperCase")
 SET stsrtpos=$LOCATE(pInput,"[[:upper:]]{1}",2)
 SET endpos=$LOCATE(pInput,"[[:upper:]]{1}",(stsrtpos+1))
 SET result=$EXTRACT(pInput,stsrtpos,(endpos-1))
 
 quit result
}

}
----------------------------------------------------------------

Regards,

Muni Ganesh

Thanks!   I was almost there...  I think this will work nicely!

Have a look at %Regex.Matcher. Since you always want the 2nd capitalized section you can just make that your regex capture group:

    set matcher=##class(%Regex.Matcher).%New("^[A-Z][a-z]*([A-Z][a-z]*)")                             
    set matcher.Text="ToVendornameORM"
    if matcher.Locate() {
        write "Found ",matcher.Group(1)," at position ",matcher.Start,!
    }

This gives me the output:

Found Vendorname at position 1

Thanks!   I think this would have gotten me there too!

Here's the code to get capitalized piece of string by number:

/// Get Capitalized piece.
///   str - string to search
///   piece - 1-based piece to return
/// write ##class(test.CustomQuery).GetCapitalizedPiece()
ClassMethod GetCapitalizedPiece(str As %String = "lowerHelloWorldAgainHelloWorldAgainHelloWorldAgainHelloWorldAgain", piece As %Integer = 1) As %String
{
    
    // Only uppercase letters
    set upper = $tr(str, $zcvt(str, "l"))
    
    // Number of uppercase letteres
    set capCount = $l(upper)
    
    // Quit if requested piece can not exist
    quit:piece>capCount ""
    quit:piece<1 ""
    
    // First letter in our capitalized piece
    set startLetter = $e(upper, piece)
    
    // All previous capitalized letters
    set prevLetters = $e(upper, 1, piece - 1)

    // Check if current capitalized letter is a first capitalized letter
    // If not - skip previous capitalized letters
    if '$find(prevLetters, startLetter) {
        set start = $find(str, startLetter) - 1
    } else {
        set count = $l(prevLetters, startLetter) + 1
        set start = 0
        while $i(count,-1) {
            set start = $find(str, startLetter, start)
        }
        set start = start - 1
    }
    
    // Is this capitalized piece the last one?
    if piece=capCount {
        set end = $l(str)
    } else {
        set end = $find(str, $e(upper, piece + 1), start) - 2
    }

    quit $e(str, start, end)
}

Advantages over regexp:

  • ~50 times faster
  • Get any piece, not only the second one (although generating required regexp by piece number seems easy to do)