Compare-Object and include properties not being compared in output - powershell

I'm comparing two CSV files that come from different sources (different column/property names) using the Compare-Object cmdlet. How can I include properties that are in either CSV file in the output without including them in the comparison?
Example CSV data
users1.csv
e-mail-address,name,side
luke#sw.com,Luke,light
users2.csv
e-mail-address,hiredate,hobbies
lando#sw.com,5/2/17,Sabacc
The following gives me a column with the e-mail address and side indicator, but how can I get $Users1.name and $Users2.hiredate without using them in the comparison?
$Users1 = Import-Csv users1.csv
$Users2 = Import-Csv users2.csv
Compare-Object $Users1 $Users2 -Property "E-mail-Address"
I'd like output similar to:
e-mail-address | SideIndicator | name | hiredate
---------------|---------------|------|----------
luke#sw.com | <= | Luke |
lando#sw.com | => | | 5/2/17

Add the PassThru parameter to have Compare-Object return all the properties, then use Select-Object to grab the name and hiredate properties:
Compare-Object $users1 $users2 -Property e-mail-address -PassThru|Select-Object e-mail-address,SideIndicator,name,hiredate

Related

Sort CSV powershell script delete duplicate, keep the one with a special value in 3rd column

How do I delete double entriys in a csv by one column and leave the one with one special value in one of the columns?
Example: I got a csv with
Name;Employeenumber;Accessrights
Max;123456;ReadOnly
Berta;133556;Write
Jhonny;161771;ReadOnly
Max;123456;Write
I want to end up with:
Name;Employeenumber;Accessrights
Max;123456;Write
Berta;133556;Write
Jhonny;161771;ReadOnly
I tried by Get-Content Select-Object -unique, but that does not solve the problem that it should only keep the ones with the value "write" at the property Accessrights.
So I have no clue at all
You can use a combination of sorting and grouping ....
#'
Name;Employeenumber;Accessrights
Max;123456;ReadOnly
Berta;133556;Write
Jhonny;161771;ReadOnly
Max;123456;Write
'# |
ConvertFrom-Csv -Delimiter ';' |
Sort-Object -Property Name, Accessrights -Descending |
Group-Object -Property Name |
ForEach-Object {
$_.Group[0]
}

Select-Object - Keep default columns but add one

I can use Select-Object to choose which columns to show and even add calculated columns. An example:
gci | select *, #{n='LAS'; e={(Get-Date)-$_.LastAccessTime}}
I want add a calculated column but keep the defaut ones. Without the * wildcard I only get my calculated property. With it I get everything. The only workaround I've got to work is to manually list the default property names. Any ideas?
The thing is that that you actually states to display all properties ('*').
So to add to only the standard properties, you first need to get the standard properties.
[string[]]$StdProperties = (Get-ChildItem).PSStandardMembers.DefaultDisplayPropertySet[1].ReferencedPropertyNames
We don't actually want to change the standard property of the objects returned
Get-Childitem | select Name | Get-Member| group TypeName | select Name
Name
----
Selected.System.IO.DirectoryInfo
Selected.System.IO.FileInfo
So we just need to expand on that extracted string array with the new property to use.
$StdProperties += 'LAS'
And finally, to put it to use...
Get-ChildItem | select *, #{n='LAS'; e={(Get-Date) - $_.LastAccessTime}} |
select $StdProperties
Just for fun, building on Abrahams comment, you could do something weird like this:
# get the default properties used on Format-Table
$defaultProps = (((Get-ChildItem | Format-Table | Out-String) -split '\r?\n' |
Where-Object { $_ -match '^\w.*' }) |
Select-Object -First 1) -split '\s+' -ne ''
# now execute the command
Get-ChildItem | Select-Object *, #{n='LAS'; e={(Get-Date)-$_.LastAccessTime}} |
Select-Object ($defaultProps + 'LAS') | Format-Table -AutoSize

Compare-Object different property names

Is there a way to use Compare-Object -property, for comparing 2 properties with different names? I have something like this:
$ComparisonProperty= $ComparisonProperty | Add-Member -MemberType AliasProperty -Name Url -Value ITEM_TARGET_URI -PassThru
Compare-Object $FirstFile $SecondFile -Property Url -PassThru | Where-Object{$_.SideIndicator -eq "<="} | Out-file .\result.txt
But this gives an error:
Cannot bind argument to parameter 'InputObject' because it is null
At $ComparisonProperty
//Edit
Sample data are 2 csv files with many headers, 1 with Url header in it, another with ITEM_TARGET_URI in it. Result should be strings from file 1 that do not exist in file 2. Comparison works if i provide them with the same property names, but the whole point is to force it to compare 2 properties with different names.
To answer the actual question calculated properties or property aliases would get you what you need. You have been trying to use the latter but you need to add the property to the file object itself. Consider the two file examples
id,first_name
1,Elia
2,Nikolos
3,Bert
4,Sharleen
5,Bill
id,beginning_name
1,Elia
2,Nikolos
3,Bert
4,Mildrid
5,Bill
Notice that the headers are different. So now lets try and create the property alias. Assume that I have already imported these files as CSV's
$file2 | Add-Member -MemberType AliasProperty -Name first_name -Value beginning_name
compare-object $file1 $file2 -Property first_name
That will give you the results you were expecting. I added an alias to the second file object
You could just work with column data
Another approach to this is to drop the properties and just work with string arrays. Since I know the headers I want I can just get those columns themselves as well
$namesfromfile1 = $file1 | Select-Object -ExpandProperty first_name
$namesfromfile2 = $file2 | Select-Object -ExpandProperty beginning_name
Compare-Object $urls1 $urls2
Depending on your PS version this can also be shortened. Whether or not its simplified is up to the reader.
Compare-Object ($file1).first_name ($file2).beginning_name

