Monthly Archive

Categories

PowerShell

Count the occurrence of given character in a string

Last time I showed how to get the number of occurrences of each character in a string but how do you count the occurrence of given character in a string?

 

You use one of the fundamental concepts on which PowerShell is built – its composability. In other words PowerShell is composed of a lot of very small pieces of code that each do its own job and you select and assemble the components you need to complete a given task.

 

What that boils down to is that you already have a function that counts the number of occurrences of a character in a string. Rather than writing another function, or spending time modifying your existing function, see if you use other PowerShell cmdlets (or functions) help you get the result you need. In this case:

 

PS> measure-occurrence -teststring 'cwfhgfhcdsgfchgfegfegfkvcnfdhvjewy\dfsa' | where Name -eq 's'

Count Name
----- ----
2 s

 

Gives you the result you want without any further work.

 

Two ways to use the function makes it more cost effective to write.

Count occurrence of characters in a string

How do you count occurrence of characters in a string?

 

Group-object is a ready made cmdlet to answer this question

function measure-occurrence {
[CmdletBinding()]
param (
[string]$teststring
)

$teststring.ToCharArray() |
Group-Object -NoElement |
Sort-Object -Property Count -Descending

}

 

Split the string into a character array. Pipe into group-object using –NoElement to drop the data and sort the results in descending order.

 

PS> measure-occurrence -teststring 'cwfhgfhcdsgfchgfegfegfkvcnfdhvjewy\dfsa'

Count Name
----- ----
8 f
5 g
4 c
4 h
3 d
3 e
2 w
2 s
2 v
1 k
1 n
1 j
1 y
1 \
1 a

Test if string contains numeric

How can you test if a string contains a numeric character?

The simple answer is to use a regular expression. If you’ve been reading my stuff for any length of time you’ll know how much I love regular expressions. This is a simple regex.

Create a string

PS> $s1 = 'qwertyuiop'

Test if it has numerics

PS> $s1 -match '\d'
False

if you use D you’re testing for non numeric characters
PS> $s1 -match '\D'
True

 

Lets add a numeric to confirm

PS> $s2 = 'qwert6yuiop'
PS> $s2 -match '\d'
True

This time we get a positive result

The test for non numerics still works
PS> $s2 -match '\D'
True

 

Reverse a string

I sort of brushed over it on my last post but this is how you reverse a string.

 

function get-reversestring {
[CmdletBinding()]
param (
[string]$teststring
)

$ca = $teststring.ToCharArray()

[array]::Reverse($ca)

-join $ca
}

 

Take the input string and turn it into an array of chars. Use the reverse static method of the array class to reverse the array. Other useful array methods can be found in the documentation - https://docs.microsoft.com/en-us/dotnet/api/system.array?redirectedfrom=MSDN&view=netframework-4.7.2

-Join is used to stitch the reversed char array back into a string.

Test if a string is a palindrome

This is the first in a short series in which I’ll look at some string handling techniques. PowerShell is all about objects but sometimes you just have to work with the basics. In this post I’ll show how to test in a string is a palindrome.

 

A palindrome is a list of characters that read the same backwards as well as forwards. Radar is a word that is the same when reversed for example – its a simple palindrome. You can have phrases that are palindromes as well.

 

function test-palindrome {
[CmdletBinding()]
param (
[string]$teststring
)

Write-Verbose -Message "Input string: $teststring"
$cr = $teststring.ToCharArray()

$ca = $cr | where {$_ -match '\w'}

$clnstring = -join $ca
Write-Verbose -Message "Clean string: $clnstring"

[array]::Reverse($ca)

$revstring = -join $ca
Write-Verbose -Message "Reversed string: $revstring"

## make test case insensitive
if ($revstring -ieq $clnstring){
$true
}
else {
$false
}

}

 

There isn’t a method on the string class to reverse a string so convert the string to an array of chars then use Where-object to filter out non-word characters e.g. punctuation. Recreate the input string without the punctuation etc then use the Reverse static method on the array class. Use –join to create the reversed string from the char array.

 

Use –ieq (force case insensitive comparison) to test if the two strings are equal and return true or false accordingly.

 

Some simple examples:

PS> test-palindrome -teststring radar
True

PS> test-palindrome -teststring Radar
True

PS> test-palindrome -teststring Richard
False

 

And testing a phrase:

PS> test-palindrome -teststring "Madam, I'm Adam" -Verbose
VERBOSE: Input string: Madam, I'm Adam
VERBOSE: Clean string: MadamImAdam
VERBOSE: Reversed string: madAmImadaM
True

PS> test-palindrome -teststring 'The fat cat sat on the mat'
False

Third way to find pairs for given sum

There’s a third way to find pairs for given sum that’s a bit more complicated.

function get-pairs2 {
[CmdletBinding()]
param (
[int[]]$iarray,

[int]$value
)

$sarray = $iarray | Sort-Object

Write-Information -MessageData "Array: $iarray" -InformationAction Continue
Write-Information -MessageData "Sorted Array: $sarray" -InformationAction Continue
Write-Information -MessageData "Sum: $value" -InformationAction Continue

$left = 0
$right = $sarray.Count - 1

while ($left -lt $right){
Write-Verbose -Message "left = $left right =$right"
$sum = $sarray[$left] + $sarray[$right]

if ($sum -eq $value){
Write-Information -MessageData "Pair to give sum: ($($sarray[$left]), $($sarray[$right]))" -InformationAction Continue
$left += 1
$right -= 1
}
elseif ($sum -lt $value) {
$left += 1
}
elseif ($sum -gt $value){
$right -= 1
}
}
}

 

