How to map 2 arrays of object using PowerShell? - arrays

I would like to map an array object. I need to find the matched object of array. After the object match, I need to stop checking other objects, then continue to the next process.
I tried this, but it always return not match, even if the array object match exists.
$ID = #("8537", "8538", "8539", "8540", "85AC", "85DE", "82EA")
$Signal = #("8537", "8220")
for ($i = 0; $i -lt $Signal.count; $i++)
{
if ($Signal[$i] -like "$ID[$i]")
{
"found ‘$($Signal[$i])’
at index $i"
# Do some process
}
else {
"Not Match"
# Do some process
}
}

If you need to go Match process if exists at least 1 match object, you can try this:
$ID = #("8538", "8539", "8540", "85AC", "85DE", "82EA","8537")
$Signal = #("8537","8220","85DE")
$matched = $false
for ($i = 0; $i -lt $ID.count; $i++) {
if ($matched) {
break
}
for ($j = 0; $j -lt $Signal.count; $j++) {
if ($ID[$i] -like $Signal[$j])
{
$matched = $true
"found $($ID[$i]) at index $i"
break
}
}
}
if ($matched) {
# Do some process
} else {
"Not Match"
# Do some process
}

Something like that you should test :
cls
$ID = #("8538", "8539", "8540", "85AC", "85DE", "82EA","8537")
$Signal = #("8537","8220","85DE")
for ($i = 0; $i -lt $ID.count; $i++) {
for ($j = 0; $j -lt $Signal.count; $j++) {
if ($ID[$i] -like $Signal[$j])
{
"found $($ID[$i]) at index $i"
# Do some process
}
else
{
"Not Match"
# Do some process
}
}
}

As mentioned in my comment above - to make your example work you can use a nested loop like this:
$ID = #("8537", "8538", "8539", "8540", "85AC", "85DE", "82EA")
$Signal = #("8234", "8512", "8220")
$MatchFound = $false
for ($i = 0; $i -lt $Signal.count; $i++) {
for ($x = 0; $x -lt $ID.Count; $x++) {
if ($Signal[$i] -eq $ID[$x]) {
"found '$($Signal[$i])' at index $i"
$MatchFound = $true
}
}
}
if (-not $MatchFound) {
"No match was found"
}
If you have a complex object you want to compare you can take a look at the cmdlet Compare-Object.

Related

Powershell - Creating an 2D array inside a class

I'm trying to create a "matrix / multidimensional array) inside a class function whit a loop.
Also (because I'm used to other languages) couldn't get it to work.
Is there somebody who could help me to get this working / or could explain to me what I'm doing wrong?
Class Matrix {
[int]$row
[int]$column
[int]$counter
$data
Matrix() {
Write-Host "No rows and columns provided"
}
Matrix($row, $column){
#Write-Host "row = " $row " column = " $column
$array = #(,#())
for ($i=0;$i -le $row -1 ; $i++) {
#Write-Host "This is row number " + $i
for ($j=0;$j -le $column -1 ; $j++) {
#Write-Host "This is column number " + $j
#00,01,02..10,11,12..
$array[$i][$j] = 2
}
}
$this.row = $row
$this.column = $column
$this.counter = 0
$this.data = $array
}
[void]add($adder){
if ($adder.GetType().name -eq "Matrix"){
Write-Host "You had given a matrix not a number"
}Else{
$this.counter = $this.counter + $adder
}
}
}
$matrix1 = [Matrix]::New(4,3)
$matrix1
I got it :)
for the people who are searching this , here is the code I used :
Matrix($row,$col){
$temp_array = New-Object 'object[,]' $row,$col #<<<<============
for ($i=0;$i -le $row -1 ; $i++) {
for ($j=0;$j -le $col -1 ; $j++) {
$temp_array[$i,$j] = Get-Random -Minimum 0 -Maximum 10
}
}
$this.row = $row
$this.col = $col
$this.counter = 0
$this.prosessed = 0
$this.data = $temp_array #<<<<============
}

Why is my array being read as null? What am I missing?

