5
\$\begingroup\$

I would like your opinion of my PowerShell function. Does it follow conventions, and what could can I do to improve it. It is made to create LDAP friendly username from a full name in Serbian language.

function Get-UsernameFromFullName
{
    [CmdletBinding()]
    param
    (
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [string]
        $FullName,

        [Parameter(Position = 1, Mandatory = $false, ValueFromPipelineByPropertyName = $true)]
        [ValidateSet('First', 'Last')]
        [string]
        $LastNamePosition = 'First'
    )
    
    $usernameComponents = $FullName.ToLower().Split(" ")

    switch ($LastNamePosition)
    {
        'First' { $firstName = $usernameComponents.Length -1; $lastName = 0 }
        'Last'  { $firstName = 0; $lastName = $usernameComponents.Length -1 }
        Default { $firstName = $usernameComponents.Length -1; $lastName = 0 }
    }

    $chars = ($usernameComponents.Get($firstName) + "." + $usernameComponents.Get($lastName)).ToCharArray()

    foreach($char in $chars)
    {
        switch($char)
        {
            'ć' { $segment = "c" }
            'č' { $segment = "c" }
            'đ' { $segment = "dj" }
            'š' { $segment = "s" }
            'ž' { $segment = "z" }
            default { $segment = $char }
        }
        $Username += $segment
    }
    return $Username
}

Here is a link to GitHub page of the actual .psm1 file.

This is an example of the function usage:

Get-UsernameFromFullName -FullName "Čedomir Đorđević"

cedomir.djordjevic

Get-UsernameFromFullName -FullName "JANKOV ZORAN" 

zoran.jankov

Get-UsernameFromFullName -FullName "Stojić Gradimir"

gradimir.stojic

Get-UsernameFromFullName -FullName "Vučićević R Željko"

zeljko.vucicevic

Get-UsernameFromFullName -FullName "Magda Boškić" -LastNamePosition Last

magda.boskic

\$\endgroup\$
9
  • 2
    \$\begingroup\$ could you add some sample names & the result from processing them? i have a hard time thinking about things without something solid to work with ... [blush] \$\endgroup\$ – Lee_Dailey Aug 4 '20 at 2:04
  • 4
    \$\begingroup\$ PLEASE, do not post images of text/code/sample-data/errors. why force others to squint to read it ... or to type it in to test it? for example, the reason to ask for samples is to see how the code runs when given sample data to work with. i am not willing to type up 4-5 sample names just to help test your code. so please post the requested sample data in a usable format - text. [grin] \$\endgroup\$ – Lee_Dailey Aug 4 '20 at 7:40
  • 2
    \$\begingroup\$ yep! [grin] all i needed was the names, not the command line ... but that gives me some sample data to work with after i strip out the commands. i have a few ideas, so i otta have a bit of commentary sometime in the next 18 hours or so. gotta take care of "real life" 1st ... [grin] \$\endgroup\$ – Lee_Dailey Aug 5 '20 at 4:05
  • 3
    \$\begingroup\$ Please do not edit the code after you have an answer! See our help center \$\endgroup\$ – pacmaninbw Aug 5 '20 at 12:52
  • 2
    \$\begingroup\$ @ZoranJankov - thank you for the info ...i was wondering why ... [grin] \$\endgroup\$ – Lee_Dailey Aug 5 '20 at 13:28
3
\$\begingroup\$

i think the comments cover the why of things, but please feel free to ask if you have any questions. [grin]

#region >>> fake reading in a list of names
#    in real life, use Get-Content
$InStuff = @'
Čedomir Đorđević
JANKOV ZORAN 
Stojić Gradimir
Vučićević R Željko
Magda Boškić
'@ -split [System.Environment]::NewLine

<# intended output
cedomir.djordjevic
zoran.jankov
gradimir.stojic
zeljko.vucicevic 
magda.boskic [lastname @ end]
#>
#endregion >>> fake reading in a list of names



