mirror of
https://github.com/dfinke/ImportExcel.git
synced 2025-12-23 19:53:58 +00:00
Added Merge-Workshet. Made perf improvements to Import-Excel
This commit is contained in:
@@ -14,6 +14,10 @@ if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue)
|
||||
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName TabColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Merge-Worksheet -ParameterName AddBackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Merge-Worksheet -ParameterName ChangeBackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Merge-Worksheet ` -ParameterName DeleteBackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Merge-Worksheet -ParameterName KeyFontColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
|
||||
Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion
|
||||
|
||||
179
ImportExcel.psm1
179
ImportExcel.psm1
@@ -1,43 +1,44 @@
|
||||
Add-Type -Path "$($PSScriptRoot)\EPPlus.dll"
|
||||
|
||||
. $PSScriptRoot\AddConditionalFormatting.ps1
|
||||
. $PSScriptRoot\Charting.ps1
|
||||
. $PSScriptRoot\ColorCompletion.ps1
|
||||
. $PSScriptRoot\ConvertExcelToImageFile.ps1
|
||||
. $PSScriptRoot\Compare-WorkSheet.ps1
|
||||
. $PSScriptRoot\ConvertFromExcelData.ps1
|
||||
. $PSScriptRoot\ConvertFromExcelToSQLInsert.ps1
|
||||
. $PSScriptRoot\ConvertToExcelXlsx.ps1
|
||||
. $PSScriptRoot\Copy-ExcelWorkSheet.ps1
|
||||
. $PSScriptRoot\Export-Excel.ps1
|
||||
. $PSScriptRoot\Export-ExcelSheet.ps1
|
||||
. $PSScriptRoot\Get-ExcelColumnName.ps1
|
||||
. $PSScriptRoot\Get-ExcelSheetInfo.ps1
|
||||
. $PSScriptRoot\Get-ExcelWorkbookInfo.ps1
|
||||
. $PSScriptRoot\Get-HtmlTable.ps1
|
||||
. $PSScriptRoot\Get-Range.ps1
|
||||
. $PSScriptRoot\Get-XYRange.ps1
|
||||
. $PSScriptRoot\Import-Html.ps1
|
||||
. $PSScriptRoot\InferData.ps1
|
||||
. $PSScriptRoot\Invoke-Sum.ps1
|
||||
. $PSScriptRoot\New-ConditionalFormattingIconSet.ps1
|
||||
. $PSScriptRoot\New-ConditionalText.ps1
|
||||
. $PSScriptRoot\New-ExcelChart.ps1
|
||||
. $PSScriptRoot\New-PSItem.ps1
|
||||
. $PSScriptRoot\Open-ExcelPackage.ps1
|
||||
. $PSScriptRoot\Pivot.ps1
|
||||
. $PSScriptRoot\Send-SQLDataToExcel.ps1
|
||||
. $PSScriptRoot\Set-CellStyle.ps1
|
||||
. $PSScriptRoot\Set-Column.ps1
|
||||
. $PSScriptRoot\Set-Row.ps1
|
||||
. $PSScriptRoot\SetFormat.ps1
|
||||
. $PSScriptRoot\TrackingUtils.ps1
|
||||
. $PSScriptRoot\Update-FirstObjectProperties.ps1
|
||||
#region import everything we need
|
||||
Add-Type -Path "$($PSScriptRoot)\EPPlus.dll"
|
||||
. $PSScriptRoot\AddConditionalFormatting.ps1
|
||||
. $PSScriptRoot\Charting.ps1
|
||||
. $PSScriptRoot\ColorCompletion.ps1
|
||||
. $PSScriptRoot\ConvertExcelToImageFile.ps1
|
||||
. $PSScriptRoot\Compare-WorkSheet.ps1
|
||||
. $PSScriptRoot\ConvertFromExcelData.ps1
|
||||
. $PSScriptRoot\ConvertFromExcelToSQLInsert.ps1
|
||||
. $PSScriptRoot\ConvertToExcelXlsx.ps1
|
||||
. $PSScriptRoot\Copy-ExcelWorkSheet.ps1
|
||||
. $PSScriptRoot\Export-Excel.ps1
|
||||
. $PSScriptRoot\Export-ExcelSheet.ps1
|
||||
. $PSScriptRoot\Get-ExcelColumnName.ps1
|
||||
. $PSScriptRoot\Get-ExcelSheetInfo.ps1
|
||||
. $PSScriptRoot\Get-ExcelWorkbookInfo.ps1
|
||||
. $PSScriptRoot\Get-HtmlTable.ps1
|
||||
. $PSScriptRoot\Get-Range.ps1
|
||||
. $PSScriptRoot\Get-XYRange.ps1
|
||||
. $PSScriptRoot\Import-Html.ps1
|
||||
. $PSScriptRoot\InferData.ps1
|
||||
. $PSScriptRoot\Invoke-Sum.ps1
|
||||
. $PSScriptRoot\Merge-Worksheet.ps1
|
||||
. $PSScriptRoot\New-ConditionalFormattingIconSet.ps1
|
||||
. $PSScriptRoot\New-ConditionalText.ps1
|
||||
. $PSScriptRoot\New-ExcelChart.ps1
|
||||
. $PSScriptRoot\New-PSItem.ps1
|
||||
. $PSScriptRoot\Open-ExcelPackage.ps1
|
||||
. $PSScriptRoot\Pivot.ps1
|
||||
. $PSScriptRoot\Send-SQLDataToExcel.ps1
|
||||
. $PSScriptRoot\Set-CellStyle.ps1
|
||||
. $PSScriptRoot\Set-Column.ps1
|
||||
. $PSScriptRoot\Set-Row.ps1
|
||||
. $PSScriptRoot\SetFormat.ps1
|
||||
. $PSScriptRoot\TrackingUtils.ps1
|
||||
. $PSScriptRoot\Update-FirstObjectProperties.ps1
|
||||
|
||||
|
||||
New-Alias -Name Use-ExcelData -Value "ConvertFrom-ExcelData" -Force
|
||||
New-Alias -Name Use-ExcelData -Value "ConvertFrom-ExcelData" -Force
|
||||
|
||||
if ($PSVersionTable.PSVersion.Major -ge 5) {
|
||||
if ($PSVersionTable.PSVersion.Major -ge 5) {
|
||||
. $PSScriptRoot\Plot.ps1
|
||||
|
||||
Function New-Plot {
|
||||
@@ -46,12 +47,12 @@ if ($PSVersionTable.PSVersion.Major -ge 5) {
|
||||
[PSPlot]::new()
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
Write-Warning 'PowerShell 5 is required for plot.ps1'
|
||||
Write-Warning 'PowerShell Excel is ready, except for that functionality'
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
Function Import-Excel {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -209,7 +210,7 @@ Function Import-Excel {
|
||||
|
||||
Notice that empty rows and empty columns are not imported.
|
||||
|
||||
.EXAMPLE
|
||||
.EXAMPLE
|
||||
Import data from an Excel worksheet. One object is created for each row. The property names are provided with the ‘-HeaderName’ parameter. The import will start from row 2 and empty columns and rows are not imported.
|
||||
|
||||
----------------------------------------------------------
|
||||
@@ -236,6 +237,7 @@ Function Import-Excel {
|
||||
#>
|
||||
|
||||
[CmdLetBinding(DefaultParameterSetName)]
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
|
||||
Param (
|
||||
[Alias('FullName')]
|
||||
[Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Position=0, Mandatory)]
|
||||
@@ -251,34 +253,13 @@ Function Import-Excel {
|
||||
[Switch]$NoHeader,
|
||||
[Alias('HeaderRow','TopRow')]
|
||||
[ValidateRange(1, 9999)]
|
||||
[Int]$StartRow,
|
||||
[Int]$StartRow = 1,
|
||||
[Switch]$DataOnly,
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[String]$Password
|
||||
)
|
||||
|
||||
Begin {
|
||||
Function Add-Property {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Add the property name and value to the hashtable that will create a new object for each row.
|
||||
#>
|
||||
|
||||
Param (
|
||||
[Parameter(Mandatory)]
|
||||
[String]$Name,
|
||||
$Value
|
||||
)
|
||||
|
||||
Try {
|
||||
$NewRow.$Name = $Value
|
||||
Write-Verbose "Import cell '$($Worksheet.Cells[$R, $P.Column].Address)' with property name '$Name' and value '$Value'"
|
||||
}
|
||||
Catch {
|
||||
throw "Failed adding the property name '$Name' with value '$Value': $_"
|
||||
}
|
||||
}
|
||||
|
||||
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
Function Get-PropertyNames {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
@@ -313,7 +294,7 @@ Function Import-Excel {
|
||||
}
|
||||
|
||||
foreach ($C in $Columns) {
|
||||
$Worksheet.Cells[$StartRow,$C] | where {$_.Value} | Select-Object @{N='Column'; E={$C}}, Value
|
||||
$Worksheet.Cells[$StartRow,$C] | Where-Object {$_.Value} | Select-Object @{N='Column'; E={$C}}, Value
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -356,56 +337,40 @@ Function Import-Excel {
|
||||
$Worksheet = $Excel.Workbook.Worksheets | Select-Object -First 1
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Set the top row
|
||||
if (((-not ($NoHeader -or $HeaderName)) -and ($StartRow -eq 0))) {
|
||||
$StartRow = 1
|
||||
}
|
||||
#endregion
|
||||
|
||||
if (-not ($AllCells = $Worksheet.Cells | where {($_.Start.Row -ge $StartRow)})) {
|
||||
Write-Warning "Worksheet '$WorksheetName' in workbook '$Path' is empty after StartRow '$StartRow'"
|
||||
}
|
||||
else {
|
||||
Write-Debug $sw.Elapsed.TotalMilliseconds
|
||||
#region Get rows and columns
|
||||
#If we are doing dataonly it is quicker to work out which rows to ignore before processing the cells.
|
||||
if ($DataOnly) {
|
||||
$CellsWithValues = $AllCells | where {$_.Value}
|
||||
|
||||
$Columns = $CellsWithValues.Start.Column | Sort-Object -Unique
|
||||
$Rows = $CellsWithValues.Start.Row | Sort-Object -Unique
|
||||
#If we are using headers startrow will be the headerrow so examine data from startRow + 1,
|
||||
if ($NoHeader) {$range = "A" + ($StartRow ) + ":" + $Worksheet.Dimension.End.Address }
|
||||
else {$range = "A" + ($StartRow + 1 ) + ":" + $Worksheet.Dimension.End.Address }
|
||||
#We're going to look at every cell and build 2 hash tables holding rows & columns which contain data.
|
||||
#Want to Avoid 'select unique' operations & large Sorts, becuse time time taken increases with square
|
||||
#of number of items (PS uses heapsort at large size). Instead keep a list of what we have seen,
|
||||
#using Hash tables: "we've seen it" is all we need, no need to worry about "seen it before" / "Seen it many times"
|
||||
$colHash = @{}
|
||||
$rowHash = @{}
|
||||
foreach ($cell in $Worksheet.Cells[$range]) {
|
||||
if ($cell.Value -ne $null) {$colHash[$cell.Start.Column]=1; $rowHash[$cell.Start.row]=1 }
|
||||
}
|
||||
$rows = ($StartRow..($Worksheet.Dimension.End.Row)).Where({$rowHash[$_]})
|
||||
$columns = (1..($Worksheet.Dimension.End.Column) ).Where({$colHash[$_]})
|
||||
}
|
||||
else {
|
||||
$LastColumn = $AllCells.Start.Column | Sort-Object -Unique | Select-Object -Last 1
|
||||
$Columns = 1..$LastColumn
|
||||
|
||||
$LastRow = $AllCells.Start.Row | Sort-Object -Unique | Select-Object -Last 1
|
||||
$Rows = $StartRow..$LastRow | where {($_ -ge $StartRow) -and ($_ -gt 0)}
|
||||
$Columns = ($Worksheet.Dimension.Start.Column)..($Worksheet.Dimension.End.Column)
|
||||
if ($NoHeader) {$Rows = ( $StartRow)..($Worksheet.Dimension.End.Row) }
|
||||
else {$Rows = (1 + $StartRow)..($Worksheet.Dimension.End.Row) }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Create property names
|
||||
if ((-not $Columns) -or (-not ($PropertyNames = Get-PropertyNames -Columns $Columns -StartRow $StartRow))) {
|
||||
throw "No column headers found on top row '$StartRow'. If column headers in the worksheet are not a requirement then please use the '-NoHeader' or '-HeaderName' parameter."
|
||||
}
|
||||
|
||||
if ($Duplicates = $PropertyNames | Group-Object Value | where Count -GE 2) {
|
||||
if ($Duplicates = $PropertyNames | Group-Object Value | Where-Object Count -GE 2) {
|
||||
throw "Duplicate column headers found on row '$StartRow' in columns '$($Duplicates.Group.Column)'. Column headers must be unique, if this is not a requirement please use the '-NoHeader' or '-HeaderName' parameter."
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Filter out rows with data in columns that don't have a column header
|
||||
if ($DataOnly -and (-not $NoHeader)) {
|
||||
$Rows = $CellsWithValues.Start | where {$PropertyNames.Column -contains $_.Column} |
|
||||
Sort-Object Row -Unique | Select-Object -ExpandProperty Row
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Filter out the top row when it contains column headers
|
||||
if (-not ($NoHeader -or $HeaderName)) {
|
||||
$Rows = $Rows | where {$_ -gt $StartRow}
|
||||
}
|
||||
#endregion
|
||||
|
||||
Write-Debug $sw.Elapsed.TotalMilliseconds
|
||||
if (-not $Rows) {
|
||||
Write-Warning "Worksheet '$WorksheetName' in workbook '$Path' contains no data in the rows after top row '$StartRow'"
|
||||
}
|
||||
@@ -416,14 +381,15 @@ Function Import-Excel {
|
||||
$NewRow = [Ordered]@{}
|
||||
|
||||
foreach ($P in $PropertyNames) {
|
||||
Add-Property -Name $P.Value -Value $Worksheet.Cells[$R, $P.Column].Value
|
||||
$NewRow[$P.Value] = $Worksheet.Cells[$R, $P.Column].Value
|
||||
Write-Verbose "Import cell '$($Worksheet.Cells[$R, $P.Column].Address)' with property name '$($p.Value)' and value '$($Worksheet.Cells[$R, $P.Column].Value)'."
|
||||
}
|
||||
|
||||
[PSCustomObject]$NewRow
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Write-Debug $sw.Elapsed.TotalMilliseconds
|
||||
}
|
||||
Catch {
|
||||
throw "Failed importing the Excel workbook '$Path' with worksheet '$Worksheetname': $_"
|
||||
@@ -499,7 +465,7 @@ function ConvertFrom-ExcelSheet {
|
||||
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $stream
|
||||
$workbook = $xl.Workbook
|
||||
|
||||
$targetSheets = $workbook.Worksheets | Where {$_.Name -like $SheetName}
|
||||
$targetSheets = $workbook.Worksheets | Where-Object {$_.Name -like $SheetName}
|
||||
|
||||
$params = @{} + $PSBoundParameters
|
||||
$params.Remove("OutputPath")
|
||||
@@ -522,6 +488,7 @@ function ConvertFrom-ExcelSheet {
|
||||
}
|
||||
|
||||
function Export-MultipleExcelSheets {
|
||||
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
$Path,
|
||||
|
||||
213
merge-worksheet.ps1
Normal file
213
merge-worksheet.ps1
Normal file
@@ -0,0 +1,213 @@
|
||||
Function Merge-Worksheet {
|
||||
<#
|
||||
.Synopsis
|
||||
Merges two worksheets (or other objects) into a single worksheet with differences marked up.
|
||||
.Description
|
||||
The Compare-Worksheet command takes two worksheets and marks differences in the source document, and optionally outputs a grid showing the changes.
|
||||
By contrast the Merge-Worksheet command takes the worksheets and combines them into a single sheet showing the old and new data side by side .
|
||||
Although it is designed to work with Excel data it can work with arrays of any kind of object; so it can be a merge *of* worksheets, or a merge *to* worksheet.
|
||||
.Example
|
||||
merge-worksheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -OutputFile Services.xlsx -OutputSheetName 54-55 -show
|
||||
The workbooks contain audit information for two servers, one page contains a list of services. This command creates a worksheet named 54-55
|
||||
in a workbook named services and shows all the services and their differences, and opens it in Excel
|
||||
.Example
|
||||
merge-worksheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -OutputFile Services.xlsx -OutputSheetName 54-55 -HideEqual -AddBackgroundColor LightBlue -show
|
||||
This modifies the previous command to hide the equal rows in the output sheet and changes the color used to mark rows "Added" to the second file.
|
||||
.Example
|
||||
merge-worksheet -OutputFile .\j1.xlsx -OutputSheetName test11 -ReferenceObject (dir .\ImportExcel\4.0.7) -DifferenceObject (dir '\ImportExcel\4.0.8') -Property Length -Show
|
||||
This version compares two directories, and marks what has changed.
|
||||
Because no "Key" property is given, "Name" is assumed to be the key and the only other property examined is length.
|
||||
Files which are added or deleted or have changedd size will be highlighed in the output sheet. Changes to dates or other attributes will be ignored
|
||||
.Example
|
||||
merge-worksheet -Outf .\dummy.xlsx -RefO (dir .\ImportExcel\4.0.7) -DiffO (dir .\ImportExcel\4.0.8') -Pr Length -WhatIf -Passthru | Out-GridView
|
||||
This time no file is written because -WhatIf is specified, and -Passthru causes the results to go Out-Gridview. This version uses aliases to shorten the parameters,
|
||||
(OutputFileName can be "outFile" and the sheet "OutSheet" : DifferenceObject & RefeenceObject can be DiffObject & RefObject)
|
||||
#>
|
||||
[cmdletbinding(SupportsShouldProcess=$true)]
|
||||
Param(
|
||||
#First Excel file to compare. You can compare two Excel files or two other objects but not one of each.
|
||||
[parameter(ParameterSetName='A',Mandatory=$true,Position=0)]
|
||||
[parameter(ParameterSetName='B',Mandatory=$true,Position=0)]
|
||||
[parameter(ParameterSetName='C',Mandatory=$true,Position=0)]
|
||||
$Referencefile ,
|
||||
|
||||
#Second Excel file to compare.
|
||||
[parameter(ParameterSetName='A',Mandatory=$true,Position=1)]
|
||||
[parameter(ParameterSetName='B',Mandatory=$true,Position=1)]
|
||||
[parameter(ParameterSetName='C',Mandatory=$true,Position=1)]
|
||||
$Differencefile ,
|
||||
|
||||
#Name(s) of worksheets to compare,
|
||||
[parameter(ParameterSetName='A',Position=2)]
|
||||
[parameter(ParameterSetName='B',Position=2)]
|
||||
[parameter(ParameterSetName='C',Position=2)]
|
||||
$WorkSheetName = "Sheet1",
|
||||
|
||||
#The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
|
||||
[parameter(ParameterSetName='A')]
|
||||
[parameter(ParameterSetName='B')]
|
||||
[parameter(ParameterSetName='C')]
|
||||
[int]$Startrow = 1,
|
||||
|
||||
#Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
|
||||
[Parameter(ParameterSetName='B',Mandatory=$true)]
|
||||
[String[]]$Headername,
|
||||
|
||||
#Automatically generate property names (P1, P2, P3, ..) instead of the using the values the top row of the sheet.
|
||||
[Parameter(ParameterSetName='C',Mandatory=$true)]
|
||||
[switch]$NoHeader,
|
||||
|
||||
[parameter(ParameterSetName='D',Mandatory=$true)]
|
||||
[Alias('RefObject')]
|
||||
$ReferenceObject ,
|
||||
[parameter(ParameterSetName='D',Mandatory=$true)]
|
||||
[Alias('DiffObject')]
|
||||
$DifferenceObject ,
|
||||
|
||||
#File to hold merged data.
|
||||
[parameter(Mandatory=$true,Position=3)]
|
||||
[Alias('OutFile')]
|
||||
$OutputFile ,
|
||||
#Name of worksheet to output - if none specified will use the reference worksheet name.
|
||||
[Alias('OutSheet')]
|
||||
$OutputSheetName = "Sheet1",
|
||||
#Properties to include in the DIFF - supports wildcards, default is "*".
|
||||
$Property = "*" ,
|
||||
#Properties to exclude from the the search - supports wildcards.
|
||||
$ExcludeProperty ,
|
||||
#Name of a column which is unique used to pair up rows from the refence and difference side, default is "Name".
|
||||
$Key = "Name" ,
|
||||
#Sets the font color for the "key" field; this means you can filter by color to get only changed rows.
|
||||
[System.Drawing.Color]$KeyFontColor = "DarkRed",
|
||||
#Sets the background color for changed rows.
|
||||
[System.Drawing.Color]$ChangeBackgroundColor = "Orange",
|
||||
#Sets the background color for rows in the reference but deleted from the difference sheet.
|
||||
[System.Drawing.Color]$DeleteBackgroundColor = "LightPink",
|
||||
#Sets the background color for rows not in the reference but added to the difference sheet.
|
||||
[System.Drawing.Color]$AddBackgroundColor = "PaleGreen",
|
||||
#if Specified hides the rows in the spreadsheet that are equal and only shows changes, added or deleted rows.
|
||||
[switch]$HideEqual ,
|
||||
#If specified outputs the data to the pipeline (you can add -whatif so it the command only outputs to the command)
|
||||
[switch]$Passthru ,
|
||||
#If specified, opens the output workbook.
|
||||
[Switch]$Show
|
||||
)
|
||||
|
||||
#region Read Excel data
|
||||
if ($Referencefile -and $Differencefile) {
|
||||
#if the filenames don't resolve, give up now.
|
||||
try { $oneFile = ((Resolve-Path -Path $Referencefile -ErrorAction Stop).path -eq (Resolve-Path -Path $Differencefile -ErrorAction Stop).path)}
|
||||
Catch { Write-Warning -Message "Could not Resolve the filenames." ; return }
|
||||
|
||||
#If we have one file , we must have two different worksheet names. If we have two files worksheet can be a single string or two strings.
|
||||
if ($onefile -and ( ($WorkSheetName.count -ne 2) -or $WorkSheetName[0] -eq $WorkSheetName[1] ) ) {
|
||||
Write-Warning -Message "If both the Reference and difference file are the same then worksheet name must provide 2 different names"
|
||||
return
|
||||
}
|
||||
if ($WorkSheetName.count -eq 2) {$worksheet1 = $WorkSheetName[0] ; $WorkSheet2 = $WorkSheetName[1]}
|
||||
elseif ($WorkSheetName -is [string]) {$worksheet1 = $WorkSheet2 = $WorkSheetName}
|
||||
else {Write-Warning -Message "You must provide either a single worksheet name or two names." ; return }
|
||||
|
||||
$params= @{ ErrorAction = [System.Management.Automation.ActionPreference]::Stop }
|
||||
foreach ($p in @("HeaderName","NoHeader","StartRow")) {if ($PSBoundParameters[$p]) {$params[$p] = $PSBoundParameters[$p]}}
|
||||
try {
|
||||
$ReferenceObject = Import-Excel -Path $Referencefile -WorksheetName $WorkSheet1 @params
|
||||
$DifferenceObject = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 @Params
|
||||
}
|
||||
Catch {Write-Warning -Message "Could not read the worksheet from $Referencefile and/or $Differencefile." ; return }
|
||||
$firstDataRow = + 1 ;
|
||||
if ($NoHeader) {$firstDataRow = $Startrow } else {$firstDataRow = $Startrow + 1}
|
||||
}
|
||||
else {$firstDataRow = 1 }
|
||||
#endregion
|
||||
|
||||
#region Set lists of properties and row numbers
|
||||
#Make a list of properties/headings using the Property (default "*") and ExcludeProperty parameters
|
||||
$propList = @()
|
||||
$headings = $ReferenceObject[-1].psobject.Properties.Name # This preserves the sequence - using get-member would sort them alphabetically!
|
||||
if ($NoHeader -and "Name" -eq $Key) {$Key = "p1"}
|
||||
if ($headings -notcontains $Key) {Write-Warning -Message "You need to specify one of the headings in the sheet '$worksheet1' as a key." ; return }
|
||||
foreach ($p in $Property) { $propList += ($headings.where({$_ -like $p}) )}
|
||||
foreach ($p in $ExcludeProperty) { $propList = $propList.where({$_ -notlike $p}) }
|
||||
if ($propList -notcontains $Key) { $propList += $Key} #If $key isn't one of the headings we will have bailed by now
|
||||
$propList = $propList | Select-Object -Unique #so, prolist must contain at least $key if nothing else
|
||||
#Build the list of the properties to output, in order.
|
||||
$diffpart = @()
|
||||
$refpart = @()
|
||||
foreach ($p in $proplist.Where({$key -ne $_}) ) {$refPart += "<=$p" ; $diffPart += "=>$p" }
|
||||
$outputProps = @($key) + $refpart + $diffpart
|
||||
#Key will go in column A, last reference column will be A if there is one property, B if there are two, C if theere are 3 etc
|
||||
$lastRefCol = [char](64 + $propList.count)
|
||||
#First difference column will be the next one (we'll trap the case of only having the key later)
|
||||
$FirstDiffCol = [char](65 + $propList.count)
|
||||
#Last difference column will be A if there is one property, C if there are two, E if there are 3
|
||||
$lastDiffCol = [char](64 + 2 * $propList.count)
|
||||
|
||||
#Add RowNumber to every row
|
||||
#If one sheet has extra rows we can get a single "==" result from compare, but with the row from the reference sheet
|
||||
#but the row in the other sheet might so we will look up the row number from the key field build a hash table for that
|
||||
|
||||
$Rowhash = @{}
|
||||
$i = $firstDataRow ; foreach ($row in $ReferenceObject) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++) -Force}
|
||||
$i = $firstDataRow ; foreach ($row in $DifferenceObject) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++) -Force
|
||||
$Rowhash[$row.$key] = $row._row
|
||||
}
|
||||
#endregion
|
||||
|
||||
$expandedDiff = Compare-Object -ReferenceObject $ReferenceObject -DifferenceObject $DifferenceObject -Property $propList -PassThru -IncludeEqual |
|
||||
Group-Object -Property $key | ForEach-Object {
|
||||
#The value of the key column is the name of the group.
|
||||
$keyval = $_.name
|
||||
#we're going to create a custom object from a hash table. We want the fields to be ordered
|
||||
$hash = [ordered]@{}
|
||||
foreach ($result in $_.Group) {
|
||||
if ($result.SideIndicator -ne "=>") {$hash["<Row"] = $result._Row }
|
||||
elseif (-not $hash["<Row"]) {$hash["<Row"] = "" }
|
||||
#if we have already set the side, this is the second record, so set side to indicate "changed"
|
||||
if ($hash.Side) {$hash.Side = "<>"} else {$hash["Side"] = $result.SideIndicator}
|
||||
#if result is "in reference" and we don't have a matching "in difference" (meaning a change) the lookup will be blank. Which we want.
|
||||
$hash[">Row"] = $Rowhash[$keyval]
|
||||
$hash[$key] = $keyval
|
||||
#For all the other fields we care about, create <=FieldName and/or =>FieldName
|
||||
foreach ($p in $propList.Where({$_ -ne $key})) {
|
||||
if ($result.SideIndicator -eq "==") {$hash[("=>$P")] = $hash[("<=$P")] =$result.$P}
|
||||
else {$hash[($result.SideIndicator+$P)] =$result.$P}
|
||||
}
|
||||
}
|
||||
[Pscustomobject]$hash
|
||||
} | Sort-Object -Property "<row"
|
||||
|
||||
#Already sorted by reference row number, and fill in any blanks in the differene-row column
|
||||
for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i].">row") {$expandedDiff[$i].">row" = $expandedDiff[$i-1].">row" } }
|
||||
#Sort by difference row number, and fill in any blanks in the reference-row column
|
||||
$expandedDiff = $expandedDiff | Sort-Object -Property ">row"
|
||||
for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i]."<row") {$expandedDiff[$i]."<row" = $expandedDiff[$i-1]."<row" } }
|
||||
if ($PSCmdlet.ShouldProcess($OutputFile,"Write Output to Excel file")) {
|
||||
$xl = $expandedDiff | Select-Object -Property $outputProps |
|
||||
Export-Excel -Path $OutputFile -WorkSheetname $OutputSheetName -FreezeTopRow -BoldTopRow -AutoSize -AutoFilter -PassThru
|
||||
$ws = $xl.Workbook.Worksheets[$OutputSheetName]
|
||||
for ($i = 0; $i -lt $expandedDiff.Count; $i++ ) {
|
||||
if ( $expandedDiff[$i].side -ne "==" ) {
|
||||
Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -FontColor $KeyFontColor
|
||||
}
|
||||
elseif ( $HideEqual ) {$ws.row($i+2).hidden = $true }
|
||||
if ( $expandedDiff[$i].side -eq "<>" ) {
|
||||
$range = $ws.Dimension -replace "\d+", ($i + 2 )
|
||||
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $ChangeBackgroundColor
|
||||
}
|
||||
elseif ( $expandedDiff[$i].side -eq "<=" ) {
|
||||
$range = "A" + ($i + 2 ) + ":" + $lastRefCol + ($i + 2 )
|
||||
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $DeleteBackgroundColor
|
||||
}
|
||||
elseif ( $expandedDiff[$i].side -eq "=>" ) {
|
||||
if ($propList.count -gt 1) {
|
||||
$range = $FirstDiffCol + ($i + 2 ) + ":" + $lastDiffCol + ($i + 2 )
|
||||
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $AddBackgroundColor
|
||||
}
|
||||
Set-Format -WorkSheet $ws -Range ("A" + ($i + 2 )) -BackgroundColor $AddBackgroundColor
|
||||
}
|
||||
}
|
||||
Close-ExcelPackage -ExcelPackage $xl -Show:$Show
|
||||
}
|
||||
if ($PassThru) {$expandedDiff}
|
||||
}
|
||||
Reference in New Issue
Block a user