PowerShell: Expression only with Last item of an Array - powershell

I've been stuck on this for a little while however I've got an array of People and im trying to get the last person and creating a seperate column with that person only.
I've played around with #{NAME = 'NAME' Expression = {}} in Select-Object but I don't really know how to tackle it.
Current:
| Employee |
|---------------|
| John Doe |
| Jane West |
| Jordan Row |
| Paul Willson |
| Andrew Wright |
Desired Result:
| Employee | Employee2 |
|--------------|---------------|
| John Doe | |
| Jane West | |
| Jordan Row | |
| Paul Willson | Andrew Wright |
TIA!

So what I decided to do here is create 2 groups. One group contains all of the values except the last 2, and the other group contains these last 2 values
# create the sample array
$employees = #(
'John Doe'
'Jane West'
'Jordan Row'
'Paul Willson'
'Andrew Wright'
)
$employees |
# Separate objects into 2 groups: those contained in the last 2 values and those not contained in the last 2 values
Group-Object {$_ -in ($employees | Select-Object -Last 2)} |
ForEach-Object {
switch ($_) {
{$_.name -eq 'False'} { # 'False' Name of group where values are not one of the last 2
# Iterate through all the values and assign them to Employee property. Leave Employee2 property blank
$_.group | ForEach-Object {
[PSCustomObject]#{
Employee = $_
Employee2 = ''
}
}
}
{$_.name -eq 'True'} { # 'True' Name of group where values are those of the last 2
# Create an object that assigns the values to Employee and Employee2
[PSCustomObject]#{
Employee = $_.group[0]
Employee2 = $_.group[1]
}
}
}
}
Output
Employee Employee2
-------- ---------
John Doe
Jane West
Jordan Row
Paul Willson Andrew Wright
Edit
Here is another way you can do it
$employees[0..($employees.Count-3)] | ForEach-Object {
[PSCustomObject]#{
Employee = $_
Employee2 = ''
}
}
[PSCustomObject]#{
Employee = $employees[-2]
Employee2 = $employees[-1]
}

Related

Grouping duplicate values in a Powershell custom object without deleting

I have a PS custom object that is in this format:
|Name|Number|Email|
|----|------| -----|
|Bob| 23| bob.bob#test.com|
|Tom|124|tom.tom#test.com|
|Jeff|125|jeff.jeff#test.com|
|Jeff|127|jeff.jeff#test.com|
|Jeff|129|jeff.jeff#test.com|
|Jessica|126|jessica.jessica#test.com|
|Jessica|132|jessica.jessica#test.com|
I'd like to group together the fields where the numbers are the same. I.e:
|Name|Number|Email|
|----|------|-----|
|Bob|123|bob.bob#test.com|
|Tom|124|tom.tom#test.com|
|Jeff|125,127,129|jeff.jeff#test.com|
|Jessica|126,132|jessica.jessica#test.com|
I've tried a number of compare-object, sort-object, creating a new array etc. but I can't seem to get it.
Any ideas?
Group-Object is perfectly suited for that task. With the help of calculated properties, you can create a select statement that will produce the desired output in one sweep.
$data = #'
Name|Number|Email|
Bob| 23| bob.bob#test.com|
Tom|124|tom.tom#test.com|
Jeff|125|jeff.jeff#test.com|
Jeff|127|jeff.jeff#test.com|
Jeff|129|jeff.jeff#test.com|
Jessica|126|jessica.jessica#test.com|
Jessica|132|jessica.jessica#test.com|
'# | ConvertFrom-Csv -Delimiter '|'
# Group by name and email
$data | Group-Object -Property Name, Email |
Select #{'Name' = 'Name' ; 'Expression' = { $_.Group[0].Name } },
#{'Name' = 'Number' ; 'Expression' = { $_.Group.Number } },
#{'Name' = 'Email' ; 'Expression' = { $_.Group[0].Email } }
Output
Name Number Email
---- ------ -----
Bob 23 bob.bob#test.com
Tom 124 tom.tom#test.com
Jeff {125, 127, 129} jeff.jeff#test.com
Jessica {126, 132} jessica.jessica#test.com
References
Group-Object
about calculated properties