In this technique start by sorting the array. I’ve sorted it ascending which is the default.

 

Set the initial counters to the start and end of the sorted array. Use the while loop to work through the array. Sum the array elements and test if the sum equals the required value – if it does print the results and increment the left counter and decrement the low counter (move them closer together).

 

If the sum is less than than the required value increment the left counter and if its more decrement the right counter.

 

Eventually the counters will meet, the right counter will be greater than the left counter and the loop will finish.

 

This technique takes a bit of thinking about so this is a run with the counter values printed

PS> get-pairs2 -iarray $iarray -value 7 -Verbose
Array: 1 8 3 -3 6 4 9 5 10 2
Sorted Array: -3 1 2 3 4 5 6 8 9 10
Sum: 7
VERBOSE: left = 0 right =9
Pair to give sum: (-3, 10)
VERBOSE: left = 1 right =8
VERBOSE: left = 1 right =7
VERBOSE: left = 1 right =6
Pair to give sum: (1, 6)
VERBOSE: left = 2 right =5
Pair to give sum: (2, 5)
VERBOSE: left = 3 right =4
Pair to give sum: (3, 4)

 

Next time we’ll start to look at some string handling techniques

A more elegant way to find pairs

Last time I showed a brute force way to find the pairs of numbers in an array that would sum to a given value. This time I have a more elegant way to find pairs.

 

function get-pairs1 {
[CmdletBinding()]
param (
[int[]]$iarray,

[int]$value
)

Write-Information -MessageData "Array: $iarray" -InformationAction Continue
Write-Information -MessageData "Sum: $value" -InformationAction Continue

foreach ($n in $iarray){
$target = $value - $n

if ($target -in $iarray) {
Write-Information -MessageData "Pair to give sum: ($n, $target)" -InformationAction Continue
}
}
}

 

Iterate through the array and for each element subtract the element from the required value. Test to see if this result is a member of the array and print out the pair if it is.

 

This has the advantage of printing the pair each way so for an example array

Array: 1 8 3 -3 6 4 9 5 10 2

 

You’ll get

Pair to give sum: (1, 6)

and

Pair to give sum: (6, 1)

Find pairs that give required sum

If you have an array of integers how do you find pairs that give required sum. In other words which pairs of numbers add up to a given value.

 

There are a number of ways to solve this. Lets start with my favourite technique – brute force.

function get-pairs {
[CmdletBinding()]
param (
[int[]]$iarray,

[int]$value
)

Write-Information -MessageData "Array: $iarray" -InformationAction Continue
Write-Information -MessageData "Sum: $value" -InformationAction Continue

for ($i=0; $i -le ($iarray.Count -1); $i++){

for ($j= $i+1; $j -le ($iarray.Count -1); $j++){

if( ($iarray[$i] + $iarray[$j]) -eq $value) {

Write-Information -MessageData "Pair to give sum: ($($iarray[$i]), $($iarray[$j]))" -InformationAction Continue

}
} 
}
}

 

Start by outputting the array and the required value for reference. Iterate through the array then for each value compare that with the values ahead of it in the array. You don’t need to compare earlier values because they’ve already been done – counter intuitive but correct as if you have a 1 and a 6 in the array you only count them once not as 1 and 6 and 6 and 1.

PS> $iarray = 1,8,3,-3,6,4,9,5,10,2

 

PS> get-pairs -iarray $iarray -value 7
Array: 1 8 3 -3 6 4 9 5 10 2
Sum: 7
Pair to give sum: (1, 6)
Pair to give sum: (3, 4)
Pair to give sum: (-3, 10)
Pair to give sum: (5, 2)

 

There are more elegant and quicker solutions that I’ll cover in subsequent posts

Reverse an array

Ever needed to reverse an array?

If its sorted then sorting in the opposite direction will work. Most arrays aren’t sorted so you need to use the Reverse static method of the array class

 

Here’s some examples

$carray = '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'
$carray -join ','
[array]::Reverse($carray)
$carray -join ','

 

$iarray = 1,2,3,4,5,6,8,9,10
"$iarray"
[array]::Reverse($iarray)
"$iarray"

 

$psa = Get-Process p*
$psa
[array]::Reverse($psa)
$psa

 

The thing to note is that the array is reversed rather than creating output so if you do this

$iarray = 1,2,3,4,5,6,8,9,10
$newary = [array]::Reverse($iarray)

$iarray is reversed and $newary is empty!

 

If you need the original and reversed arrays take a copy of the original and then reverse one of them.

Finding the minimum and maximum values

Finding the minimum and maximum values in a set of numbers is ridiculously easy with PowerShell.

I’ve created a function in line with the other techniques I’ve shown but in reality could be be done equally well as a single line of code:

function get-minmax {
[CmdletBinding()]
param (
[int[]]$iarray
)

$mm = $iarray | Measure-Object -Minimum -Maximum

New-Object -TypeName PSobject -Property @{
Minimum = $mm.Minimum
Maximum = $mm.Maximum
}
}

 

Just pipe the array into Measure-Object and use the –Minimum and –Maximum parameters as shown. I created an output object for easier handling if you want to do anything else to the array.

 

You can also get the sum and average of the array with Measure-Object. PowerShell v6.1 adds the Standard Deviation and an –Allstats parameter so you don’t need to specify each individual option:

PS> $iarray = 1,2,3,4,23,5,6,7,8,9,10,23,11,12,13,7,14,15,16,17,18,20,21,22,11,23,24,25
PS> $iarray | Measure-Object -AllStats

Count : 28
Average : 13.2142857142857
Sum : 370
Maximum : 25
Minimum : 1
StandardDeviation : 7.46030057411125
Property :