I am exploring options for presenting multidimensional array in some specified format. Desired output is a single line string with elements within each dimension separated by specified character. For example:
$foo = #(#("A","B","C"),#("1","2","3"))
$bar = #()
foreach ($i in $foo)
{
$bar += $i -Join ", "
}
$bar -join "; "
Produces desired output. When number of dimensions in the array grows, or is variable within the nested elements, this approach becomes cumbersome.
I am wondering if exists some Powershell magic to assist with this task. Ideally, something like:
$foo[([] -join ", ")] -join "; "
And perhaps a solution that will scale well for more complex array configurations.
Thoughts?
this way?
$foo = #(#("A","B","C"),#("1","2","3"))
($foo | % { $_ -join ',' }) -join '; '
Related
I have an array of strings make like this "name: number" and I need to order it by number every time that I add another row to the array but the sorting doesn't work.
The idea is that every time that the function is called I create a new row and I add it to the text.
Then, for each row in the text I add in front of the row the number and a '#'.
Now the array is like this:
15#foo:15
2#bar:2
4#foobar:4
Now I'd like to order it by number and then remove the part that I added to sort.
Function Add($name, $number) {
$newRow = "$($name):$number`r`n"
$Script:text = $Script:text + $newRow
ForEach($row in $text.Split("`r`n")) {
if ($row.length -ne 0) {
$rows = $rows + $row.Split(":")[1] + "#" + $row + "`r`n"
}
}
$rows | Sort-Object
ForEach($row in $rows.Split("`r`n")) {
$newText = $newText + $row.Split("#")[1] + "`r`n"
}
$textBox.Text = $newText
}
It all works except the sorting.
Does anyone knows how to fix it?
You can sort your input directly, by passing a script block ({ ... }) to Sort-Object that extracts the number from each input string ($_) and sorts based on it:
PS> 'foo:15', 'bar:2', 'foobar:4' | Sort-Object { [int] ($_ -split ':')[-1] }
bar:2
foobar:4
foo:15
In your case, since the lines are part of a single, multi-line string you need to split the string into individual lines first, then sort them, then rejoin the sorted lines:
(
#'
foo:15
bar:2
foobar:4
'# -split '\r?\n' | Sort-Object { [int] ($_ -split ':')[-1] }
) -join [Environment]::NewLine
The output looks the same as above, but in this case it isn't an array of sorted lines, but a single multi-line string containing the sorted lines.
Note: Sort-Object outputs a new array, it doesn't sort in place. Therefore, to save the sorted array back to the same variable, you must use something like:
$rows = $rows | Sort-Object ...
For starters, I'm on Fedora 30 using PSCore version 6.2.1. I've encountered this issue in GNOME Terminal and the vscode snap.
I'm on the first challenge of the PSKoans module and I'm stuck when trying to use a for loop. I am given an array of strings, each of which is a collection of strings separated by commas.
$StockData = #(
"Date,Open,High,Low,Close,Volume,Adj Close"
"2012-03-30,32.40,32.41,32.04,32.26,31749400,32.26"
"2012-03-29,32.06,32.19,31.81,32.12,37038500,32.12"
) # The array is much longer than that, but shortened for simplicity's sake
So, my idea is to build a hashtable out of each subsequent string line in the array by using the first string in the array as keys and each following line as a set of values. I'm using -split to split the values apart from within the strings. I want to use a for loop to iterate through the array and pull values, building a hastable in a file to be read later like so:
# Build the array of keys
[array]$keys = $StockData[0] -split ','
# Begin for loop, using $i as int
for ($i = 1, $StockData[$i], $i++) {
# Create a text file for each hastable
New-Item -Name "ht$i.txt" -ItemType File
# Split current string into values
$values = $StockData[$i] -split ','
# Set value int
$valuesInt = 0
foreach ($key in $keys) {
Add-Content -Path "./ht$i.txt" -Value "$key = $values[$valuesInt]"
$valuesInt++
}
}
As I run that, I get the following error:
Index operation failed; the array index evaluated to null.
At /home/user/PSKoans/Foundations/SolutionStockChallenge.ps1:28 char:6
+ for ($i = 1, $stockData[$i], $i++) {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArrayIndex
I've looked it up and I find all kinds of scenarios in which people get this error message. I didn't really find a solid explanation for the error message the might lead me to an answer.
Reading the error message, it doesn't make sense to me. the array index evaluated to null...but the array index in the first case is $StockData[1] which is a valid index and should return $true and continue with the loop. Am I missing something?
The syntax of your for loop is wrong. The for loop uses semi-colons as separators.
for ($i = 1, $StockData[$i], $i++) {
should be
for ($i = 1; $StockData[$i]; $i++) {
ConvertFrom-Json in PowerShell Core has the coolest switch - AsHashTable. Try this:
$StockData | convertfrom-csv | convertto-json | ConvertFrom-Json -AsHashtable
I have a powershell script and a txt database with different number of elements per line.
My txt file is list.txt:
"10345","doomsday","life","hope","run","stone"
"10346","ride","latest","metal"
My powershell script search.ps1:
#Get file path
$path = Split-Path $script:MyInvocation.MyCommand.Path
$search = #()
Get-Content -LiteralPath "$path\list.txt" | ForEach-Object {
$search += $_
}
So, how to convert each line as a element of array? As this:
$search = #(("10345","doomsday","life","hope","run","stone"),("10346","ride","latest","metal"))
To operate as:
echo $search[0][0]
Here's a concise PSv4+ solution:
$search = (Get-Content -LiteralPath $path\list.txt).ForEach({ , ($_ -split ',') })
The .ForEach() method operates on each line read from the input file by Get-Content.
$_ -split ',' splits each line into an array of strings by separator ,
, (...) wraps this array in an aux. single-item array to ensure that the array is effectively output as a whole, resulting in an array of arrays as the overall output.
Note: Strictly speaking, the .ForEach() method outputs a [System.Collections.ObjectModel.Collection[psobject]] collection rather than a regular PowerShell array ([object[]]), but for all practical purposes the two types act the same.
Note: The .ForEach() method was chosen as a faster alternative to a pipeline with the ForEach-Object (%) cmdlet.
Note that the .ForEach() method requires storing the input collection in memory as a whole first.
A faster and more memory-efficient, though perhaps slightly obscure alternative is to use a switch statement with the -file option:
$search = switch -file $path\list.txt { default { , ($_ -split ',') } }
switch -file processes each line of the specified file.
Since each line should be processed, only a default branch is used, in which the desired splitting is performed.
Use -split. A code snippet you can debug in ISE or VSCode below.
$x1 = #'
"10345","doomsday","life","hope","run","stone"
"10346","ride","latest","metal"
'#
$data = $x1 -split "`r`n"
$data.Count
$data[0] -split ","
$arr = #()
foreach ($row in $data)
{
$arr += ,($row -split ",")
}
"arr"
$arr
"0,3"
$arr[0][3]
"1,3"
$arr[1][3]
So you can split each line in your file returned from Get-Content and add it to your new array which lets you reference how you wanted...
There are other ways you can use your data depending on your needs.
Assuming you do not want each item quoted, you might consider to not using the -Split operator but just evaluating each line with the Invoke-Expression cmdlet or using a more secure [ScriptBlock] for this:
$Search = Get-Content ".\list.txt" | ForEach-Object {,#(&([ScriptBlock]::Create($_)))}
I am running below script and retrieve information from the template and assign permission. Here I would like to get the User as array input my below script is not processing the user as array.
$userObj = [PSCustomObject]((Get-Content -Raw C:\txt\sample.txt) -replace ':','=' | ConvertFrom-StringData)
[array]$userObj.User
for ($i[0]; $userObj.user; $i++) {
Add-MailboxPermission -Identity $userObj.Identity -User $userObj.User -AccessRights FullAccess -InheritanceType All -confirm:$false
}
Here is my text input which is converted as custom object
$userObj.User is a string with comma-separated names. Casting it to an array just gives you an array with one string with comma-separated names, not an array of the names.
[array]$userObj.User ⇒ [ 'auto,auto1' ]
[array]$userObj.User ⇏ [ 'auto', 'auto1' ]
To get an array of the names from the comma-separated string you need to split it:
$userObj.User -split ','
Also, your for loop is broken. Those loops have the following structure:
for (loop variable initialization; condition; loop variable incrementation)
e.g.
for ($i=0; $i -lt 10; $i++)
But you probably don't need a for loop here anyway. If you want to run a command for each element of the array resulting from your split operation use a ForEach-Object instead:
$userObj.User -split ',' | ForEach-Object {
Add-MailboxPermission -Identity $userObj.Identity -User $_ ...
}
I have the follow array with the elements
$list = "A","B","C","1","2","3"
using foreach I can view all items in the array.
foreach ( $item in $list ) { $item }
I would like to print all elements in the array but the last one. As I need to add ; at the end.
How can I go about doing this?
Is this what you're looking for?
$List = "A","B","C","1","2","3";
($List[0..($List.Length-2)] -join '') + ';';
Result
ABC12;
This can also be done in as one-liner:
-join $List -replace [Regex]'.$',';'
First -join sticks all the elements in the array together. Then -replace & regex replace the last element with ;.
Regex ('.$')
. = Matches any character except line breaks.
$ = Matches the end of the string.