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
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!
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
}
}
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 } )
$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]
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