powershell script to make array elements as the keys of hashtable - powershell

I want to assign all the array elements as the keys of hash table. For Example...
# arrays
$k=#(1,2,3)
$v=#("one","two","three")
# hashtable
$table=#{}
I want output like this:
$table={1="one",2="two",3="three"}
Is there any way to do it?

0..($k.count-1) |
Foreach-Object -Begin {
$table=[ordered]#{}
} -process {
$table.Add($k[$_],$v[$_])
}
Assuming each array is the same size, you can loop through your indexes and grab elements at the same index from both arrays. The Add(key,value) method adds new key-value pairs to your hash table.

You can also accomplish this with a for each loop to be able to easily track the index between both arrays
$k = #(1, 2, 3)
$v = #("one", "two", "three")
$table = #{}
for ($i=0; $i -lt $k.Length; $i++){
$table[$k[$i]] = $v[$i]
}
Note: this is also somtimes referred to as "zipping" two arrays

Related

How to compare values in arrays and add the value from one array to another

I've got 2 arrays:
$array1 = ('1','2','7','9')
$array2 = ('7','9','1','2')
These collections are dynamically changed and I need to add the values from the first array to second. I need to create a condition if.... and check... if these arrays are equals - do nothing.
But, if the first array has new value for example it becomes $array1 = ('1','2','7','9', '6') and second array doesn't have such value
then I need to add this '6' new value to the second array...so the second array will become $array2 = ('7','9','1','2', '6')... please help to achieve it.
Please note that values in 2 arrays could be in different order it doesn't matter, the goal is to have the same values inside 2 arrays.
Merge the two and sort out non-unique values:
$array2 = #($array1;$array2) |Sort-Object -Unique
For large arrays you may want to add all items from both arrays to a HashSet instead - it'll only store distinct values, so the resulting set will correspond to the new value of $array2:
$set = [System.Collections.Generic.HashSet[psobject]]::new()
$array1 |% { [void]$set.Add($_) }
$array2 |% { [void]$set.Add($_) }
$array2 = #($set)
If you want to retain the order in $array2 and append those elements (in order) from $array1 that aren't yet in $array2, you can use Compare-Object:
$array1 = '1','2','7','9','6'
$array2 = '7','9','1','2'
$array2 +=
(Compare-Object -PassThru $array1 $array2 | Where-Object SideIndicator -eq '<=')
Note: Compare-Object, like PowerShell in general, is case-insensitive by default; add -CaseSensitive if needed.
You can use IsSupersetOf and UnionWith from HashSet<T>:
$array1 = [string[]] ('1','2','7','9','new value')
$array2 = [System.Collections.Generic.HashSet[string]] ('7','9','1','2')
if(-not $array2.IsSupersetOf($array1)) {
$array2.UnionWith($array1)
}
$array2
In case you need to have an actual array back you can cast [object[]] or [string[]] back depending on your need:
[string[]] $array2
Worth mentioning that hashsets are case sensitive by default, in case you need a case-insensitive hashset you can instantiate it using a case-insensitive comparer:
$array2 = [System.Collections.Generic.HashSet[string]]::new(
[string[]]('7','9','1','2'),
[System.StringComparer]::OrdinalIgnoreCase
)
You could build on top of this solution, by creating a function.
This code block lists array 1 and array 2
It uses the -notcontains cmdlet to check if the elements in array are in array2 and if not, then appends the element in array 2.
$array = #('1','2','7','9', '15')
$array2 = #('1','2','7','9')
Write-Output "$array `n"
Write-Output "$array2 `n"
$array | ForEach-Object {
if ($array2 -notcontains $_) {
$array2 += $_
}
}
Write-Output "$array2"
Hope this helps!

Casting Object to String Array Powershell

I want to create an array of strings instead of a variable object so that I can use the "contains" keyword on each index of the array.
$myArray = Get-ADDomain
The above creates an object, which is not what I want. I also tried
[string[]] $myArray = Get-ADDomain
But after that, $myArray only contains one string and it is the first non-empty property of Get-ADDomain, in my case "ComputersContainer". What should I do to receive an array of strings where each string is a different property, such as
$myArray[0] = "AllowedDNSSuffixes = {}"
PowerShell will always return objects by design of course, and specifying that [string[]], does not really change that.
For what you are trying to use, you have to force the array creation. The below is just one way, but I am sure others will have more elegant ways of doing this as well. Though I am curious why one would want to do this, this way. But, hey, that's just me.
# Create an empty array
$DomainData = #()
# Get all the data points for the utilized cmdlet, split on a common delimiter for the array
[string[]]$DomainData = (Get-ADDomain | Select *) -split ';'
# Display the array count
$DomainData.Count
34
# validate getting a value from the array by using an index number
$Item = $DomainData[17]
NetBIOSName=CONTOSO
[array]::IndexOf($DomainData, $Item)
17
# Use that element number to validate the use of the contains comparison operator
0..($DomainData.Count - 1) | %{ If($DomainData[$_] -contains $item){"Index key is $_ contains a value of $Item"} }
Index key is 17 contains a value of NetBIOSName=CONTOSO
# Use the previous with a partial string for a comparison, -contains cannot be used, like or match has to be used
# From the documentation:
# -Contains
# Description: Containment operator. Tells whether a collection of reference values includes a single test value.
$Item = '*domain*'
0..($DomainData.Count - 1) | %{ If($DomainData[$_] -like $item){"Index key is $_ like a value of $Item"} }
Index key is 1 like a value of *domain*
Index key is 6 like a value of *domain*
Index key is 7 like a value of *domain*
Index key is 8 like a value of *domain*
Index key is 18 like a value of *domain*
Index key is 20 like a value of *domain*
You cannot cast a PSObject directly to a string array like that.
However, this can be accomplished rather easily.
To get an array of string from the object
$myArray = Get-ADDomain
# You can use a standard array #() but these tends to be slower for bigger amount of data
$outArray = New-Object -TypeName System.Collections.Generic.List[String]
#To add just the value
$myArray.psobject.properties | Foreach { $outArray.Add($_.Value) }
# To add Name = {Value} instead
$myArray.psobject.properties | Foreach { $outArray.Add("$($_.Name) = {$($_.Value)}") }
Using an hasthable instead:
$myArray = Get-ADDomain
$hashtable = #{}
$myArray.psobject.properties | Foreach { $hashtable[$_.Name] = $_.Value }
# If you need to do something with the key
Foreach ($key in $hashtable.Keys) {
$Value = $hashtable[$key]
if ($value -like '*prod*') {
Write-Host $key
}
}