PowerShell: Iterating Multiple Variables not working as expected

I am trying to iterate over an array $dailyTasks to find 'Blank' i.e. '' values in the EmployeeName column and inject names from another array into those empty values.
Example of what the array looks like before the for loop starts:
| Task | EmployeeName | EmployeeName2 |
|-------|--------------|---------------|
| Task1 | | |
| Task2 | Person Y | |
| Task3 | | |
| Task4 | Person Z | Person X |
This is my for loop code that produces an undesired result. $randomisedUsers is an Object[]
$randomisedUsers | Group-Object { $_ -in ($randomisedUsers | Select-Object -Last 2) } | ForEach-Object {
if ($_.Name -eq 'True') {
for ($i = 0; $i -lt $dailyTasks.Count; $i++) {
if ($dailyTasks[$i].Task -eq 'Task4') {
$dailyTasks[$i].EmployeeName = $_.Group.EmployeeName[0]
$dailyTasks[$i].EmployeeName2 = $_.Group.EmployeeName[1]
}
}
} else {
for ($i = 0; $i -lt $dailyTasks.Count; $i++) {
if ($dailyTasks[$i].EmployeeName -eq '') {
if ($_.Count -gt '1') {
for ($x = 0; $x -lt $_.Group.EmployeeName.Count; $x++) {
$dailyTasks[$i].EmployeeName = $_.Group.EmployeeName[$x]
}
} else {
$dailyTasks[$i].EmployeeName = $_.Group.EmployeeName
}
}
}
}
}
Result:
| Task | EmployeeName | EmployeeName2 |
|-------|--------------|---------------|
| Task1 | Person A | |
| Task2 | Person Y | |
| Task3 | Person A | |
| Task4 | Person Z | Person X |
The problem here is that $_.Group.EmployeeName contains two objects but for whatever reason the result table doesnt populate Person B in the array:
$_.Group.EmployeeName
{Person A, Person B}
The desired result in this case is:
| Task | EmployeeName | EmployeeName2 |
|-------|--------------|---------------|
| Task1 | Person A | |
| Task2 | Person Y | |
| Task3 | Person B | |
| Task4 | Person Z | Person X |
Im not completely sure where im going wrong in my for loops and i've been stuck on this for a while...
TIA
I would personally use something like this:
$csv = #'
Task,EmployeeName,EmployeeName2
Task1,,
Task2,Person Y,
Task3,,
Task4,Person Z,Person X
'# | ConvertFrom-Csv
$fillEmployees = [System.Collections.ArrayList]#(
'Person A'
'Person B'
)
foreach($line in $csv)
{
if([string]::IsNullOrWhiteSpace($line.EmployeeName))
{
$line.EmployeeName = $fillEmployees[0]
$fillEmployees.RemoveAt(0)
}
}
The flow is quite simple, if the loop finds a value in EmployeeName that is null or has white spaces it will replace that value with the index 0 of $fillEmployees and then remove that index 0 from the list.
It's hard to tell what you're trying to accomplish with your code, but if you have an array of the type System.Array filled with random names which will be used to fill this empty values on EmployeeName you can convert that Array to an ArrayList which will allow you to use the .RemoveAt(..) method:
PS /> $fillEmployees = 0..10 | ForEach-Object {"Employee {0}" -f [char](Get-Random -Minimum 65 -Maximum 90)}
PS /> $fillEmployees
Employee J
Employee S
Employee D
Employee P
Employee O
Employee E
Employee M
Employee K
Employee R
Employee F
Employee A
PS /> $fillEmployees.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Attempting to Remove an item from an Array would result in the following:
PS /> $fillEmployees.RemoveAt(0)
Exception calling "RemoveAt" with "1" argument(s): "Collection was of a fixed size."
At line:1 char:1
...
...
However if convert it to an ArrayList (not convert it but copy it):
PS /> $fillEmployees = [System.Collections.ArrayList]$fillEmployees
PS /> $fillEmployees.RemoveAt(0)

