Powershell - output a foreach loop to one line - powershell

I am trying to take the output of my foreach loop and apply the array to a string that reads on one line. Here is my code so far:
$upper = 65..90
$lower = 97..122
foreach ($i in $upper)
{
[char]$i
}
foreach ($i in $lower)
{
[char]$i
}
I'm guessing I need to convert the output of the scriptblock to a variable and use the -join option, but everywhere I look I'm struggling to find how to structure that. Any guidance would be appreciated.

For this particular case, ForEach(type convertToType) is very useful, here is a cool way to get your lower and upper case dictionary string:
$lowerDict = [string]::new(([int][char]'a'..[int][char]'z').ForEach([char]))
$upperDict = $lowerDict.ToUpper()
If you have access to PowerShell Core, it can be reduced to:
$lowerDict = [string]::new('a'..'z')
$upperDict = $lowerDict.ToUpper()
As for what you are struggling on, how to do it with what you currently have (a foreach loop). You can capture all the output from the loop first:
$upper = foreach ($i in 65..90) { [char]$i }
Now, $upper is an array of chars, then to convert it to string, you can either use -join (guessed right) or [string]::new(...) as I did on my previous example:
$upperDict = -join $upper
# OR
$upperDict = [string]::new($upper)

Related

Find index of array where condition is true in PowerShell [duplicate]

I have trouble of getting index of the current element for multiple elements that are exactly the same object:
$b = "A","D","B","D","C","E","D","F"
$b | ? { $_ -contains "D" }
Alternative version:
$b = "A","D","B","D","C","E","D","F"
[Array]::FindAll($b, [Predicate[String]]{ $args[0] -contains "D" })
This will return:
D
D
D
But this code:
$b | % { $b.IndexOf("D") }
Alternative version:
[Array]::FindAll($b, [Predicate[String]]{ $args[0] -contains "D" }) | % { $b.IndexOf($_) }
Returns:
1
1
1
so it's pointing at the index of the first element. How to get indexes of the other elements?
You can do this:
$b = "A","D","B","D","C","E","D","F"
(0..($b.Count-1)) | where {$b[$_] -eq 'D'}
1
3
6
mjolinor's answer is conceptually elegant, but slow with large arrays, presumably due to having to build a parallel array of indices first (which is also memory-inefficient).
It is conceptually similar to the following LINQ-based solution (PSv3+), which is more memory-efficient and about twice as fast, but still slow:
$arr = 'A','D','B','D','C','E','D','F'
[Linq.Enumerable]::Where(
[Linq.Enumerable]::Range(0, $arr.Length),
[Func[int, bool]] { param($i) $arr[$i] -eq 'D' }
)
While any PowerShell looping solution is ultimately slow compared to a compiled language, the following alternative, while more verbose, is still much faster with large arrays:
PS C:\> & { param($arr, $val)
$i = 0
foreach ($el in $arr) { if ($el -eq $val) { $i } ++$i }
} ('A','D','B','D','C','E','D','F') 'D'
1
3
6
Note:
Perhaps surprisingly, this solution is even faster than Matt's solution, which calls [array]::IndexOf() in a loop instead of enumerating all elements.
Use of a script block (invoked with call operator & and arguments), while not strictly necessary, is used to prevent polluting the enclosing scope with helper variable $i.
The foreach statement is faster than the Foreach-Object cmdlet (whose built-in aliases are % and, confusingly, also foreach).
Simply (implicitly) outputting $i for each match makes PowerShell collect multiple results in an array.
If only one index is found, you'll get a scalar [int] instance instead; wrap the whole command in #(...) to ensure that you always get an array.
While $i by itself outputs the value of $i, ++$i by design does NOT (though you could use (++$i) to achieve that, if needed).
Unlike Array.IndexOf(), PowerShell's -eq operator is case-insensitive by default; for case-sensitivity, use -ceq instead.
It's easy to turn the above into a (simple) function (note that the parameters are purposely untyped, for flexibility):
function get-IndicesOf($Array, $Value) {
$i = 0
foreach ($el in $Array) {
if ($el -eq $Value) { $i }
++$i
}
}
# Sample call
PS C:\> get-IndicesOf ('A','D','B','D','C','E','D','F') 'D'
1
3
6
You would still need to loop with the static methods from [array] but if you are still curious something like this would work.
$b = "A","D","B","D","C","E","D","F"
$results = #()
$singleIndex = -1
Do{
$singleIndex = [array]::IndexOf($b,"D",$singleIndex + 1)
If($singleIndex -ge 0){$results += $singleIndex}
}While($singleIndex -ge 0)
$results
1
3
6
Loop until a match is not found. Assume the match at first by assigning the $singleIndex to -1 ( Which is what a non match would return). When a match is found add the index to a results array.

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 = #())

Handle 4 For loops at once?

