I can't seem to get this function quite right. I want to pass it an object and if the object is empty, return 1, else count items in the object and increment by 1.
Assuming the following function "New-Test":
function New-Test
{
[cmdletbinding()]
Param
(
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[object[]]$Object
#[object]$object
)
Begin
{
$oData=#()
}
Process
{
"Total objects: $($object.count)"
if($Object.count -gt 0)
{
$oData += [pscustomobject]#{
Name = $_.Name
Value = $_.Value
}
}
Else
{
Write-Verbose "No existing object to increment. Assuming first entry."
$oData = [pscustomobject]#{Value = 0}
}
}
End
{
$LatestName = ($oData | Sort-Object -Descending -Property Value | Select -First 1).value
[int]$intNum = [convert]::ToInt32($LatestName, 10)
$NextNumber = "{0:00}" -f ($intNum+1)
$NextNumber
}
}
And the following test hashtable:
#Create test hashtable:
$a = 00..08
$obj = #()
$a | foreach-object{
$obj +=[pscustomobject]#{
Name = "TestSting" + "{0:00}" -f $_
Value = "{0:00}" -f $_
}
}
As per the function above, if I pass it $Obj, I get:
$obj | New-Test -Verbose
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
Total objects: 1
09
Which is as expected. However, if I pass it $Obj2:
#Create empty hash
$obj2 = $null
$obj2 = #{}
$obj2 | New-Test -Verbose
I get:
Total objects: 1
Exception calling "ToInt32" with "2" argument(s): "Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: startIndex"
At line:33 char:9
+ [int]$intNum = [convert]::ToInt32($LatestName, 10)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ArgumentOutOfRangeException
01
I don't understand why $object.count is 1, when there's nothing in the hashtable.
If I change the parameter, $object's type from [object[]] to [object], the empty hashtable test results in:
$obj2 | New-Test -Verbose
Total objects: 0
VERBOSE: No existing object to increment. Assuming first entry.
01
Which is what I'd expect, however, if I run the first test, it results in:
$obj | New-Test -Verbose
Total objects:
VERBOSE: No existing object to increment. Assuming first entry.
Total objects:
VERBOSE: No existing object to increment. Assuming first entry.
This time $objects has nothing in it.
I'm sure it's simple, but I can't fathom this one out. Any help is appreciated.
P.S. PowerShell 5.1
$obj2 is a hashtable, not an array. Hashtables are not enumerated by default, so the hashtable itself is the one object. If you want to loop through an hashtable using the pipeline you need to use $obj2.GetEnumerator().
#{"hello"="world";"foo"="bar"} | Measure-Object | Select-Object Count
Count
-----
1
#{"hello"="world";"foo"="bar"}.GetEnumerator() | Measure-Object | Select-Object Count
Count
-----
2
Related
I have the following JSON records stored in a container
{"memberId":"123","city":"New York"}
{"memberId":"234","city":"Chicago"}
{"memberId":"345","city":"San Francisco"}
{"memberId":"123","city":"New York"}
{"memberId":"345","city":"San Francisco"}
I am looking to check if there is any duplication of the memberId - ideally return a true/false and then also return the duplicated values.
Desired Output:
true
123
345
Here's an efficient approach using inputs. It requires invoking jq with the -n command-line option. The idea is to create a dictionary that keeps count of each memberId string value.
The dictionary can be created as follows:
reduce (inputs|.memberId|tostring) as $id ({}; .[$id] += 1)
Thus, to produce a true/false indicator, followed by the duplicates if any, you could write:
reduce (inputs|.memberId|tostring) as $id ({}; .[$id] += 1)
| to_entries
| map(select(.value > 1))
| (length > 0), .[].key
(If all the .memberId values are known to be strings, then of course the call to tostring can be dropped. Conversely, if .memberId is both string and integer-valued, then the above program won't differentiate between occurrences of 1 and "1", for example.)
bow
The aforementioned dictionary is sometimes called a "bag of words" (https://en.wikipedia.org/wiki/Bag-of-words_model). This leads to the generic function:
def bow(stream):
reduce stream as $word ({}; .[($word|tostring)] += 1);
The solution can now be written more concisely:
bow(inputs.memberId)
| to_entries
| map(select(.value > 1))
| (length > 0), .[].key
For just the values which have duplicates, one could write the more efficient query:
bow(inputs.memberId)
| keys_unsorted[] as $k
| select(.[$k] > 1)
| $k
I am working in PowerShell with a hashtable like so:
When I convert it to JSON notice that the "oranges" key does not contain brackets:
I have tried to accommodate for this when I create my hashtable by doing something like this:
foreach ($Group in ($input | Group fruit)) {
if ($Group.Count -eq 1) {
$hashtable[$Group.Name] = "{" + ($Group.Group | Select -Expand number) + "}"
} else {
$hashtable[$Group.Name] = ($Group.Group | Select -Expand number)
}
}
Which looks fine when I output it as a hashtable but then when I convert to JSON I get this:
I am trying to get that single item also surrounded in []. I found a few things here and one of them took me to this:
https://superuser.com/questions/414650/why-does-powershell-silently-convert-a-string-array-with-one-item-to-a-string
But I don't know how to target just that one key when it only contains a single item.
You want to ensure that all hashtable values are arrays (that is what the curly brackets in the hashtable output and the square brackets in the JSON mean).
Change this code:
if ($Group.Count -eq 1) {
$hashtable[$Group.Name] = "{" + ($Group.Group | Select -Expand number) + "}"
} else {
$hashtable[$Group.Name] = ($Group.Group | Select -Expand number)
}
into this:
$hashtable[$Group.Name] = #($Group.Group | Select -Expand number)
and the problem will disappear.
There is a JSON URL which produces dynamic content, and there is one particular part of the JSON URL which I am trying to separate two values which have no title or name associated to them other than the parent title (accountUsage) and give them each a unique title, which I can then call upon in PowerShell.
Any ideas how to achieve this?
I need to convert this
accountUsage : #{10.10.2018=5; 09.10.2018=0; 08.10.2018=0; 07.10.2018=0; 06.10.2018=0; 05.10.2018=8; 04.10.2018=1; 03.10.2018=0;
02.10.2018=0; 01.10.2018=0}
Into this:
date
----
10.10.2018
value
----
5
date
----
09.10.2018
value
----
0
$json = '{"accountUsage":{"06.10.2018":0,"09.10.2018":0,"04.10.2018":1,"08.10.2018":0,"02.10.2018":0,"07.10.2018":0,"03.10.2018":0,"05.10.2018":8,"10.10.2018":5,"01.10.2018":0}}'
$data = $json | ConvertFrom-Json
$data.accountUsage | Get-Member -MemberType NoteProperty | ForEach-Object {
$key = $_.Name
[PSCustomObject]#{
date = $key
value = $data.accountUsage.$key
}
}
gives me a list of date/value pairs:
date value
---- -----
06.10.2018 0
09.10.2018 0
04.10.2018 1
08.10.2018 0
02.10.2018 0
07.10.2018 0
03.10.2018 0
05.10.2018 8
10.10.2018 5
01.10.2018 0
See this earlier answer of mine for some more insight into this.
Powershell Script to Delete Blank Columns from CSV
I have a spread sheet which I'm importing into a MySQL database, the import fails because of blank columns in the spread sheet.
Is there a powershell script I can run / create that will check any given CSV file and remove blank columns?
Col1,Col2,Col3,Col4,,,,
Val1,Val2,Val3,Val4
How about something like this:
$x = Import-Csv YourFile.csv
$f = $x[0] | Get-Member -MemberType NoteProperty | Select name
$f | Add-Member -Name count -Type NoteProperty -Value 0
$f | %{
$n = $_.Name
$_.Count = #($x | Select $n -ExpandProperty $n | ? {$_ -ne ''}).count
}
$f = #($f | ? {$_.count -gt 0} | Select Name -expandproperty Name)
$x | Select $f | Export-Csv NewFile.csv -NoTypeInformation
It uses Get-Member to get the column names, cycles though each one to check how many are not blank and then uses the results in a select.
When I run Dave Sexton's code, I get:
Select-Object : Cannot convert System.Management.Automation.PSObject to one of the following
types {System.String, System.Management.Automation.ScriptBlock}.
At line:15 char:12
+ $x | Select <<<< $f | Export-Csv ColsRem.test.$time.csv -NoTypeInformation
+ CategoryInfo : InvalidArgument: (:) [Select-Object], NotSupportedException
+ FullyQualifiedErrorId :
DictionaryKeyUnknownType,Microsoft.PowerShell.Commands.SelectObjectCommand
I corrected this issue by adding one more line, to force each array element to be a string.
$x = Import-Csv YourFile.csv
$f = $x[0] | Get-Member -MemberType NoteProperty | Select name
$f | Add-Member -Name count -Type NoteProperty -Value 0
$f | %{
$n = $_.Name
$_.Count = #($x | Select $n -ExpandProperty $n | ? {$_ -ne ''}).count
}
$f = #($f | ? {$_.count -gt 0} | Select Name -expandproperty Name)
# I could get the select to work with strings separated by commas, but the array would
# always produce the error until I added the following line, explicitly changing the
#values to strings.
$f = $f | Foreach-Object { "$_" }
$x | Select $f | Export-Csv NewFile.csv -NoTypeInformation
My import CSV contains a few hundred columns and about half likely won't be populated, so getting rid of the extra columns was necessary. Now I just need to figure out how to counteract the unintended re-ordering of the columns into alphabetical order by name, without changing the names.
In my script I've collected all the data I want to report in different variables. Now I'm trying to generate an HTML-table so I can send this by mail.
What I would like to achieve is HTML-code that generates this:
OU | Logon scripts incorrect | Name incorrect | No description
\\Domain\NLD Users | 2 | 6 | 2
\\Domain\FRA users | 5 | 7 | 0
\\Domain\BEL users | 6 | 1 | 1
TOTAL USERS: 2048 | 13 | 14 | 3
I'm a bit confused on what would be the best approach for this (array, psobject, hashtable, ..). Because I'm not going to work with a foreach loop, the data would be static.
What I tried so far but isn't quite giving the desired result:
$Table = #( ('OU', 'Logon scripts incorrect', 'Name incorrect', 'No description'),
('\\Domain\NLD Users','2','6','2' ),
('\\Domain\FRA Users','5','7','0' ),
('\\Domain\BEL Users','6','1','1' ),
('TOTAL USERS: 2048','13','14','3' )
)
$Table | ConvertTo-Html -As Table -Fragment
It feels like I'm over-complicating things.
convertto-html is waiting for a psobject as its input.
What you can do is pass your data as csv then use convertfrom-csv to tansform it to psobject the run convertto-html :
$Table = #"
'\\Domain\NLD Users','2','6','2'
'\\Domain\FRA Users','5','7','0'
"#
$Table | ConvertFrom-Csv -Header 'OU', 'Logon scripts incorrect', 'Name incorrect', 'No description' | ConvertTo-Html -Fragment -As Table
#( ('OU', 'Logon scripts incorrect', 'Name incorrect', 'No description'),
('\\Domain\NLD Users','2','6','2' ),
('\\Domain\FRA Users','5','7','0' ),
('\\Domain\BEL Users','6','1','1' ),
('TOTAL USERS: 2048','13','14','3' )
) | Select-Object -Skip 1 | ForEach-Object{
[PSCustomObject]#{
OU = $_[0]
"Logon scripts incorrect" = $_[1]
"Name incorrect" = $_[2]
"No Description" = $_[3]
}
} | ConvertTo-Html -As Table -Fragment
From what i have read ConvertTo-Html takes .Net Objects and not arrays to convert to html. I converted your static table to a custom object which then was exported. This might look more to your liking.
You're creating an array of arrays with your statement, which looks neat, but isn't something ConvertTo-Html can handle. Create new objects instead (this is for v3):
$x = #(
([pscustomobject] #{
"OU" = "\\Domain\NLD Users";
"Logon scripts incorrect" = 2;
"Name incorrect" = 6;
"No description" = 2;
}),
([pscustomobject] #{
"OU" = "\\Domain\FRA Users";
"Logon scripts incorrect" = 5;
"Name incorrect" = 7;
"No description" = 0;
})
# ...and so on, or more realistically, constructed using Foreach-Object
)
$x | convertto-html -fragment
If you're confined to PowerShell v2, you can use new-object -type psobject -prop instead, but then the properties are not ordered so you will need an explicit Select.