I am working on PowerShell script that will check server services. I keep getting a "Cannot index into a null array."
The error references the second if statement "if ($select_string_result.Line[$select_string_result.Line.Length-1] -eq '1')"
The object type of "$select_string_result" is displayed as Array and the txt file has data but the script will not process through it.
The "Line" property of the array records as null and the "Length" is recorded as 0.
$filepathserver = 'Path'
$filepathlocal = 'Path'
function Get-Timestamp
{
return Get-Date -Format "MM/dd/yyyy hh:mm:ss tt"
}
function refresh-data
{
# Pulls Services and Services Status
$orionData = Get-SwisData $swis "SELECT ComponentID, StatusDescription FROM Orion.APM.Component"
# Sends output to a txt file
$orionData | Out-File "$filepathlocal\All_App_Services.txt"
}
function check-status($select_string_result)
{
if ($select_string_result.Line -isnot [system.array])
{
if ($select_string_result.Line[$select_string_result.Line.Length-1] -eq '1')
{
return 100
}
else
{
return 0
}
}
else
{
$sum = 0.0
$add = 100.0/$select_string_result.Length
foreach ($match in $select_string_result)
{
if ($match.Line[$match.Line.Length-1] -eq '1')
{
$sum += $add
}
}
if ($sum -lt 100) {$sum = 0} # this line collapses the values in to either 0 or 100
$sum = [int][Math]::Ceiling($sum)
return $sum
}
}
function main
{
refresh-data
# Filters for Application specific Services
$f = #("94944 ", "94945 ", "94951 ", "94946 ", "94942 ", "94948 ", "94949 ", "94950 ", "94943 ", "94947 ", "94952 ", "94953 ")
$AppServices = Get-Content "Path" | Select-String $f
$AppServices | Set-Content "Path"
#Removes leading spaces from array
(Get-Content "$filepathlocal\File.txt") -replace "Up","1" | % {$_.trim()} | Out-File "$filepathlocal\File.txt"
$AppServices = Get-Content "$filepathlocal\File.txt"
$AppServices.GetType()
# Writes status of each group to .txt file
$logfile= "$filepathserver\ServicesStatus.txt"
$t = Get-Timestamp
$v = check-status $AppServices
$s = "$t|Application-Services|$v"
$s | Out-File $logfile -Append -Encoding "UTF8"
$s
}
main
$select_string_result.Line resolves to $null because the array of strings that you get from Get-Content does not have a Line property, so the if statement should look more like:
if($select_string_result[$select_string_result.Length - 1] -eq '1') { ... }
PowerShell also allows you to address the last index with just -1, allowing us to simplify the statement as:
if($select_string_result[-1] -eq '1') { ... }
That being said, rather than attempting to check whether the parameter passed to a function is an array or not, you'd want to declare the parameter an array in the first place and then use a foreach loop over it:
function check-status([string[]]$select_string_result)
{
$sum = 0.0
$add = 100.0/$select_string_result.Length
foreach ($match in $select_string_result)
{
if ($match[-1] -eq '1')
{
$sum += $add
}
}
if ($sum -lt 100) {$sum = 0} # this line collapses the values in to either 0 or 100
$sum = [int][Math]::Ceiling($sum)
return $sum
}
much nice, way less code.
Now, instead of attempting to index into the string, I'd suggest using the -like wildcard operator or the -match regex operator to check whether each string ends with 1:
if ($match -like '*1')
{
$sum += $add
}
Since $sum is always exactly 100, or otherwise gets reset to 0, the call to [Math]::Ceiling() is redundant and can be removed:
function check-status([string[]]$select_string_result)
{
$sum = 0.0
$add = 100.0/$select_string_result.Length
foreach ($match in $select_string_result)
{
if ($match -like '*1')
{
$sum += $add
}
}
if ($sum -lt 100) {$sum = 0} # this line collapses the values in to either 0 or 100
return $sum
}
If you look carefully at the function as implemented, you'll notice that the only case in which 100 is returned is when all strings in $select_string_result end in 1.
We can easily test for this by using the -like operator directly on our input array, it will act as a filter operator:
function check-status([string[]]$select_string_result)
{
if(#($select_string_result -like '*1').Count -eq $select_string_result.Count)
{
$sum = 100
}
else
{
$sum = 0
}
return $sum
}
Now, another way of asserting that all strings in the array end in 1, is to simply test whether no string does not end in 1:
function check-status([string[]]$select_string_result)
{
if(#($select_string_result -notlike '*1'))
{
$sum = 0
}
else
{
$sum = 100
}
return $sum
}
Now all we need to do is shine it up a bit, like change check to a more appropriate verb and we've got a much nicer, short or powershell-idiomatic function :-)
function Measure-StatusValue
{
param(
[ValidateNotNullOrEmpty()]
[string[]]$StatusStrings
)
if(#($StatusStrings -notlike '*1'))
{
return 0
}
return 100
}

Continue while from nested for-loop

I have the following loop structure:
while ($reader.Read() -eq $true)
{
$row = #{}
for ($i = 0; $i -lt $reader.FieldCount; $i++)
{
if(something...)
{
#continue with while
}
}
#do more stuff...
}
Now, is there any way to continue from within the for loop with the next iteration of the outer while loop without any break-variable? So if "something is true" I do not want to go #do more stuff but instead do the next $reader.read(). Continue only goes to the next iteration of the for loop. Break will only break the for loop.
Factoring out the inner loop to a function could improve readability, depending on how tangled up your variables are.
function processRow($reader) {
$row = #{}
for ($i = 0; $i -lt $reader.FieldCount; $i++)
{
if(-not something...) { return $null }
# process row
}
$row
}
while ($reader.Read()) {
$row = processRow $reader
if ($row) {
#do more stuff...
}
}
But if you want to do this directly, you can, because PowerShell has labeled breaks:
:nextRow while ($reader.Read()) {
$row = #{}
for ($i = 0; $i -lt $reader.FieldCount; $i++) {
if(something...) {
#continue with while
continue nextRow
}
}
#do more stuff...
}
EDIT: a revised, recursive (and untested!) solution so your millage may vary:
function doReader()
{
while ($reader.Read() -eq $true)
{
$row = #{}
for ($i = 0; $i -lt $reader.FieldCount; $i++)
{
if(something...)
{
#continue with while
doReader
break;
}
}
}
}
doReader
#do more stuff

Get all combinations of an array

I'm currently trying to make a function that gets all possible combinations of array values.
I have come up with a non function version but it's limited to 3 values so i'm trying to make a function out of it to become more Dynamic
I tried searching SO but could not find a powershell example of what i was trying to do, i could find a PHP version but i'm very limited in my PHP
PHP: How to get all possible combinations of 1D array?
Non-function Script
$name = 'First','Middle','Last'
$list = #()
foreach ($c1 in $name) {
foreach ($c2 in $name) {
foreach ($c3 in $name) {
if (($c1 -ne $c2) -and ($c2 -ne $c3) -and ($c3 -ne $c1))
{
$list += "$c1 $c2 $c3"
}
}
}
}
This gives me the result
First Middle Last
First Last Middle
Middle First Last
Middle Last First
Last First Middle
Last Middle First
I'm not sure how i would rearrange the values when i'm recursing the function, this is what i have so far:
<#
.Synopsis
Short description
.DESCRIPTION
Long description
.EXAMPLE
Example of how to use this cmdlet
.EXAMPLE
Another example of how to use this cmdlet
#>
function Get-Combinations
{
[CmdletBinding()]
[OutputType([int])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string[]]$Array,
# Param1 help description
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$false,
Position=1)]
[string]$Temp,
# Param1 help description
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=2)]
[string[]]$Return
)
Begin
{
Write-Verbose "Starting Function Get-Combinations with parameters `n`n$($Array | Out-String)`n$temp`n`n$($Return | Out-String)"
If ($Temp)
{
$Return = $Temp
}
$newArray = new-object system.collections.arraylist
}
Process
{
Write-Verbose ($return | Out-String)
For($i=0; $i -lt $Array.Length; $i++)
{
#Write-Verbose $i
$Array | ForEach-Object {$newArray.Add($_)}
$newArray.RemoveAt($i)
Write-Verbose ($newArray | Out-String)
if ($newArray.Count -le 1)
{
Get-Combinations -Array $newArray -Temp $Temp -Return $Return
}
else
{
$Return = $Temp
}
}
$newArray
}
End
{
Write-Verbose "Exiting Function Get-Combinations"
}
}
$combinations = #("First","First2","Middle","Last")
$Combos = Get-Combinations -Array $combinations
$Combos
But the output i'm getting is all over the place
First2
Last
First2
Last
First
First2
Middle
Last
First
First2
Middle
Last
28/08 Update
Getting closer but still getting weird output
<#
.Synopsis
Short description
.DESCRIPTION
Long description
.EXAMPLE
Example of how to use this cmdlet
.EXAMPLE
Another example of how to use this cmdlet
#>
function Get-Combinations
{
[CmdletBinding()]
[OutputType([int])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string[]]$Array,
# Param1 help description
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$false,
Position=1)]
[string]$Temp,
# Param1 help description
[Parameter(Mandatory=$false,
ValueFromPipelineByPropertyName=$true,
Position=2)]
[string[]]$Return
)
Begin
{
Write-Verbose "Starting Function Get-Combinations with parameters `n`n$($Array | Out-String)`n$temp`n`n$($Return | Out-String)"
If ($Temp)
{
$Return += $Temp
}
#$newArray = new-object [System.Collections.ArrayList]
#$Array | ForEach-Object {$newArray.Add($_) | Out-Null}
[System.Collections.ArrayList]$newArray = $Array
}
Process
{
Write-Verbose "return -> $return"
For($i=0; $i -lt $Array.Length; $i++)
{
Write-Verbose "`$i -> $i"
$element = $newArray[0]
$newArray.RemoveAt(0)
Write-Verbose "`$newArray -> $newArray"
Write-Verbose "Element -> $element"
if ($newArray.Count -gt 0)
{
Get-Combinations -Array $newArray -Temp (($temp + " " +$element).Trim()) -Return $Return
}
else
{
$Return = $Temp + " " + $element
}
}
$return
}
End
{
Write-Verbose "Exiting Function Get-Combinations"
}
}
$combinations = #("First","First2","Middle","Last")
$return = #()
$Combos = Get-Combinations -Array $combinations -Return $return
$Combos
New output (Yes there is a space before the 'Last' value, no i have no idea why)
First First2 Middle Last
First First2 Last
First Middle Last
First Last
First2 Middle Last
First2 Last
Middle Last
Last
Here is my solution:
function Remove ($element, $list)
{
$newList = #()
$list | % { if ($_ -ne $element) { $newList += $_} }
return $newList
}
function Append ($head, $tail)
{
if ($tail.Count -eq 0)
{ return ,$head }
$result = #()
$tail | %{
$newList = ,$head
$_ | %{ $newList += $_ }
$result += ,$newList
}
return $result
}
function Permute ($list)
{
if ($list.Count -eq 0)
{ return #() }
$list | %{
$permutations = Permute (Remove $_ $list)
return Append $_ $permutations
}
}
cls
$list = "x", "y", "z", "t", "v"
$permutations = Permute $list
$permutations | %{
Write-Host ([string]::Join(", ", $_))
}
EDIT: the same in one function (Permute). This is cheating a bit, however since I replaced plain functions whith lambdas. You could replace recursive calls with a stack you handle yourself, but that would make the code unecessarily complex ...
function Permute ($list)
{
$global:remove = {
param ($element, $list)
$newList = #()
$list | % { if ($_ -ne $element) { $newList += $_} }
return $newList
}
$global:append = {
param ($head, $tail)
if ($tail.Count -eq 0)
{ return ,$head }
$result = #()
$tail | %{
$newList = ,$head
$_ | %{ $newList += $_ }
$result += ,$newList
}
return $result
}
if ($list.Count -eq 0)
{ return #() }
$list | %{
$permutations = Permute ($remove.Invoke($_, $list))
return $append.Invoke($_, $permutations)
}
}
cls
$list = "x", "y", "z", "t"
$permutations = Permute $list
$permutations | %{
Write-Host ([string]::Join(", ", $_))
}
I tried to learn something new and help you out but Im stuck. maybe this will help you get in the right direction but I dont know enough about Powershell recursion to figure this out. I converted the php to powershell and in theory it should work but it doesnt.
$array = #('Alpha', 'Beta', 'Gamma', 'Sigma')
function depth_picker([system.collections.arraylist]$arr,$temp_string, $collect)
{
if($temp_string -ne ""){$collect += $temp_string}
for($i = 0; $i -lt $arr.count;$i++)
{
[system.collections.arraylist]$arrCopy = $arr
$elem = $arrCopy[$i]
$arrCopy.removeRange($i,1)
if($arrCopy.count -gt 0){
depth_picker -arr $arrCopy -temp_string "$temp_string $elem" -collect $collect}
else{$collect += "$temp_string $elem"}
}
}
$collect = #()
depth_picker -arr $array -temp_string "" -collect $collect
$collect
It seems to work and will get you the first set of possibles:
Alpha
Alpha Beta
Alpha Beta Gamma
Alpha Beta Gamma Sigma
But for some reason that I cant figure out when it gets back to the previous functions and does $i++ then checks ($i -lt $arr.count) $arr.count it always 0 so it never goes to the next iteration to continue finding the possibilities.
Hopefully someone else can fix what I cant seem to figure out as I dont know enough about recursion. But it seems that with each level of depth called the previous depth level $arr variable and values is lost.
Here is my solution with a recursive function. It generates space separated strings but it's quite simple to split each element with $list[$i].split(" "):
function Get-Permutations
{
param ($array, $cur, $depth, $list)
$depth ++
for ($i = 0; $i -lt $array.Count; $i++)
{
$list += $cur+" "+$array[$i]
if ($depth -lt $array.Count)
{
$list = Get-Permutations $array ($cur+" "+$array[$i]) $depth $list
}
}
$list
}
$array = #("first","second","third","fourth")
$list = #()
$list = Get-Permutations $array "" 0 $list
$list
The solution posted by Micky Balladelli almost worked for me. Here is a version that does not duplicate values:
Function Get-Permutations
{
param ($array_in, $current, $depth, $array_out)
$depth++
$array_in = $array_in | select -Unique
for ($i = 0; $i -lt $array_in.Count; $i++)
{
$array_out += ($current+" "+$array_in[$i]).Trim()
if ($depth -lt $array_in.Count)
{
$array_out = Get-Permutations $array_in ($current+" "+$array_in[$i]) $depth $array_out
}
else {}
}
if(!($array_out -contains ($array_in -Join " "))) {}
for ($i = 0; $i -lt $array_out.Count; $i++)
{
$array_out[$i] = (($array_out[$i].Split(" ")) | select -Unique) -Join " "
}
$array_out | select -Unique
}

How to append to powershell Hashtable value?

I am interating through a list of Microsoft.SqlServer.Management.Smo.Server objects and adding them to a hashtable like so:
$instances = Get-Content -Path .\Instances.txt
$scripts = #{}
foreach ($i in $instances)
{
$instance = New-Object Microsoft.SqlServer.Management.Smo.Server $i
foreach($login in $instance.Logins)
{
$scripts.Add($instance.Name, $login.Script())
}
}
So far so good. What I want to do now is append a string to the end of the hashtable value. So for an $instance I want to append a string to the hashtable value for that $instance. How would I do that? I have started with this, but I'm not sure if I'm on the right track:
foreach ($db in $instance.Databases)
{
foreach ($luser in $db.Users)
{
if(!$luser.IsSystemObject)
{
$scripts.Set_Item ($instance, <what do I add in here?>)
}
}
}
Cheers
$h= #{}
$h.add("Test", "Item")
$h
Name Value
---- -----
Test Item
$h."Test" += " is changed"
$h
Name Value
---- -----
Test Item is changed
I would go with this code.
$instances = Get-Content -Path .\Instances.txt
$scripts = #{}
foreach ($i in $instances)
{
$instance = New-Object Microsoft.SqlServer.Management.Smo.Server $i
foreach($login in $instance.Logins)
{
$scripts[$instance.Name] = #($scripts[$instance.Name]) + $login.Script().ToString()
}
}
.
foreach ($db in $instance.Databases)
{
foreach ($luser in $db.Users)
{
if(!$luser.IsSystemObject)
{
$scripts[$instance] = #($scripts[$instance]) + $luser.Script().ToString()
}
}
}
The result will be a hash table with each instance as a key, and an array of strings where each string is the T-SQL script for a user.
The .Script() method returns a string collection. There's probably a more elegant way of doing it, but replacing
$scripts.Set_Item ($instance, <what do I add in here?>)
with
$val = $scripts[$instance]
$val.Add("text to add")
$scripts.Set_Item($instance, $val)
should work.
$test = #{}
$test.Hello = "Hello World"
Write-Host "message from $($test.Hello)"
$test.Hello += " Cosmonaut"
Write-Host "message from $($test.Hello)"

Resources