Introduction to question
I'm trying to search for the following values: '02-08-1997' and '01-08-1997' in string $b1. To find that value, I used searching for the starting and ending index of the values around it.
How did I do it?
To do this I used 4 For loops, only to execute the exact same thing over and over again. I know this can be done much simpler; so far I haven't found a way to do so yet. I have to write multiple scripts like this, so I really need to find a simpler - more easy to read - way to do so.
These are 2 of the For loops I'm using:
$b1 = 'set-aduser -identity 3423-234-23-42-432 dorstm -replace #{ geboortedatum = 01-08-1997 } waarde_org = 02-08-1997'
For ($i = 0; $i -lt $b1InArray.Length; $i++) {
if ($b1InArray[$i].contains("=")) {
$e = $i
break
}
}
For ($j = ($e + 1); $j -lt $b1InArray.Length; $j++) {
if ($b1InArray[$j].contains("}")) {
$f = $j
break
}
}
Looks like you're trying to use more of a Java solution. In Powershell you can use 'ForEach'
$b1 = 'set-aduser -identity 3423-234-23-42-432 dorstm -replace #{ geboortedatum = 01-08-1997 } waarde_org = 02-08-1997'
foreach ($b in $b1) {
if ( $b -contains '=') {
$f = $j
}
}
Note: $b in the foreach loop condition can be anything you want it to be just like $i in Java.
If you want to get the index of the strings you are searching for: there's a method for that:
$b1.IndexOf('02-08-1997')
$b1.IndexOf('01-08-1997')
No need to write loops. For simple things like this .Net has a working method for you.
Many ways to do this. This could work for you:
(Select-String -InputObject $b1 -allmatches -Pattern '\d{2}-\d{2}-\d{4}').matches.value
it will output all dates from your $b1 string as an array of strings. Assuming all dates have the same format like your example.
Fiddle with regex at https://regex101.com/

Is this the best way to replace text in all of an object's properties in powershell?

I have a large CSV file in which some fields have a new line embedded. Excel 2016 produces errors when importing a CSV with rows which have fields with a new line embedded.
Based on this post, I wrote code to replace any new line in any field with a space. Below is a code block that duplicates the functionality and issue. Option 1 works. Option 2, which is commented out, casts my object to a string. I was hoping Option 2 might run faster.
Question: Is there a better way to do this to optimize for performance processing very large files?
$array = #([PSCustomObject]#{"ID"="1"; "Name"="Joe`nSmith"},
[PSCustomObject]#{"ID"="2"; "Name"="Jasmine Baker"})
$array = $array | ForEach-Object {
#Option 1: produces an Object, but is code optimized?
foreach ($n in $_.PSObject.Properties.Name) {
$_.PSObject.Properties[$n].Value = `
$_.PSObject.Properties[$n].Value -replace "`n"," "
}
#Option 2: produces a string, not an object
#$_ = $_ -replace "`n"," "
$_
}
Keep in mind that in my real-world use case, each row has > 15 fields and any combination of them may have one or more new lines embedded.
Use the fast TextFieldParser to read, process, and build the CSV from the file (PowerShell 3+):
[Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') >$null
$parser = New-Object Microsoft.VisualBasic.FileIO.TextFieldParser 'r:\1.csv'
$parser.SetDelimiters(',')
$header = $parser.ReadFields()
$CSV = while (!$parser.EndOfData) {
$i = 0
$row = [ordered]#{}
foreach ($field in $parser.ReadFields()) {
$row[$header[$i++]] = $field.replace("`n", ' ')
}
[PSCustomObject]$row
}
Or modify each field in-place in an already existing CSV array:
foreach ($row in $CSV) {
foreach ($field in $row.PSObject.Properties) {
$field.value = $field.value.replace("`n", ' ')
}
}
Notes:
foreach statement is much faster than piping to ForEach-Object (also aliased as foreach)
$stringVariable.replace() is faster then -replace operator

PowerShell Function within Array Comprehension

I am parsing all logins for a Windows system ($system). I'm trying to get a tuple/array of domain, user, session id.
$logged_on_users = Get-WmiObject Win32_LoggedOnUser -ComputerName $system
I'm then calling the type's function to pull out two variables (Antecedent and Depedent) within each value:
$all_user_sessions = #()
foreach ($x in $logged_on_users){
$t = #()
$t += $x.GetPropertyValue("Antecedent").tostring()
$t += $x.GetPropertyValue("Dependent").tostring()
$all_user_sessions += $t
}
What is the syntax for a one-line Array Comprehension (if it's even possible)? I have:
$all_user_sessions = ($logged_on_users | % { #($_.GetPropertyValue("Antecedent").tostring(), $_.GetPropertyValue("Dependent").tostring() ) })
When I add
foreach ($s in $all_user_sessions){
echo $s.GetType().FullName
}
The type is a String, not an Array.
This is going to sound a little weird, but you need to add a comma before the array in your ForEach-Object loop:
$all_user_sessions = ($logged_on_users | % { ,#($_.GetPropertyValue("Antecedent").tostring(), $_.GetPropertyValue("Dependent").tostring() ) })
As your code is written you're adding everything to a single array. Placing the comma like that will create an array (actually an object) of each index.