Use Import-Csv to read changable column Titles by location

I'm trying to see if there is a way to read the column values in a csv file based on the column location. The reason for this is the file I'm being handed always has it's titles being changed...
For example, lets say csv file column A (via excel) looks like the following:
ColumnOne
ValueOne
ValueTwo
ValueThree
Now the user changes the title:
Column 1
ValueOne
ValueTwo
ValueThree
Now I want to create an array of the first column. Normally what I do is the following:
$arrayFirstColumn = Import-Csv 'C:\test\test1.csv' | where-object {$_.ColumnOne} | select-object -expand 'ColumnOne'
However, as we can see if ColumnOne is changed to Column 1, it breaks this code. How can I create this array to allow an interchangeable column title, but the column location will always be the same?
You can specify headers of your own on import:
Import-Csv 'C:\path\to\your.csv' -Header 'MyHeaderA','MyHeaderB',...
As long as you don't export the data back to a CSV (or don't require the original headers to be in the output CSV as well) you can use whatever names you like. You can also specify as many header names as you like. If their number is less than the number of the columns in the CSV the additional columns will be omitted, if it's greater then the columns for the additional headers will be empty.
If you need to preserve the original headers you could get the header name(s) you need to work with in variable(s) like this:
$csv = Import-Csv 'C:\test\test1.csv'
$firstCol = $csv | Select-Object -First 1 | ForEach-Object {
$_.PSObject.Properties | Select-Object -First 1 -Expand Name
}
$arrayFirstColumn = $csv | Where-Object {$_.$firstCol} |
Select-Object -Expand $firstCol
Or you could simply read the first line from the CSV and split it to get an array with the headers:
$headers = (Get-Content 'C:\test\test1.csv' -TotalCount 1) -split ','
$firstCol = $headers[0]
One option:
$ImportFile = 'C:\test\test1.csv'
$FirstColumn = ((Get-Content $ImportFile -TotalCount 2 | ConvertFrom-Csv).psobject.properties.name)[0]
$FirstColumn
$arrayFirstColumn = Import-Csv $ImportFile | where-object {$_.$FirstColumn} | select-object -expand $FirstColumn
If you are using PowerShell v2.0 then the expression for $FirstColumn in $mjolinor's answer would be:
$FirstColumn = ((Get-Content $ImportFile -TotalCount 2 | ConvertFrom-Csv).psobject.properties | ForEach-Object {$_.name})[0]
(Apologies for starting a new answer; I do not yet have enough reputation to add a comment to mjolinor's post)

Sort-Object by greatest numerical value value from Import-CSV

I want the greatest value (mailboxSize) at the top of the file. I have a cvs as inport.
When I do the following sort cmd:
Import-Csv import.csv| Sort-Object MailboxSize,DisplayName -Descending | Export-Csv SORT.csv
I get the following result:
"DisplayName","MailboxSize"
"persone6","9941"
"persone3","8484"
"persone1","7008"
"persone4","4322"
"persone5","3106"
"persone7","27536"
"persone10","24253"
"persone8","1961"
"persone9","17076"
"persone11","17012"
"persone2","15351"
"persone12","11795"
"persone14","1156"
"persone13","1008"
But I want this as a result!
"persone7","27536"
"persone10","24253"
"persone9","17076"
"persone11","17012"
"persone2","15351"
"persone12","11795"
"persone6","9941"
"persone3","8484"
"persone1","7008"
"persone4","4322"
"persone5","3106"
"persone14","1156"
"persone13","1008"
When importing a CSV-file, all properties are made string-type. You have to cast the MailboxSize to an int before you can sort it properly. Try:
Import-Csv import.csv |
Sort-Object {[int]$_.MailboxSize}, DisplayName -Descending |
Export-Csv SORT.csv
You should also use the -NoTypeInformation switch in Export-CSV to avoid the #TYPE ..... line (first line in an exported CSV-file).
Sample:
$data = #"
"DisplayName","MailboxSize"
"persone6","9941"
"persone3","8484"
"persone1","7008"
"persone4","4322"
"persone5","3106"
"persone7","27536"
"persone10","24253"
"persone8","1961"
"persone9","17076"
"persone11","17012"
"persone2","15351"
"persone12","11795"
"persone14","1156"
"persone13","1008"
"# | ConvertFrom-Csv
$data |
Sort-Object {[int]$_.MailboxSize}, DisplayName -Descending |
Export-Csv SORT.csv -NoTypeInformation
SORT.csv
"DisplayName","MailboxSize"
"persone7","27536"
"persone10","24253"
"persone9","17076"
"persone11","17012"
"persone2","15351"
"persone12","11795"
"persone6","9941"
"persone3","8484"
"persone1","7008"
"persone4","4322"
"persone5","3106"
"persone8","1961"
"persone14","1156"
"persone13","1008"
I'm guessing the usernames are fake, but be aware that the same issue goes for DisplayName if your usernames actually was personeXX where XX is an int. Like:
persone7 27536
persone20 27536
persone13 27536
To sort them probably, you'd have to create a scriptblock for Sort-Object or create your own function to split the value and sort them correctly.