function Get-UserNameFromFullName
    # your "Get-UsernameFromFullName" name leaves the "N" in "Username" in lowercase
    #    that is not consistent with your use of that in other names of items
    {

    <#
    where is the Comment Based Help?
    #>

    [CmdletBinding ()]
    Param
        (
        [Parameter (
            Position = 0,
            # the following are switches that default to "$False"
            #    that means there is no need to add "= $True"
            #    the simple presense of the attribute flips it to "$True"
            Mandatory,
            ValueFromPipeline,
            # the following requres the pipeline object contain a property named ".FullName"
            #    is that always going to be the case?
            #    if not, then the "ValueFromPipeline" above will handle bare values
            ValueFromPipelineByPropertyName
            )]
            [string]
            $FullName,

        [Parameter (
            Position = 1,
            # making this mandatory makes little sense when a default is supplied
            #Mandatory,
            # does it make any sense to use the following attribute?
            ValueFromPipelineByPropertyName
            )]
            # don't use "First" and "Last" for _position_ info when the same words are used for the name parts
            #    it's needlessly confusing
            #[ValidateSet ('First', 'Last')]
            [ValidateSet ('Start', 'End')]
            [string]
            $LastNamePosition = 'Start'
        )

    begin {}

    process
        {
        # properly supporting the pipeline requires one to have a "process" block.
        #    otherwise all code is run in a virtual "end" block ... and that does not correctly support pipeline input

        # mixing camelCase and PascalCase for variable names is confusing [*grin*] 
        #    the recommended style for PoSh is PascalCase
        #$usernameComponents = $FullName.ToLower().Split(" ")
        # the 2nd sample name has a trailing space
        #    the ".Trim()" removes that
        # good practice in PoSh is to avoid using double quotes since that can trigger unwanted expansion of $Vars
        $UserNameComponents = $FullName.Trim().ToLower().Split(' ')

        switch ($LastNamePosition)
            {
            'Start' {
                # "index -1, index 0" for the $UserNameComponents skips any middle name or initial
                $UserName = '{0}.{1}' -f $UserNameComponents[-1], $UserNameComponents[0]
                }
            'End' {
                $UserName = '{0}.{1}' -f $UserNameComponents[0], $UserNameComponents[-1]
                }
            # this is a binary choice, so the "default" is not needed
            #Default { $firstName = $UserNameComponents.Length -1; $lastName = 0 }
            }

        # there is no need to assign the output to anything
        #    whatever is left unassigned will be sent out
        # there is also no need for "return"
        #    that is disrecommeded since it gives the false impression that ONLY the item to its right will be returned
        # the "-replace" operator can be chained. so can the ".Replace()" method
        #    that allows us to skip breaking things into an array of chars
        $UserName -replace 
            'č', 'c' -replace
            'ć', 'c' -replace
            'đ', 'dj' -replace
            'š', 's' -replace
            'ž', 'z'

        } # end >>> process

    end {}

    } # end >>> function Get-UserNameFromFullName

run with all names, no parameters, and using the pipeline ...

$InStuff |
    Get-UserNameFromFullName

output ...

djordjevic.cedomir
zoran.jankov
gradimir.stojic
zeljko.vucicevic
boskic.magda

run with all parameters listed and only one input value ...

Get-UserNameFromFullName -FullName $InStuff[-1] -LastNamePosition End

output = magda.boskic

\$\endgroup\$
2
  • \$\begingroup\$ Thank you for your great advice! I have created a new version of my function based on your suggestions. \$\endgroup\$ – Zoran Jankov Aug 8 '20 at 15:15
  • 1
    \$\begingroup\$ @ZoranJankov - you are most welcome! glad to have helped somewhat ... [grin] \$\endgroup\$ – Lee_Dailey Aug 9 '20 at 5:27
0
\$\begingroup\$

I have created a new version of my function based on Lee_Dailey suggestions.

function Get-UserNameFromFullName
{
    [CmdletBinding()]
    param
    (
        [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string]
        $FullName,

        [Parameter(Position = 1, ValueFromPipelineByPropertyName)]
        [switch]
        $ReverseNamePositions = $false
    )
    
    process
    {    
        if ($ReverseNamePositions)
        {
            $Front = -1
            $End = 0
        }
        else
        {
            $Front = 0
            $End = -1
        }

        $UserNameComponents = $FullName.Trim().ToLower().Split(' ')

        $UserName = '{0}.{1}' -f $UserNameComponents[$Front], $UserNameComponents[$End]

        $UserName -replace 'č', 'c' `
                  -replace 'č', 'c' `
                  -replace 'ć', 'c' `
                  -replace 'đ', 'dj' `
                  -replace 'š', 's' `
                  -replace 'ž', 'z'
    }
}

If someone thinks that the function is lacking something feel free to tell me. I would be grateful.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ backticks are ... problematic ... in that they are easy to overlook. that means you can do some editing, leave one behind, and then spend HOURS tracking down the reason for your code misbehaving. [grin] try to avoid the backtick whenever you can. that is why i used that odd-looking structure in my version. ///// otherwise, very neat code! [grin] \$\endgroup\$ – Lee_Dailey Aug 9 '20 at 5:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.