The question to be asked by someone is to edit the post trial method
Powershell : Comparing Variable from one CSV to another CSV and create new variable on based of that
-
neither of your csv files work at all, can you please update them to be separated by commas or tabs so we can see exactly what you're trying to read in? – colsw May 30 '17 at 13:37
2 Answers
your csv files aren't properly formatted so I can't be sure if this is exactly what you need, but this should point you in the right direction:
$File1 = Import-Csv .\file1.csv -Delimiter "`t"
$File2 = Import-Csv .\file2.csv -Delimiter "`t"
$NewFile1 = $File1 | % {
$Obj = [PSCustomObject]$_
$Obj | Add-Member -Mem NoteProperty -Name Patched -Value ([bool]($_.CommanName -in $File2.CommanName))
return $Obj
}
$NewFile1
Which will output
Server HotfixID CommanName Patched
------ -------- ---------- -------
apdk778 kb3333 apdk778 kb3333 True
apdk778 yn2919 apdk778 yn2919 True
apdk778 lk4898 apdk778 lk4898 True
iijka211 kb3333 iijka211 kb3333 False
iijka211 yn2919 iijka211 yn2919 True
iijka211 lk4898 iijka211 lk4898 True
I replaced the first two sets of spaces with tabs in order to read in the csv files.
- 3,008
- 1
- 13
- 27
-
Will this check for a given `Server`, or just that the `CommanName` exists across all servers? I think you have to filter out a subset of `$File2` e.g. `$serverDetails = $File2 | Where-Object {$_.Server -eq $Obj.Server}`. Appreciate this isn't clear in the question – G42 May 30 '17 at 14:06
-
2@gms0ulman he's already included a column with both the server name and update I think - if i'm wrong about the Csv format I can update with a two-loop solution, bit I think this is ok for him. – colsw May 30 '17 at 14:08
-
Good point, I missed that the `CommanName` is the other 2 columns combined. – G42 May 30 '17 at 14:10
-
Nicely done. Just to show a shorter (though possibly more obscure to beginners) variant: `$_ | Add-Member -PassThru -Mem NoteProperty -Name Patched -Value ([bool]($_.CommanName -in $File2.CommanName))` is sufficient as the body of the `%` script block. – mklement0 May 30 '17 at 14:14
ConnorLSW's helpful answer works well, but you can simplify to a single Select-Object call that defines a calculated property, which also performs better:
$file1Objects = Import-Csv .\file1.csv -Delimiter "`t"
$file2Objects = Import-Csv .\file2.csv -Delimiter "`t"
$decoratedFile1Objects = $file1Objects | Select-Object *,
@{ n='Patched'; e={ ('No', 'Yes')[$file2Objects.CommanName -contains $_.CommanName] } }
Get-Help -Detailed Select-Object briefly explains how calculated properties work; for a more detailed explanation, see this answer of mine.
Note the use of idiom (<choice-A>, <choice-B>)[<Boolean-Test>] for selecting one of two choices based on the outcome of a Boolean test:
('No', 'Yes')is a string array with elementsNo(index0) andYes(index1).$file2Objects.CommanName -contains $_.CommanNameis a Boolean expression that, when used in the context of an array index ([...]) is coerced to either0(if$False) or1(if$True).In other words:
- if the Boolean expression returns
$False, index0is used, resulting in stringNo. - if the Boolean expression returns
$True, index1is used, resulting in stringYes.
- if the Boolean expression returns
Note that this approach is a workaround for PowerShell's lack of ternary conditionals that other languages, such as C#, have and that the order in which the branches are specified is reversed:
- C# pseudo-code:
<Boolean-Test> ? "Yes" : "No" - PowerShell workaround:
('No', 'Yes')[<Boolean-Test>] - Aside from the ordering of branches / choices, there is another important difference: if your array elements are commands/expressions, they are all evaluated, irrespective of which one is chosen (no short-circuiting).
- There is an outstanding feature request for ternary conditionals.
- C# pseudo-code:
As for what you tried:
Your nested foreach-loop approach:
- doesn't create an output object for un-patched rows
- and, for a patched row, creates a new object whose only property is the new one.
The following code fixes these issues, again relying on Select-Object to add the new property:
$decoratedFile1Objects = foreach ($f1 in $file1) {
# Add property and default to 'No'
$f1Decorated = Select-Object -InputObject $f1 *, @{ n='Patched'; e={ 'No' } }
foreach($f2 in $file2) {
if ($f1.CommanName -eq $f2.CommanName) {
# Match found: set property to 'Yes' and exit the inner loop
$f1Decorated.Patched = 'Yes'
break
}
}
$f1Decorated # Output the decorated object.
}
However, use of PowerShell's -contains and -in array-membership test operators is not only much more convenient, but also performs better.
- 312,089
- 56
- 508
- 622
-
@AtulD: Glad to hear it. I've added a variable assignment analogous to `$all = ...` for clarity. – mklement0 May 31 '17 at 19:27