Array of strings to array of array - powershell

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

Related

PowerShell replace text in string after certain expression

I am trying to replace everything in a string after a certain expression is found: In this case ",=dc".
Im kinda new to PowerShell so would appreciate any help. My code only replaces any ",=dc" but not what is behind that
$array += #()
$array += "vu=doc,vu=noc,dc=zet,dc=zez"
$array += "vu=doc,vu=nud,dc=gut,dc=zrt"
$array += "vu=doc,vu=nud,vu=dit,dc=gut,dc=zrt,dc=opt"
foreach ($value in $array){
$arraytype -replace "(?<=(,=dc)).*"
}
I want to only get back the "vu=doc,vu=noc"-part. Everything after the first ",=dc" should be deleted.
It's easier to use an indexed loop if you want the values in the array updated. Also, You can define an array simply by using a comma between the elements. Avoid concatenating with += as this means every time the entire array needs to be recreated in memory, which is both time and memory consuming.
Try
$array = "vu=doc,vu=noc,dc=zet,dc=zez",
"vu=doc,vu=nud,dc=gut,dc=zrt",
"vu=doc,vu=nud,vu=dit,dc=gut,dc=zrt,dc=opt"
for ($i = 0; $i -lt $array.Count; $i++) {
$array[$i] = ($array[$i] -replace 'dc=[^,]+,?').TrimEnd(",")
}
$array
Result:
vu=doc,vu=noc
vu=doc,vu=nud
vu=doc,vu=nud,vu=dit
Regex details:
dc= Match the character string “dc=” literally (case insensitive)
[^,] Match any character that is NOT a “,”
+ Between one and unlimited times, as many times as possible, giving back as needed (greedy)
, Match the character “,” literally
? Between zero and one times, as many times as possible, giving back as needed (greedy)
You can use IndexOf method to find the position of the string and use Substring method to slice the string up to that position.
$array = #()
$array += "vu=doc,vu=noc,dc=zet,dc=zez"
$array += "vu=doc,vu=nud,dc=gut,dc=zrt"
$array += "vu=doc,vu=nud,vu=dit,dc=gut,dc=zrt,dc=opt"
foreach ($value in $array){
$index = $value.IndexOf(",dc=")
if($index -eq -1) {
Write-Output $value
} else {
Write-Output $value.Substring(0, $index)
}
}
Note: I think you have a typo in the first line($array = #())

powershell script to make array elements as the keys of hashtable

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

Unable to remove item from hash table

In Powershell, I have a hash table that contains data similar to this -
Name Value
---- -----
1-update.bat 1
2-update.bat 2
3-update.bat 3
3.1-update.bat 3.1
4-update.bat 4
I also have an variable that contians a number, for example 3
What I would like to do is loop through the array and remove any entry where the value is less than or equal to 3
I'm thinking that this will be easy, especially as the docs say that has tables contain a .remove method. However, the code I have below fails, yeilding this error -
Exception calling "Remove" with "1" argument(s): "Collection was of a
fixed size."
Here is the code that I used -
$versions = #{}
$updateFiles | ForEach-Object {
$versions.Add($_.name, [decimal]($_.name -split '-')[0])
}
[decimal]$lastUpdate = Get-Content $directory\$updatesFile
$versions | ForEach-Object {
if ( $_.Value -le $lastUpdate ) {
$versions.Remove($version.Name)
}
}
I first tried to loop $versions in a different manner, trying both the foreach and for approaches, but both failed in the same manner.
I also tried to create a temporary array to hold the name of the versions to remove, and then looping that array to remove them, but that also failed.
Next I hit Google, and while I can find several similar questions, none that answer my specific question. Mostly they suggest using a list (New-Object System.Collections.Generic.List[System.Object]), whcih from what I can tell is of no help to me here.
Is anyone able to suggest a fix?
Here you go, you can use .Remove(), you just need a clone of the hashtable so that it will let you remove items as you enumerate.
[hashtable]$ht = #{ '1-update.bat'=1;'2-update.bat'=2;'3-update.bat'=3;'3.1-update.bat'=3.1; '4-update.bat'=4 }
#Clone so that we can remove items as we're enumerating
$ht2 = $ht.Clone()
foreach($k in $ht.GetEnumerator()){
if([decimal]$k.Value -le 3){
#notice, deleting from clone, then return clone at the end
$ht2.Remove($k.Key)
}
}
$ht2
Notice I've cast the original variable too so that it's explicitly a hash table, may not be required, but I like to do it to at least keep things clear.
It looks like you just confused ForEach-Object with foreach but only halfway (maybe it was foreach before and you converted it).
You can't send a [hashtable] directly to ForEach-Object; the $_ in that case will just refer to the single [hashtable] you sent in. You can do:
foreach ($version in $versions.GetEnumerator()) {
$version.Name
$version.Value
}
or you can do something like this:
$versions.Keys | ForEach-Object {
$_ # the name/key
$versions[$_] # the value
$versions.$_ # also the value
}
$ht.Keys # list all keys
$ht[$_] # take an element of hastable
$ht.Remove($_) # remove an element of hastable by his key
what you want:
$ht.Keys | ? { $ht[$_] -le 3 } | %{$ht.Remove($_) }
You need to create a temporary array to hold the name/key of the versions to remove, and then looping that array to remove them from hash table:
$versionKeysToRemove = $versions.Keys | Where-Object { $versions[$_] -le $lastUpdate }
$versionKeysToRemove | ForEach-Object { $versions.Remove($_) }
Or shorter:
($versions.Keys | ? { $versions[$_] -le $lastUpdate }) | % { $versions.Remove($_) }
Please note the parentheses.

how to understand foreach loop in powershell

Hi every one I am new to power shell and trying to make sense about for each loop but I don't understand what does + $ means in the below script and why they are used together
$names = "jones","mike","Ash"
foreach ($name in $names)
{
"$name = " + $name.length
}
First of all, it is PowerShell. There is no space and observe the capitalization of letters.
Second, RTFM. There is a lot of documentation on Technet. Make use of that. There is built-in help. Read Help about_* topics.
Coming to your question, the foreach loop iterates over all collection. In your example, $names is an array of strings. An array is a collection.
In your example, when the foreach loop iterates over the collection, it copies each item in the collection to another variable called $name.
foreach ($name in $names) {}
Inside the foreach loop, you can use the $name variable to retrieve the value stored in the item. So, the following code will print the values in the collection.
foreach ($name in $names) {
$name
}
$name is a string. So, $name.Length gives us the length of that string.
+ is an arithmetic operator. It can be used to concatenate strings. In this case, $name + $name.length will result in the value getting appended with the length.
Here is the modified example with output:
$names = "jones","mike","Ash"
foreach ($name in $names)
{
$name + $name.Length
}
jones5
mike4
Ash3
Finally, coming to your example and the output:
$names = "jones","mike","Ash"
foreach ($name in $names)
{
"$name = " + $name.Length
}
jones = 5
mike = 4
Ash = 3
I hope this provides some explanation for you on what the example is doing.
Let's see this step by step:
$names = "jones","mike","Ash"
This declares an array with three string items and stores it in the $names variable.
foreach ($name in $names) { }
This is a loop that iterates through all items in the $names array variable. Each item is "copied" into the "$name" variable, so when you're working with $name, you are working with the current item in the loop.
"$name = " + $name.length
This outputs the contents of the $name variable and the "length" property of the object represented by $name. If you recall, we declared an array of string items, in other words string objects. Strings objects have properties and methods, one of those properties is the length property that tells how long the string is.
The notation is a bit odd, though. In PowerShell, variables embedded into string literals (e.g. "start $name end" will be replaced with their string value, so "start $name end" will become start jones end for the first item. However, you can't just write "start $name.length end", so in this example the author used + to concatenate (append) the two strings. An alternative solution to that would be "$name = $($name.length))": this way a single string literal would contain both the string and its length property.

Powershell being too clever

Apologies for what is probably a newbish question.
I am writing some Powershell scripts that run various queries against AD. They will usually return a bunch of results, which are easy to deal with, ie:
$results = myQuery
write-host "Returned " + $results.Count + " results."
foreach ($result in $results) { doSomething }
No worries. However, if there is only 1 result, Powershell automagically translates that result into a single object, rather than an array that contains 1 object. So the above code would break, both at the Count and the foreach. I'm sure the same problem would occur with 0 results.
Could someone suggest an elegant way to handle this? Perhaps some way to cast the results so they are always an array?
Change the first line of code to
$results = #(myQuery)
This will always return an array. See this blog entry for additional details.
Actually, the foreach works just fine. All uses of foreach (the foreach keyword, the Foreach-Object cmdlet, and Foreach-Object's aliases "foreach" and "%") all have the same behavior of "wrapping" the object in question in an array if needed. Thus, all uses of foreach will work with both scalar and array values.
Annoyingly, this means they work with null values too. Say I do:
$x = $null
foreach ($y in $x) {Write-Host "Hello world 1"}
$x | Foreach-Object {Write-Host "Hello world 2"}
I'll get
"Hello world 1"
"Hello world 2"
out of that.
This has bitten me as well. No clever ideas on how to fix $results.Count, but the foreach can be fixed by switching to a pipeline.
$scalar = 1
$list = (1,2)
$list | % { $_ }
prints
1
2
$scalar | % { $_ }
prints
1