Problem to compare array in PowerShell and copy it

I have a
List of data in csv and I need to compare it again a set of array.
And then I need to copy the whole data and then insert into new set of array.
Sample data in CSV
ID VersionLabel Created Author Modified Editor FileLeafRef Title Remarks Business_x0020_Unit DE_Department Record_x0020_Disposition_x0020_Date Note
1 0.6 02/11/2020 03:21:12 John 02/11/2020 03:21:12 John fields.csv Test1 Test111 Finance Department A 02/10/2020 23:00:00 notes
1 0.5 02/10/2020 11:16:39 John 02/10/2020 11:16:39 John fields.csv Test1 Test111 Marketing Department A old 02/10/2019 23:00:00 notes old
1 0.4 02/10/2020 10:28:54 John 02/10/2020 10:28:54 John fields.csv Test1 Test111 Sales Department A old 02/10/2019 23:00:00 notes old
1 0.3 02/10/2020 09:29:31 John 02/10/2020 09:29:31 John fields.csv Test1 Test111 Sales Department A old 02/10/2020 23:00:00 notes old
1 0.2 02/10/2020 09:28:35 John 02/10/2020 09:28:35 John fields.csv Test1 Test111 Sales Department A old 02/10/2020 23:00:00 notes old
Set of Arrary that I have
ID
VersionLabel
Created
Author
which basically i got using following code
$FinalReport = Import-Csv $OriginalReport -Delimiter ","
$Header = $FinalReport[0].psobject.properties.name
$FinalReportGrouped = $FinalReport | Group-Object {$_.ID} |Foreach-Object {
$_.Group | Sort-Object VersionLabel -Descending | Select-Object -First 1
}
The problem is, I'm not able to come out with the proper logic on how to compare the set of array.
Example, below is the code to compare it..
foreach ($row in $FinalReportGrouped)
{
$FinalReport | Where-Object {$_.ID -eq $row.ID} | Foreach-Object {
#I need to compare here, if the value inside $ is not equal $header array
if($_.value -ne $header)
{
# and then I need to copy whole content of $row into $_ variable, below code seems got problem. I can copy if using $_.Department = $row.Department, but I need to get it dynamically.
$_ = $row #this is wrong maybe..
}
}
}

Select-Object -ExcludeProperty based on the property's value

I have an object that has a large amount of properties. I want to return several of these properties, whose names may not always be consistent. I want to EXCLUDE properties that have or contain a particular value.
$notneeded = #('array of properties that I do not wish to select')
$csvPath = "$Log\$Summary"
$csvData = Get-Content -Path $csvPath | Select-Object -Skip 1 | Out-String | ConvertFrom-Csv #the first line is extra (not a header), needs skipped
$csvData | Select-Object -Property * -ExcludeProperty $notneeded
If the list of properties to exclude was static, then I could use this. But I want to exclude properties from view that contain a particular value.
INPUT CSV
John,Doe,120 jefferson st.,Riverside, NJ, 08075
Jack,McGinnis,220 hobo Av.,Phila, PA,09119
"John ""Da Man""",Repici,120 Jefferson St.,Riverside, NJ,08075
Stephen,Tyler,"7452 Terrace ""At the Plaza"" road",SomeTown,SD, 91234
,Blankman,,SomeTown, SD, 00298
"Joan ""the bone"", Anne",Jet,"9th, at Terrace plc",Desert City,CO,00123
SCRIPT
$csvData = Invoke-WebRequest -Uri "https://people.sc.fsu.edu/~jburkardt/data/csv/addresses.csv" | ConvertFrom-Csv -Header "Name", "Surname", "Address", "City", "State", "Zip"
$particularValue = "*120*"
$notneeded = #()
$csvData | Foreach-Object { $notneeded += $_.PSObject.Properties | Where-Object Value -like $particularValue | Select-Object Name }
$notneeded = $notneeded | Select-Object -Unique -ExpandProperty Name
$csvData | Select-Object * -ExcludeProperty $notneeded | Format-Table
Note That e.g. I want to exclude column where 120 is mentioned. Also I named columns in my script
OUTPUT (See the address column is missing)
Name Surname City State Zip
---- ------- ---- ----- ---
John Doe Riverside NJ 08075
Jack McGinnis Phila PA 09119
John "Da Man" Repici Riverside NJ 08075
Stephen Tyler SomeTown SD 91234
Blankman SomeTown SD 00298
Joan "the bone", Anne Jet Desert City CO 00123

