I'm adding items to an array called $MissingIps using the following command
$MissingIps = #("")
[System.Collections.ArrayList]$ExistingIps = $MissingIps
if ($lbips -notcontains $awsip){
$MissingIps.Add("$awsip")
}
On execution, PowerShell automatically echos the index position, and I'm struggling to hide that.
Any ideas?
As an alternative to the suggested methods of suppressing the unwanted output: don't use an ArrayList collection in the first place. It's the Add() method of that class that generates the output. If you use a regular array you can append without output being generated:
$MissingIps = #()
if ($lbips -notcontains $awsip){
$MissingIps += $awsip
}
This will do the work:
$MissingIps = #("")
[System.Collections.ArrayList]$ExistingIps = $MissingIps
if ($lbips -notcontains $awsip){
$MissingIps.Add("$awsip") | out-null
}
Related
This question already has an answer here:
Powershell: Piping output of pracl command to array
(1 answer)
Closed 1 year ago.
I am trying to add elements to array for filtering. after it goes through the loop the first time
I receive "Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named 'op_Addition'."
I have tried several methods to try and figure this out.
$JsonDB = Get-Content 'Q:\Technology\1AA\HardwareCollection.json' | Out-String | ConvertFrom-Json
foreach($client in $JsonDB)
{
if($client.HRSeparation -eq "No")
{
$ClientNotHRSeparated += $client
}
else
{
$ClientHRSeparated += $client
}
}
$JsonDB
Any help would be greatly appreciated, Thanks!!
ConvertFrom-Json parses a JSON string into PSObject(s).
Since you did not define $ClientNotHRSeparated and $ClientHRSeparated anywhere, but immediately start adding ($client) objects to it, in the first iteration your variable $ClientNotHRSeparated will become that client object.
The next time you do +=, you're trying to add an object to another object which does not work.
Define the variables on top of the script, preferably as List object that has a .Add() method.
$ClientNotHRSeparated = [System.Collections.Generic.List[object]]::new()
$ClientHRSeparated = [System.Collections.Generic.List[object]]::new()
Then in your loop use that as
$ClientNotHRSeparated.Add($client)
# same for $ClientHRSeparated
P.S. Using a List is much faster/better that adding to a simple array (#()), because when you add items to an array (which has a fixed length) with +=, the entire array needs to be rebuilt in memory, consuming memory and processing time
Although this works, you don't need a loop at all. Just do:
$ClientNotHRSeparated = $JsonDB | Where-Object { $_.HRSeparation -eq "No" }
$ClientHRSeparated = $JsonDB | Where-Object { $_.HRSeparation -ne "No" }
The first line can be rewritten as $JsonDB = Get-Content -Path 'Q:\Technology\1AA\HardwareCollection.json' -Raw | ConvertFrom-Json.
Switch -Raw makes the cmdlet read the content of the file as one single multilined string
The behavior of += is entirely dependent on the left-hand side operand. On the first assignment, the value of $ClientNotHRSeparated is $null, so the resulting operation is:
$ClientNotHRSeparated = $null + $someCustomPSObject
Which PowerShell evaluates as just:
$ClientNotHRSeparated = $someObject
On the second assigment, $ClientNotHRSeparated is no longer $null, and PowerShell instead of tries to identify an overload for + that works on two operands of type [PSObject], which is where it fails.
If you want += to perform array addition, define the two array variables ahead of time with an assignment of a resizable array (use the #() array subexpression operator):
$ClientNotHRSeparated = #()
$ClientHRSeparated = #()
$JsonDB = Get-Content 'Q:\Technology\1AA\HardwareCollection.json' | Out-String | ConvertFrom-Json
foreach ($client in $JsonDB) {
if ($client.HRSeparation -eq "No") {
$ClientNotHRSeparated += $client
}
else {
$ClientHRSeparated += $client
}
}
$JsonDB
Now += is unambiguous both the first time and subsequently - the left-hand side operand is an array in either case.
As an alternative to looping through the whole collection manually, consider using the .Where() extension method in Split mode:
$JsonDB = Get-Content 'Q:\Technology\1AA\HardwareCollection.json' | Out-String | ConvertFrom-Json
$ClientNotHRSeparated, $ClientHRSeparated = #($JsonDB).Where({$_.HRSeparation -eq 'No'}, 'Split')
Much faster and more concise :-)
I have a function that replaces PackageID in a SCCM task sequence, I would like to capture all those package IDs into a variable, so I would be able to create a report based on that.
The problem is that I already have a foreach loop doing the work, and I can't figure out how to not overwrite the values.
$Driver.PackageID comes from a foreach loop based on $Drivers, which contains
If I run the code I get this as I have Write-Output defined:
Updated code:
function Set-Drivers{
foreach ($Driver in $Drivers) {
Write-Output "Driver Name: $($Driver.Name)"
Write-Output "DriverPackageID: $($Driver.PackageID)"
}
}
$array = #()
$array = Set-Drivers
$hash = [ordered]#{
'DriverName' = $Driver.Name
'DriverID' = $Driver.PackageID
}
$array += New-Object -Typename PSObject -Property $hash
Can someone explain, why I only get the first result in my $array? I can see the values are being overwritten if I run it in debug mode.
Your code is not iterating over the results, but instead only using one of them. This what you intended.
$array = $drivers | foreach {
[ordered]#{
DriverName = $_.Name
DriverID = $_.PackageID
}
}
Your function doesn't return anything. It only writes lines to the console. Then after the function is finished, you create a single object and add that to your array.
Try something like
function Set-Drivers{
$result = foreach ($Driver in $Drivers) {
[PsCustomObject]#{
'DriverName' = $Driver.Name
'DriverID' = $Driver.PackageID
}
}
# output the result
# the comma wraps the result in a single element array, even if it has only one element.
# PowerShell 'flattens' that upon return from the function, leaving the actual resulting array.
,$result
}
$array = Set-Drivers
# show what you've got
$array
I've been struggling with this for a couple of days, and I'm not sure how to conquer it. I need to do the following:
Import a csv of users with the following values:
ID, Name, Region
Create an array based on the Region values that I can then use to populate with ID's and Names with that region, ie.
Array_SEA
AA_SCOM, Adam Andrews, SEA
Array_OAK
BB_SCOM, Bob Barker, OAK
Here's the code I've got right now:
$list2 = ipcsv .\TSE_Contact_List.csv | sort-object BU
$arraylist =#()
foreach ($vitem in $list2)
{
$arraylist += New-Object PsObject -Property #{'Array' = "Array_" + $vitem.bu}
}
foreach ($varray in $arraylist)
{
$arr = new-variable -Name $varray
$arr.value += $varray.array
$arr
}
This produces the following error for records with a duplicate regions:
New-Variable: A variable with name '#{Array=Array_SCA}' already exists.
I'm also getting the following when it tries to add values:
Property 'value' cannot be found on this object; make sure it exists and is settable.
I get that I'm not actually creating arrays in the second section, but I'm not sure how to pass the output of the variable to an array name without turning the variable declaration into the array name, if that makes sense.
I've tried the following with hash tables, and it gets closer:
$list2 = ipcsv .\TSE_Contact_List.csv | sort-object BU
$arraylist =#{}
foreach ($vitem in $list2){$arraylist[$vitem.bu] = #()}
foreach ($record in $list2)
{
$arraylist[$vitem.bu] += ($record.SCOMID,$record.Name,$record.BU)
Write-host "Array: "
$arraylist[$vitem.bu]
write-host ""
}
The output on this shows no errors, but it just keeps showing the added fields for all of the records for each iteration of the list, so I don't think that it's actually assigning each unique BU to the array name.
I like the hashtable-approach, but I would finetune it a little. Try:
$list2 = ipcsv .\TSE_Contact_List.csv | sort-object BU
$arraylist = #{}
foreach ($vitem in $list2){
if($arraylist.ContainsKey($vitem.BU)) {
#Array exists, add item
$arraylist[($vitem.BU)] += $vitem
} else {
#Array not found, creating it
$arraylist[($vitem.BU)] = #($vitem)
}
}
#TEST: List arrays and number of entries
$arraylist.GetEnumerator() | % {
"Array '$($_.Key)' has $($_.Value.Count) items"
}
You could also use Group-Object like:
$list2 = ipcsv .\TSE_Contact_List.csv | Group-Object BU
#TEST: List groups(regions) and number of entries
$list2 | % {
"Region '$($_.Name)' has $(#($_.Group).Count) items"
}
I'm trying to create a function that combines a new element with a existing one in an array.
So if I've created an array with 2 indexes and the value in those indexes are "Hello" and I call my function. I need it to take the selected index and combine the two values without overwriting or removing any part of the existing values in the array.
So the array looks like this after the function:
Hello
Hello stackoverflow
instead of:
Hello
Hello
So far this is my code:
Function AddToArray ($Index, $Add)
{
#$MainArray = New-Object System.Collections.ArrayList;
$MainArray[$Index] = "$MainArray $Add";
$MainArray | % {foreach ($Index in $MainArray[$Index])
{
$MainArray[$Index] + $Add;
}
}
The only thing this accomplishes for me is overwriting the value of the selected index but not combining them.
Any help is appreciated!
Kind regards
Dennis Berntsson
You're making this way too complicated. Just access the element by index and add the new string to it:
function AddToArrayElement($MainArray, $Index, $Add) {
$MainArray[$Index] += $Add
}
Example:
PS C:\> function AddToArrayElement($MainArray, $Index, $Add) {
>> $MainArray[$Index] += $Add
>> }
>>
PS C:\> $a = New-Object System.Collections.ArrayList
PS C:\> $a.Add('Hello'); $a.Add('Hello')
0
1
PS C:\> $a
Hello
Hello
PS C:\> AddToArrayElement $a 1 ' StackOverflow'
PS C:\> $a
Hello
Hello StackOverflow
After reading this helpful article on the Windows PowerShell Blog, I realized I could "shift" off the first part of an array, but didn't know how to "unshift" or push elements onto the front of an array in PowerShell.
I am creating an array of hash objects, with the last read item pushed onto the array first. I'm wondering if there is a better way to accomplish this.
## Create a list of files for this collection, pushing item on top of all other items
if ($csvfiles[$collname]) {
$csvfiles[$collname] = #{ repdate = $date; fileobj = $csv }, $csvfiles[$collname] | %{$_}
}
else {
$csvfiles[$collname] = #{ repdate = $date; fileobj = $csv }
}
A couple of things to note:
I need to unroll the previous array element with a foreach loop %{$_}, because merely referencing the array would create a nested array structure. I have to have all the elements at the same level.
I need to differentiate between an empty array and one that contains elements. If I attempt to unroll an empty array, it places a $null element at the end of the array.
Thoughts?
PS: The reason the empty hash element produces a NULL value is that $null is treated as a scalar in PowerShell. For details, see https://connect.microsoft.com/PowerShell/feedback/details/281908/foreach-should-not-execute-the-loop-body-for-a-scalar-value-of-null.
ANSWER:
Looks like the best solution is to pre-create the empty array when necessary, rather than code around the $null issue. Here's the rewrite using a .NET ArrayList and a native PoSh array.
if (!$csvfiles.ContainsKey($collname)) {
$csvfiles[$collname] = [System.Collections.ArrayList]#()
}
$csvfiles[$collname].insert(0, #{ repdate = $repdate; fileobj = $csv })
## NATIVE POSH SOLUTION...
if (!$csvfiles.ContainsKey($collname)) {
$csvfiles[$collname] = #()
}
$csvfiles[$collname] = #{ repdate = $repdate; fileobj = $csv }, `
$csvfiles[$collname] | %{$_}
You might want to use ArrayList objects instead, as they support insertions at arbitrary locations. For example:
# C:\> $a = [System.Collections.ArrayList]#(1,2,3,4)
# C:\> $a.insert(0,6)
# C:\> $a
6
1
2
3
4
You can simply use a plus operator:
$array = #('bar', 'baz')
$array = #('foo') + $array
Note: this re-creates actually creates a new array instead of changing the existing one (but the $head, $tail = $array way of shifting you refer to works extactly in the same way).