I am trying to change the format of an arraylist after I have used
group-object
to count all the entries in the list.
this is a sample
$list = [System.Collections.ArrayList]#()
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$list | Group-Object | select name,count
This is the sample output
Name Count
---- -----
letter 1
solo 1
nap 3
sharp 1
ignite 1
tap 4
evoke 1
What I would like is
letter solo nap sharp ignite tap evoke
-------- ----- ---- ---- ----- ------ ----
1 1 3 4 1 4 1
Then when exporting to excel it would format like this
Everything I have tried doesn't seem to pay off, or even get close to what I am trying to do and I think I am missing something obvious or have hit my PowerShell skill limitations. Could someone please help. Thank you
You could create a PSObject, add the properties to it with Add-Member, then format the output to a table with Format-Table:
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$groups = $list | Group-Object | Select-Object Name, Count
$psObject = New-Object -TypeName psobject
foreach ($group in $groups) {
$psObject | Add-Member -NotePropertyName $group.Name -NotePropertyValue $group.Count
}
$psObject | Format-Table
Output:
evoke ignite letter nap sharp solo tap
----- ------ ------ --- ----- ---- ---
1 1 1 3 1 1 4
Skip Group-Object altogether - instead, use a dictionary to keep track of the count, then cast the whole dictionary to a custom object:
$properties = [ordered]#{}
$list |ForEach-Object {
$properties[$_]++
}
$counts = [pscustomobject]$properties
$counts will now hold an object like what you describe, formatting as a table gives you:
PS C:\> $counts |Format-Table
letter solo nap sharp ignite tap evoke
------ ---- --- ----- ------ --- -----
1 1 3 1 1 4 1
You may try something like:
$list = [System.Collections.ArrayList]#()
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$group = $list | Group-Object | select name,count
$a = [PSCustomObject]#{}
foreach ($item in $group) {
$a | Add-Member -NotePropertyName $item.name -NotePropertyValue $item.count
}
$a | ft
One solution would be to put it into an PsObject and then export that object into a CSV:
$list = [System.Collections.ArrayList]#()
$list = "letter","solo","nap","nap","nap","sharp","ignite","tap","tap","tap","tap","evoke"
$hash = $list | Group-Object | select name,count
$object = New-Object psobject
foreach( $item in $hash ) {
$column_name = $item.Name
$row_value = $item.Count
$object | Add-Member -MemberType NoteProperty -Name $column_name -Value $row_value
}
$object | Export-csv 'Path to your CSV' -NoTypeInformation
Related
What is the simplest way to write this in PowerShell:
SELECT col1, SUM(col3) AS SumOfValues
FROM dbo.Table
GROUP BY col1
How can I combine Group-Object and Measure-Object?
I have found this answer link, but there must be a simpler way.
Like this? With a calculated property at the end.
'id,amount
1,4
1,5
2,4
2,6
3,3
3,5' | convertfrom-csv | group id | select name,
#{n='Sum';e={$_.group.amount | measure -sum | % sum}}
Name Sum
---- ---
1 9
2 10
3 8
You need to use the property GROUP. The script outputs "$group | Format-Table" which shows what the real issue is.
This is what each row of the Group Looks like :
Count Name Group
----- ---- -----
3 a {#{col1=a; col2=x; col3=1}, #{col1=a; col2=x; col3=2}, #{col1=a; col2=x; col3=3}}
See code below :
$input = #"
col1,col2,col3
a,x,1
a,x,2
a,x,3
b,x,4
b,x,5
b,x,6
c,x,7
c,x,8
c,x,9
"#
$table = $input | ConvertFrom-Csv
$table | Format-Table
$groups = $table | Group-Object {$_.col1}
$outputTable = [System.Collections.ArrayList]::new()
foreach($group in $groups)
{
$group | Format-Table
$newRow = New-Object -TypeName psobject
$newRow | Add-Member -NotePropertyName col1 -NotePropertyValue $group.Name
$sum = 0
$group.Group | foreach{$sum += $_.col3}
Write-Host "sum = " $sum
$newRow | Add-Member -NotePropertyName SumOfValues -NotePropertyValue $sum
$outputTable.Add($newRow) | Out-Null
}
$outputTable | Format-Table
If I have multiple variables and I want to pull certain properties from all of them in a single view (for instance their Count), how would one do this?
For example, if I want something like the following
# Table Format
Name Count
Variable1 $Variable1.Count
Variable2 $Variable2.Count
Variable3 $Variable3.Count
# List Format
Name : Variable1
Count : $Variable1.Count
Name : Variable2
Count : $Variable2.Count
Name : Variable3
Count : $Variable3.Count
# Variables are ArrayLists, hence the Count property
I thought this would be fairly trivial using the standard select-object, format-list or format-table cmdlets and use of calculated properties, but I just cannot get it to work as expected.
This was my first thought:
Format-List #{N='Variable1';E={$Variable1.Count}}, #{N='Variable2';E={$Variable2.Count}}
I guess those cmdlets cannot be called without piping something to them first, so then I tried the following, and it did what I wanted, however it seems to keep looping endlessly, outputting the results over and over and over.
#($Variable1, $Variable2) | Format-List #{N='Variable1';E={$Variable1.Count}}, #{N='Variable2';E={$Variable2.Count}}
Is there something stupid/simple I'm overlooking here?
In a dynamic sense of approach, Get-Variable could be a good option if I'm understanding you correctly. This passes over the name, and the value which you'd be able to use a calculated property to convert over to the values sum/count:
$variable1 = 1..10
$variable2 = 5..15
$variable3 = 10..20
Get-Variable -Name variable1,variable2,variable3 |
Format-List -Property Name, #{
Name = 'Count'
Expression = { $_.Value.Count }
}
With Format-List it will output:
Name : variable1
Count : 10
Name : variable2
Count : 11
Name : variable3
Count : 11
...and with Format-Table:
Name Count
---- -----
variable1 10
variable2 11
variable3 11
Here is a generic method. I create a table with random names like Variable25. the table contains 10000 rows with the name having number between 1 and 100. Then Group by name to get counts
$table = [System.Collections.ArrayList]::new()
for($i = 0; $i -le 10000; $i++)
{
$newRow = New-Object -TypeName psobject
$number = Get-Random -Minimum 1 -Maximum 100
$newRow | Add-Member -NotePropertyName Name -NotePropertyValue ("Variable" + $number)
$table.Add($newRow) | Out-Null
}
$groups = $table | Sort-Object -Property Name | Group-Object {$_.Name}
$tableCount = [System.Collections.ArrayList]::new()
foreach($group in $groups)
{
$newRow = New-Object -TypeName psobject
$newRow | Add-Member -NotePropertyName Name -NotePropertyValue $group.Name
$newRow | Add-Member -NotePropertyName Count -NotePropertyValue $group.Count
$tableCount.Add($newRow) | Out-Null
}
$tableCount | Format-Table
Why isn't where-object working in this case?
$controlFlowArgs = #{ waitForEnter = $false }
$controlFlowArgs | Format-Table
$controlFlowArgs | Format-List
$result = $controlFlowArgs | Where-Object -FilterScript { $_.Name -eq "waitForEnter" }
$result
Output
Name Value # Format-Table
---- -----
waitForEnter False
Name : waitForEnter # Format-List
Value : False
# Missing result would be here
$controlFlowArgs is a HashTable. You should probably think it differently.
$result = $controlFlowArgs | Where-Object { $_["waitForEnter"] }
would store $false in $result.
Else you can use the Hashtable directly:
if ($controlFlowArgs["waitForEnter"]) {
...
}
Where-object is working fine. In this example, only the 'joe' hashtable appears. Confusingly the format takes up two lines.
#{name='joe';address='here'},#{name='john';address='there'} | ? name -eq joe
Name Value
---- -----
name joe
address here
It's still considered one thing:
#{name='joe';address='here'},#{name='john';address='there'} | ? name -eq joe |
measure-object | % count
1
If you want the value itself, use foreach-object (or select-object -expandproperty):
#{name='joe';address='here'},#{name='john';address='there'} | ? name -eq joe |
% name
joe
Usually powershell works with pscustomobjects:
[pscustomobject]#{name='joe';address='here'},
[pscustomobject]#{name='john';address='there'} | ? name -eq joe
name address
---- -------
joe here
Say I have an Excel file:
I need to create n variables called $col1 up to $coln and read in their respective values.
What is the best approch? I have been trying with a hashtable but I need to be able loop through the columns. This is my code. I have not wraped it around a loop yet. I can create the column names manually but I need to be able to index the columns in the Excel file.
$ExcelRowToRead = 2;
$ListOfColumns = #{"Job_name" = 1 ; "Run_time_start" = 2}
$excel = New-Object -ComObject excel.application;
$workbook = $excel.Workbooks.Open("pathtofile.xlsx");
$workbook.sheets.item(1).activate()
$WorkbookTotal=$workbook.Worksheets.item(1)
$ListOfColumns["Job_name"] = $WorkbookTotal.Cells.Item($ExcelRowToRead, 1) # This needs to be an index
$ListOfColumns["Job_name"].Text
Taking Lee_Dailey's advice here and saving first to a CSV:
$excel = New-Object -ComObject excel.application
$workbook = $excel.Workbooks.Open("pathtofile.xlsx")
$sheet = $workbook.Sheets |
Where-Object Name -eq "SheetName" |
Select-Object -First 1
$tempCsv = New-TemporaryFile
$sheet.SaveAs($tempCsv, 6)
$sheetData = Get-Content $tempCsv | ConvertFrom-Csv
At this point you have an object where you can extract its properties via $sheetData.col1, $sheetData.col2, etc. but if you really need them as separate variables, perhaps:
$sheetData |
Get-Member |
Where-Object MemberType -Eq NoteProperty |
ForEach-Object {
New-Variable -Name $_.Name -Value $sheetData.$($_.Name)
}
In case you don't have / want Excel:
via GitHub - dfinke/ImportExcel
> # Install-Package ImportExcel
> $sheetData = Import-Excel '.\Excel 001.xlsx'
> $sheetData[0]
col1 col2 col3
---- ---- ----
value1 value2 value3
> $sheetData[0] |
Get-Member |
Where-Object { $_.MemberType -eq 'NoteProperty' } |
Select-Object -Property Name
Name
----
col1
col2
col3
> $sheetData |
Get-Member |
Where-Object MemberType -Eq NoteProperty |
ForEach-Object {
New-Variable -Name $_.Name -Value $sheetData.$($_.Name)
}
> Get-Variable col*
Name Value
---- -----
col1 value1
col2 value2
col3 value3
if I have two arrays and region column has no same values
like
$data1=
Region Type
------ -----------
EuropeWest Operational
EuropeWest Operational
EuropeWest Operational
EuropeNorth Operational
USCentral Operational
USCentral Operational
AsiaEast Operational
AsiaEast Operational
AsiaEast Operational
$data2=
Region Type
------ -----------
EuropeWest MigrateSource
EuropeWest MigrateSource
EuropeNorth MigrateSource
USCentral MigrateSource
USEast MigrateSource
output should be as:
Region Operational MigrateSource
------ ----------- -----------
EuropeWest 4 2
EuropeNorth 1 1
USCentral 2 0
AsiaEast 3 1
Useast 0 1
Any help much appreciated?
I was able to group it but did'nt get any clue how to use foreach loop here:
$data1 | group -Property region | select name,#{n='Operationaclcount';e={$_.count}}
$data2 | group -Property region | select name,#{n='Migratesourcecountt';e={$_.count}}
Since you have two objects with a property name, if we combine these together we have a full list of names. With a ForEach loop, we'll loop over these names and use a Where-Object to filter each of the two objects you created for the count. We'll then create a new object with [pscustomobject]. Finally a quick test if a name is missing from a group that means the count was zero.
$OpCount = $data1 |
Group-Object -Property region |
Select-Object name,#{n='Operationalcount';e={$_.count}}
$MigCount = $data2 |
Group-Object -Property region |
Select-Object name,#{n='Migratesourcecount';e={$_.count}}
$CombinedNames = $OpCount.name + $MigCount.name
Foreach ($Name in $CombinedNames) {
$entry = [pscustomobject]#{
Operational = $OpCount |
Where-Object {$_.name -eq $Name} |
Select-Object -Expand Count
MigrateSource = $MigCount |
Where-Object {$_.name -eq $Name} |
Select-Object -Expand Count
}
if ($entry.Operational -eq $null) { $entry.Operational = 0 }
if ($entry.MigrateSource -eq $null) { $entry.MigrateSource = 0 }
$entry
}
If you want to combine two arrays just use a plus. Something like this:
$data1 + $data2 | Group-Object region | % {
New-Object psobject -property #{
Name = $_.Name
Operational = $_.Count
MigrateSource = #($_.Group | Select Type -Unique).Count
}
}