Check if Two Values in Array / Data Table match

I put this question on here before, but I missed an important detail which causes huge issue. There will be duplicate account numbers. So I'm doing it by current_read_date now to avoid duplicates in account number. To ensure values being added to $accounts are new from the CSV.
I am trying to get all the accounts from $f which do not match the accounts in $table4 into $accounts. But I need to also check if the current_read_date matches or not.
CSV into Array $f:
Account_no |occupant_code|current_read_date
-----------|-------------|-----------------
12345 | 1 | 7/17/2017 15:32:00 AM
67890 | 2 | 7/17/2017 12:00:00 AM
45678 | 3 | 7/17/2017 12:00:00 AM
DataTable $table4
Account_no |occupant_code|current_read_date
-----------|-------------|-----------------
12345 | 1 | 7/17/2017 12:00:00 AM
12345 | 1 | 7/17/2017 15:32:00 AM
67890 | 1 | 7/17/2017 13:00:00 AM
67890 | 1 | 7/17/2017 22:00:00 AM
45678 | 3 | 7/17/2017 12:00:00 AM
Desired result:
$accounts =
67890 | 2 | 7/17/2017 12:00:00 AM
Current code:
$accounts = Import-Csv $f |
select account_no, current_read_date |
where { $table4.account_no -notcontains $_.account_no }
What this needs to do is to check that current_read_date doesn't match, i.e.:
12345: account and date from $f and $table4 match; so it's ignored
67890: account matches $table4, but the current_read_date does not match, so it is a new value, thus it is added to $accounts.
I believe I need to use Group-Object, but I do not know how to use that correctly.
I tried:
Import-Csv $f |
select account_no, occupant_code |
Group-Object account_no |
Where-Object { $_.Group.current_read_date -notcontains $table4.current_read_date }
This is the previous question:
How to use Group-Object on this?
All the answers here failed because I forgot to provide the information that account_no is not unique; there will be frequent duplicates.
All assistance would be greatly appreciated, I've been stuck on this for awhile.
I've also tried this
$testList = #()
$testList = Import-Csv $f | select account_no, occupant_code, current_read_date, current_reading
$accounts = new-object System.Collections.ArrayList
$testSet = $table4
foreach($myThing in $testList)
{
if($myThing.account_no -in $testSet.account_no )
{
foreach($ts in $testSet)
{
if ($myThing.account_no -match $ts.account_no -and $myThing.occupant_code -match $ts.occupant_code)
{
$ts.account_no
$ts.occupant_code
}
else {
$accounts.add($myThing) | out-null
write-host $mything
}
}
}
This fails because it goes through each number, therefore, 12345 will be checked against 67890, and will added 12345 to the $accounts list, even though it already exists, because I cannot compare each individually at a time with table4.
Thanks
$accounts = $f | Where {
$Record = $_
$AccNo = $table4 | Where {$_.Account_no -eq $Record.Account_no}
!($AccNo | Where {$_.current_read_date -eq $Record.current_read_date})
}
$accounts | Format-Table
Result:
Account_no occupant_code current_read_date
---------- ------------- -----------------
67890 2 7/17/2017 12:00:00 AM
Build a reference list from the records in $table4
$ref = $table4 | ForEach-Object {
$_.Account_no, $_.occupant_code, $_.current_read_date -join ','
}
then filter the records from $f by that reference list:
$accounts = Import-Csv $f | Where-Object {
$ref -notcontains ($_.Account_no, $_.occupant_code, $_.current_read_date -join ',')
} | Select-Object -Expand Account_no -Unique