Assigning values from one array to another in Powershell without encountering null index when de-referencing elements

I have an array with fields (a multi dimensional array, if I am correct), my aim is to create another array based on elements filtered from the first.
However when I come to deference $arr2 by looking at its first element $arr[0], I get the error message:
Cannot index into a null array
Can someone please provide me with some advise as to the most elegant way of resolving this.
for ($i=0; $i -lt $arr1.length; $i++) {
if ($arr1[$i].source -eq $SomeValue) {
$arr2 += #( $arr1[$i] )
}
}
$arr2 = #( $arr1 | Where { $_.source -eq $someValue } )

Why is Powershell Flattening my Array?

$arrays = #();
$array = #();
$string = 'one|two|three';
$array = ($string -split '\|');
$arrays += $array;
$arrays[0][0]
I expected $arrays to be a two dimensional array the first element of which would be a reference to $array. Thus, $arrays[0][0] would contain the string 'one'.
Instead PoSh seems to be flattening $arrays into a single list containing the elements 'one', 'two', and 'three'. This is true even if I put the #() array constructor around $array in my append operation.
Adding an additional element to my append gets me close, but then I'll have empty elements in $arrays:
$arrays = #();
$array = #();
$string = 'one|two|three';
$array = ($string -split '\|');
$arrays += $array, '';
$arrays[0][0]
Think of PowerShell's + operator with an array as the LHS as concatenating arrays, i.e., appending the individual elements of the RHS to the LHS array[1]
:
# Note: , has higher precedence than +, so this is the same as: ((1, 2) + (3, 4)).Count
> (1, 2 + 3, 4).Count # Same as: (1, 2, 3, 4).Count
4
even if I put the #() array constructor around $array
It is , that is the array-construction operator.
#() is the array sub-expression operator - its purpose is to ensure that the output from the enclosed command becomes an array unless it already is one.
In other words: Something like #(1, 2) is a no-op, because 1, 2 already is an array.
Therefore, as you've discovered, using , to construct a nested array is the solution:
> (1, 2 + , (3, 4)).Count
3
, (3, 4) wraps array 3, 4 in a single-element array.
+ then adds each element of that array - the one and only element that is the wrapped 3, 4 array - to the LHS.
[1] Let's not forget that a .NET array is an immutable data structure,
so what is really happening is that, behind the scenes, PowerShell constructs a new array.
After some additional research I found this post. This is, apparently, done by design. Adding a comma prior to $array in my append operation fixes the problem:
$arrays = #();
$array = #();
$string = 'one|two|three';
$array = ($string -split '\|');
$arrays += ,$array;
$arrays[0][0]

Array of strings to array of array

Sometimes PowerShell is completely awesome and other times it is completely frustrating and unintuitive. It is almost always an array that is causing me grief.
This time I have an array of strings. I want to split each string on white space so that I end up with an array of arrays of strings. I have tried this:
$data | ForEach-Object { $_.Split(#(), [System.StringSplitOptions]::RemoveEmptyEntries) }
But that just flattens everything into one large array of strings like SelectMany in C#. I have also tried this:
$data | Select-Object { $_.Split(#(), [System.StringSplitOptions]::RemoveEmptyEntries) }
But that gives me an array of PsCustomObject. I feel like this should be incredibly easy. Am I missing something completely obvious?
You can put unary comma (array) operator to prevent PowerShell to enumerate an array, returned by Split method:
$data | ForEach-Object { ,$_.Split(#(), [System.StringSplitOptions]::RemoveEmptyEntries) }
How about this? What I do here is I loop through all of the elements in the array, and then I do the split and replace the current item with the returned String[] from the Split():
$Outer = "hello there", "how are you?", "I'm good, thanks"
for ($i = 0; $i -lt $Outer.Count; $i++) {
$Outer[$i] = $Outer[$i].Split(" ")
}
$Outer[1][2]
# you?
$Outer[2][0]
# I'm