Compare commits

..

90 Commits
4.11 ... v5.0.0

Author SHA1 Message Date
dfinke
5e7221062d Updated 2018-06-08 17:15:28 -04:00
dfinke
bf63c27d0a bumped version 2018-06-08 17:15:22 -04:00
dfinke
3ce9018d3c updated 2018-06-08 16:55:08 -04:00
Doug Finke
b026e1b23c Merge pull request #337 from jhoneill/master
Perf for import, Merge, Minor tidying up
2018-06-08 16:53:36 -04:00
dfinke
1b8be2c605 adde databar to Examples 2018-06-08 12:08:27 -04:00
dfinke
a018df09eb Fix databar example 2018-06-08 12:08:16 -04:00
dfinke
9b306458e4 increased how long the import should take 2018-06-08 11:17:22 -04:00
dfinke
ab6bc327e8 Renamed test directory 2018-06-08 11:06:07 -04:00
dfinke
45c96cd37a Added PSVersion. Point to the new directory for tests 2018-06-08 11:05:59 -04:00
jhoneill
e47e1d99c1 Added EndRow, StartColumn, EndColumn to Import-Excel
Start row already existed. 
Aliases allow you to specify -no header -top 10 -bottom 20 -left 6 -right 8 
Start row can be below end right row, and start (left) column can be to the right of the end column - this allows read in reverse order, but does generate a warning.
2018-06-08 10:43:57 +01:00
jhoneill
f2be21f955 Added Merge-MultipleSheets to argument completers 2018-05-30 00:35:26 +01:00
dfinke
711d253d1a added build badge 2018-05-29 18:39:57 -04:00
jhoneill
2a62dc9b45 Making Merge-Worksheet, and Merge-MultipleWorksheet ready to release 2018-05-29 17:36:15 +01:00
jhoneill
15f1839d29 Added Merge Multiple worksheet 2018-05-27 20:44:02 +01:00
jhoneill
e0f3171bba Revert "Added Multiple Merge to Merge-Worksheet.ps1"
This reverts commit dc9bff8240.
2018-05-27 20:37:40 +01:00
jhoneill
dc9bff8240 Added Multiple Merge to Merge-Worksheet.ps1 2018-05-27 20:30:07 +01:00
jhoneill
cd52f3c704 Added Merge-worksheet 2018-05-27 19:43:41 +01:00
dfinke
7a9fbaedb1 update 2018-05-27 13:46:14 -04:00
dfinke
4df916c9f5 Read a larger sheet 2018-05-27 13:44:29 -04:00
dfinke
36387c8f1a Up how long to import takes 2018-05-27 13:37:22 -04:00
dfinke
a9ae83b586 update .gitignore 2018-05-27 13:35:15 -04:00
dfinke
64f55c8ba4 chk # of files 2018-05-27 13:32:14 -04:00
dfinke
817ed117a8 test dir 2018-05-27 13:30:17 -04:00
dfinke
db98f8482d test exists 2018-05-27 13:29:00 -04:00
dfinke
767d75d0c5 try 2018-05-27 13:27:15 -04:00
dfinke
cd9a5c0122 try ls 2018-05-27 13:21:05 -04:00
dfinke
0266aab415 quote it 2018-05-27 13:18:34 -04:00
dfinke
8b51ac4159 update name 2018-05-27 13:16:03 -04:00
dfinke
a10d59cbec add ipmo 2018-05-27 13:14:07 -04:00
dfinke
4bf1020431 test for export-excel 2018-05-27 13:13:01 -04:00
dfinke
8906d7dbf4 chk file exists 2018-05-27 13:09:24 -04:00
dfinke
ba06c4620f moved out of BeforeAll 2018-05-27 13:04:37 -04:00
dfinke
2683ec985f save file with BOM 2018-05-27 12:56:46 -04:00
dfinke
e09f2b5563 Force install of pester 2018-05-27 12:54:44 -04:00
dfinke
8e69b66e82 Checks version of pester on appveyor 2018-05-27 12:53:26 -04:00
dfinke
b436b61a8f First stesp to wire up appveyor 2018-05-27 12:45:57 -04:00
jhoneill
39a68e71c4 Tidying of case, parameter clarity, removal of aliasess.
Added timeout to send-SqlDataToExcel
Added Merge WorkSheet
Fixed bugs in Compare-Worksheet
2018-05-26 22:24:36 +01:00
dfinke
893b2f3214 tightened up the example 2018-05-25 16:32:33 -04:00
dfinke
3a4b0267e7 tweaked 2018-05-19 13:23:00 -04:00
dfinke
2728d21ffb Generates random data 2018-05-19 13:22:15 -04:00
jhoneill
3f28fa5ebe Merge remote-tracking branch 'upstream/master' 2018-05-14 14:24:49 +01:00
jhoneill
807990c4ba Fixed an error in Compare-Worksheet when only 1 row is different 2018-05-14 14:21:49 +01:00
jhoneill
bf8e8ed6bf Added Merge-Workshet. Made perf improvements to Import-Excel 2018-05-14 11:38:23 +01:00
jhoneill
6a53d3ddc9 Minor tidying. Making case consistent, and various things analyzer friendly; added Timeout to Send-SQL..., 2018-05-13 17:35:31 +01:00
Doug Finke
9c628c49be Merge pull request #328 from stahler/patch-1
Some minor spelling changes to the awesome help.
2018-05-09 22:01:34 -04:00
Wes Stahler
152627298a Some minor spelling changes to the awesome help. 2018-05-09 20:50:56 -04:00
jhoneill
b8cc7f163f Merge remote-tracking branch 'upstream/master' 2018-05-04 14:59:09 +01:00
jhoneill
3f1ea599e3 Merge remote-tracking branch 'dfinke/master' 2018-05-04 14:55:36 +01:00
dfinke
5d2520d8e3 Updated 2018-05-03 14:07:05 -04:00
dfinke
d99dd3bbae Bump version and update install module 2018-05-03 14:02:01 -04:00
jhoneill
1e0720f57b fat fingers ! 2018-05-03 14:01:29 -04:00
jhoneill
61fc2b24b7 Color completion wasn't working and reworked gridview for compare 2018-05-03 14:01:29 -04:00
jhoneill
21108f5136 fat fingers ! 2018-05-03 17:12:20 +01:00
jhoneill
6dd928097c One last bug in compare, and fixed bug #310 in Set-Format 2018-05-03 11:30:19 -04:00
jhoneill
8f2dd982c5 Lots of changes to the compare-worksheet module 2018-05-03 11:30:19 -04:00
jhoneill
904145ebae Default parameter set was missing 2018-05-03 11:30:19 -04:00
jhoneill
47a61f5eb3 Added paramters to Compare worksheet
Now has -Gridview, and supports startrow, headernames and NoHeader (as
per import Excel) and ensures the headers don't clash.
2018-05-03 11:30:19 -04:00
jhoneill
f703addeb1 Added Compare worksheet
Added Compate-worksheet function (in its own PS1) Updated
ColorCompletion.ps1 to hold argument completers for set row, and set
column, and removed  duplicates from formatting.ps1
Fixed case of Path param in Open-ExcelPackage
Added comments about date format (m/d/yy is trapped and translated to
local date)
2018-05-03 11:30:19 -04:00
jhoneill
a022f0ae1b Color completion wasn't working and reworked gridview for compare 2018-05-03 15:46:30 +01:00
dfinke
cff83eb692 added 2018-05-02 15:39:37 -04:00
dfinke
f3197fe076 Added 2018-05-02 15:28:50 -04:00
jhoneill
a50363e55f One last bug in compare, and fixed bug #310 in Set-Format 2018-05-02 17:03:13 +01:00
dfinke
7c2943baa1 Added link to gallery 2018-05-02 10:12:06 -04:00
jhoneill
1e115d5ede Lots of changes to the compare-worksheet module 2018-05-02 12:38:13 +01:00
dfinke
ffbc6b46dc tweaked 2018-05-01 17:10:42 -04:00
dfinke
d082ca207c Changed border color 2018-05-01 17:09:52 -04:00
jhoneill
31573ee803 Default parameter set was missing 2018-04-27 15:57:12 +01:00
jhoneill
9632664c2c Added paramters to Compare worksheet
Now has -Gridview, and supports startrow, headernames and NoHeader (as
per import Excel) and ensures the headers don't clash.
2018-04-27 10:33:48 +01:00
jhoneill
da7a70752c Added Compare worksheet
Added Compate-worksheet function (in its own PS1) Updated
ColorCompletion.ps1 to hold argument completers for set row, and set
column, and removed  duplicates from formatting.ps1
Fixed case of Path param in Open-ExcelPackage
Added comments about date format (m/d/yy is trapped and translated to
local date)
2018-04-26 16:19:59 +01:00
jhoneill
f8884a64fd Merge remote-tracking branch 'refs/remotes/dfinke/master' 2018-04-23 22:13:19 +01:00
dfinke
58ab93a6eb updated readme 2018-04-22 14:25:56 -04:00
dfinke
94d86927ba Expand aliases 2018-04-22 14:19:58 -04:00
dfinke
d31a262f37 Remove commented code 2018-04-22 14:19:47 -04:00
dfinke
2e8c69ea6b added tests 2018-04-22 14:16:17 -04:00
Doug Finke
9261b49b56 Merge pull request #318 from nzubair/EmptyStringToNull
Replace empty string with NULL when generating SQL inserts. Fixes #288
2018-04-22 14:16:09 -04:00
Nasir Zubair
bc80134560 Add help and change HeaderRow parameter with StartRow including aliases, for consistency with Import-Excel function. 2018-04-20 23:31:17 -04:00
Nasir Zubair
6dfa5b1aac Add ConvertEmptyStringsToNull parameter, along with logic to replace an empty string with NULL if the parameter is present. 2018-04-20 22:59:39 -04:00
dfinke
6f921e1a3d updated 2018-04-16 15:23:12 -04:00
dfinke
feb5868952 bumped version 2018-04-16 15:23:12 -04:00
Doug Finke
f48e4ad26c Merge pull request #316 from ili101/PSPlot
Fixes `PSPlot` OutputType
2018-04-16 15:17:57 -04:00
Ili Metuky
33d86cb3c9 Fixed the bug "Unable to find type [PSPlot]"
Also added a test for it in ImportExcel.Tests.ps1

The previous fix was broke again by commit:
bd40cfe829 (diff-f50d26003c0baf98f417a44fc1cac92b)
2018-04-16 20:19:10 +03:00
ili101
4753568a39 Merge pull request #1 from dfinke/master
Update
2018-04-16 20:01:59 +03:00
Doug Finke
af31bab499 Merge pull request #313 from PashoQ/master
Updated Install.ps1 to install module for PowerShell V4
2018-04-16 12:07:42 -04:00
Pavlo Yalandin
3d76bec6c4 Updated Install.ps1 to install all needed scripts as part of module for PowerShell v4 2018-04-11 11:52:32 +03:00
dfinke
5dd73789a3 Updated 2018-04-10 19:04:55 -04:00
dfinke
85a78dad7e Updated readme 2018-04-10 19:02:56 -04:00
dfinke
13652bc4ed bumped version 2018-04-10 19:02:56 -04:00
Doug Finke
0540d221e0 Merge pull request #312 from JustinGrote/feature-ReZipParameter
Add ReZip Parameter for MSOnline compatibility (fixes #240)
2018-04-10 18:55:18 -04:00
JustinGrote
243ba0bb3c Add ReZip Parameter for MSOnline compatability 2018-04-09 14:35:28 -07:00
Doug Finke
b5177de50d updated 2018-04-07 19:34:47 -04:00
45 changed files with 1414 additions and 390 deletions

1
.gitignore vendored
View File

@@ -55,4 +55,3 @@ test.xlsx
testCCFMT.ps1
testHide.ps1
ImportExcel.zip
*.xlsx

View File

@@ -85,11 +85,12 @@
[switch]$StrikeThru
)
#Allow add conditional formatting to work like Set-Format (with single ADDRESS parameter) split it to get worksheet and Range of cells.
If ($Address -and -not $WorkSheet -and -not $Range) {
if ($Address -and -not $WorkSheet -and -not $Range) {
$WorkSheet = $Address.Worksheet[0]
$Range = $Address.Address
}
If ($ThreeIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)}
}
if ($rule -eq "Databar" -and -not $databarColor) {Write-Warning -Message "-DatabarColor must be specified for the Databar rule type" }
if ( $ThreeIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddThreeIconSet($Range , $ThreeIconsSet)}
elseif ($FourIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFourIconSet( $Range , $FourIconsSet) }
elseif ($FiveIconsSet) {$rule = $WorkSheet.ConditionalFormatting.AddFiveIconSet( $Range , $IconType) }
elseif ($DataBarColor) {$rule = $WorkSheet.ConditionalFormatting.AddDatabar( $Range , $DataBarColor) }

View File

@@ -1,4 +1,4 @@
Function ColorCompletion {
Function ColorCompletion {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)
[System.Drawing.KnownColor].GetFields() | Where-Object {$_.IsStatic -and $_.name -like "$wordToComplete*" } |
Sort-Object name | ForEach-Object {New-CompletionResult $_.name $_.name
@@ -6,11 +6,29 @@ Function ColorCompletion {
}
if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue) {
Register-ArgumentCompleter -CommandName Export-Excel -ParameterName TitleBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName DataBarColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Export-Excel -ParameterName TitleBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName DataBarColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Compare-Worksheet -ParameterName AllDataBackgroundColor -ScriptBlock $Function:ColorCompletion
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-MulipleSheets -ParameterName KeyFontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-MulipleSheets -ParameterName AddBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-MulipleSheets -ParameterName ChangeBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-MulipleSheets ` -ParameterName DeleteBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Merge-MulipleSheets -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
Register-ArgumentCompleter -CommandName Set-Column -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Column -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Column -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Row -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Row -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Row -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion
}

View File

@@ -1,4 +1,93 @@
function ConvertFrom-ExcelToSQLInsert {
<#
.SYNOPSIS
Generate SQL insert statements from Excel spreadsheet.
.DESCRIPTION
Generate SQL insert statements from Excel spreadsheet.
.PARAMETER TableName
Name of the target database table.
.PARAMETER Path
Path to an existing .XLSX file
This parameter is passed to Import-Excel as is.
.PARAMETER WorkSheetname
Specifies the name of the worksheet in the Excel workbook to import. By default, if no name is provided, the first worksheet will be imported.
This parameter is passed to Import-Excel as is.
.PARAMETER StartRow
The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
When the parameters -NoHeader and -HeaderName are not provided, this row will contain the column headers that will be used as property names. When one of both parameters are provided, the property names are automatically created and this row will be treated as a regular row containing data.
.PARAMETER Header
Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
In case you provide less header names than there is data in the worksheet, then only the data with a corresponding header name will be imported and the data without header name will be disregarded.
In case you provide more header names than there is data in the worksheet, then all data will be imported and all objects will have all the property names you defined in the header names. As such, the last properties will be blanc as there is no data for them.
.PARAMETER NoHeader
Automatically generate property names (P1, P2, P3, ..) instead of the ones defined in the column headers of the TopRow.
This switch is best used when you want to import the complete worksheet as is and are not concerned with the property names.
.PARAMETER DataOnly
Import only rows and columns that contain data, empty rows and empty columns are not imported.
.PARAMETER ConvertEmptyStringsToNull
If specified, cells without any data are replaced with NULL, instead of an empty string.
This is to address behviors in certain DBMS where an empty string is insert as 0 for INT column, instead of a NULL value.
.EXAMPLE
Generate SQL insert statements from Movies.xlsx file, leaving blank cells as empty strings:
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Sheet1 |
----------------------------------------------------------
| A B C |
|1 Movie Name Year Rating |
|2 The Bodyguard 1992 9 |
|3 The Matrix 1999 8 |
|4 Skyfall 2012 9 |
|5 The Avengers 2012 |
----------------------------------------------------------
PS C:\> Import-Excel -TableName "Movies" -Path 'C:\Movies.xlsx'
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Bodyguard', '1992', '9');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Matrix', '1999', '8');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('Skyfall', '2012', '9');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Avengers', '2012', '');
.EXAMPLE
Generate SQL insert statements from Movies.xlsx file, specify NULL instead of an empty string.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Sheet1 |
----------------------------------------------------------
| A B C |
|1 Movie Name Year Rating |
|2 The Bodyguard 1992 9 |
|3 The Matrix 1999 8 |
|4 Skyfall 2012 9 |
|5 The Avengers 2012 |
----------------------------------------------------------
PS C:\> ConvertFrom-ExcelToSQLInsert -TableName "Movies" -Path "C:\Movies.xlsx" -ConvertEmptyStringsToNull
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Bodyguard', '1992', '9');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Matrix', '1999', '8');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('Skyfall', '2012', '9');
INSERT INTO Movies ('Movie Name', 'Year', 'Rating') Values('The Avengers', '2012', NULL);
.NOTES
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
$TableName,
@@ -8,39 +97,34 @@ function ConvertFrom-ExcelToSQLInsert {
$Path,
[Alias("Sheet")]
$WorkSheetname = 1,
[int]$HeaderRow = 1,
[Alias('HeaderRow', 'TopRow')]
[ValidateRange(1, 9999)]
[Int]$StartRow,
[string[]]$Header,
[switch]$NoHeader,
[switch]$DataOnly
[switch]$DataOnly,
[switch]$ConvertEmptyStringsToNull
)
$null = $PSBoundParameters.Remove('TableName')
$null = $PSBoundParameters.Remove('ConvertEmptyStringsToNull')
$params = @{} + $PSBoundParameters
ConvertFrom-ExcelData @params {
param($propertyNames, $record)
$ColumnNames = "'" + ($PropertyNames -join "', '") + "'"
$values = foreach ($propertyName in $PropertyNames) { $record.$propertyName }
$targetValues = "'" + ($values -join "', '") + "'"
$values = foreach ($propertyName in $PropertyNames) {
if ($ConvertEmptyStringsToNull.IsPresent -and [string]::IsNullOrEmpty($record.$propertyName)) {
'NULL'
}
else {
"'" + $record.$propertyName + "'"
}
}
$targetValues = ($values -join ", ")
"INSERT INTO {0} ({1}) Values({2});" -f $TableName, $ColumnNames, $targetValues
}
# $data = Import-Excel @params
# $PropertyNames = $data[0].psobject.Properties |
# Where-Object {$_.membertype -match 'property'} |
# Select-Object -ExpandProperty name
# $ColumnNames = "'" + ($PropertyNames -join "', '") + "'"
# foreach ($record in $data) {
# $values = $(foreach ($propertyName in $PropertyNames) {
# $record.$propertyName
# })
# $targetValues = "'" + ($values -join "', '") + "'"
# "INSERT INTO {0} ({1}) Values({2});" -f $TableName, $ColumnNames, $targetValues
# }
}

View File

@@ -0,0 +1,34 @@
Import-Module .\ImportExcel.psd1 -Force
$xlFile = ".\testSQL.xlsx"
Describe "ConvertFrom-ExcelToSQLInsert" {
BeforeEach {
$([PSCustomObject]@{
Name="John"
Age=$null
}) | Export-Excel $xlFile
}
AfterAll {
Remove-Item $xlFile -Recurse -Force -ErrorAction Ignore
}
It "Should be empty double single quotes" {
$expected="INSERT INTO Sheet1 ('Name', 'Age') Values('John', '');"
$actual = ConvertFrom-ExcelToSQLInsert -Path $xlFile Sheet1
$actual | should be $expected
}
It "Should have NULL" {
$expected="INSERT INTO Sheet1 ('Name', 'Age') Values('John', NULL);"
$actual = ConvertFrom-ExcelToSQLInsert -Path $xlFile Sheet1 -ConvertEmptyStringsToNull
$actual | should be $expected
}
}

7
DoTests.ps1 Normal file
View File

@@ -0,0 +1,7 @@
$PSVersionTable.PSVersion
if ((Get-Module -ListAvailable pester) -eq $null) {
Install-Module -Name Pester -Repository PSGallery -Force
}
Invoke-Pester -Script $PSScriptRoot\__tests__

BIN
Examples/Charts/Tools.xlsx Normal file

Binary file not shown.

View File

@@ -0,0 +1,21 @@
Remove-Item -Path .\test.xlsx -ErrorAction Ignore
$excel = Get-Process |
Select-Object -Property Name,Company,Handles,CPU,PM,NPM,WS |
Export-Excel -Path .\test.xlsx -ClearSheet -WorkSheetname "Processes" -PassThru
$sheet = $excel.Workbook.Worksheets["Processes"]
$sheet.Column(1) | Set-Format -Bold -AutoFit
$sheet.Column(2) | Set-Format -Width 29 -WrapText
$sheet.Column(3) | Set-Format -HorizontalAlignment Right -NFormat "#,###"
Set-Format -Address $sheet.Cells["E1:H1048576"] -HorizontalAlignment Right -NFormat "#,###"
Set-Format -Address $sheet.Column(4) -HorizontalAlignment Right -NFormat "#,##0.0" -Bold
Set-Format -Address $sheet.Row(1) -Bold -HorizontalAlignment Center
Add-ConditionalFormatting -WorkSheet $sheet -Range "D2:D1048576" -DataBarColor Red
Add-ConditionalFormatting -WorkSheet $sheet -Range "G2:G1048576" -RuleType GreaterThan -ConditionValue "104857600" -ForeGroundColor Red
foreach ($c in 5..9) {Set-Format -Address $sheet.Column($c) -AutoFit }
Export-Excel -ExcelPackage $excel -WorkSheetname "Processes" -IncludePivotChart -ChartType ColumnClustered -NoLegend -PivotRows company -PivotData @{'Name'='Count'} -Show

View File

@@ -42,7 +42,7 @@ Set-Format -Address $sheet1.Cells["E:E"] -Width 12
Set-Format -Address $sheet1.Cells["I:I"] -Width 12
$BorderBottom = "Thick"
$BorderColor = "LightBlue"
$BorderColor = "Black"
Set-Format -Address $sheet1.Cells["A2"] -BorderBottom $BorderBottom -BorderColor $BorderColor

Binary file not shown.

BIN
Examples/Fibonacci/fib.xlsx Normal file

Binary file not shown.

View File

@@ -0,0 +1,38 @@
if(!(gcm ig -ErrorAction SilentlyContinue)) {
"Use ``Install-Module NameIT`` to get the needed module from the gallery to support running this script"
return
}
$sign=@{sign=echo + -}
$location=@{location=echo Atlanta Newark Washington Chicago Philadelphia Houston Phoneix}
$(1..6 | % {
$from=$to=""
while($from -eq $to) {
$from=ig "[location]" -CustomData $location
$to=ig "[location]" -CustomData $location
}
[double]$a=ig "########"
[double]$b=ig ".####"
[double]$c=ig "#######"
[double]$d=ig "[sign].##" -CustomData $sign
[double]$e=ig "###"
[double]$f=ig "[sign]##" -CustomData $sign
#"{0},{1},{2},{3},{4},{5},{6},{7}" -f $from, $to, $a, $b, $c, $d, $e, $f
[PSCustomObject][Ordered]@{
From=$from
To=$to
RDollars=$a
RPercent=$b
MDollars=$c
MPercent=$d
Revenue=$e
Margin=$f
}
} | ConvertTo-Csv -NoTypeInformation) -replace '"','' # | Export-Excel

View File

@@ -1,9 +1,9 @@
ColumnChart -Title "Central Limit Theorem" ($(
for ($i = 1; $i -le 500; $i++) {
$s=0
for ($j = 1; $j -le 100; $j++){
$s+=Get-Random -Minimum 0 -Maximum 2
ColumnChart -Title "Central Limit Theorem" -NoLegend ($(
for ($i = 1; $i -le 500; $i++) {
$s = 0
for ($j = 1; $j -le 100; $j++) {
$s += Get-Random -Minimum 0 -Maximum 2
}
$s
}
$s
}
) | Sort | Group | select Count, Name)
) | Sort-Object | Group-Object | Select-Object Count, Name)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -3,7 +3,6 @@
$xlFile=".\testPivot.xlsx"
Remove-Item $xlFile -ErrorAction Ignore
$data =@"
Region,Area,Product,Units,Cost
North,A1,Apple,100,.5
@@ -19,5 +18,4 @@ $data |
-AutoSize -AutoFilter `
-IncludePivotTable `
-PivotRows Product `
-PivotData @{"Units"="sum"} `
-PivotFilter Region, Area `
-PivotData @{"Units"="sum"} -PivotFilter Region, Area

View File

@@ -1,4 +1,4 @@
 ipmo C:\Users\mcp\Documents\GitHub\ImportExcel\ImportExcel.psd1 -Force -Verbose
 Import-Module -name ImportExcel -Force -Verbose
$sql = @"
SELECT rootfile.baseName , rootfile.extension , Image.fileWidth AS width , image.fileHeight AS height ,

View File

@@ -1,6 +1,6 @@
# To ship, is to choose
ipmo .\ImportExcel.psd1 -Force
#ipmo .\ImportExcel.psd1 -Force
$pt=[ordered]@{}

View File

@@ -18,7 +18,7 @@
.PARAMETER TargetData
Data to insert onto the worksheet - this is often provided from the pipeline.
.PARAMETER ExcludeProperty
Speficies properties which may exist in the target data but should not be placed on the worksheet
Specifies properties which may exist in the target data but should not be placed on the worksheet
.PARAMETER Title
Text of a title to be placed in Cell A1
.PARAMETER TitleBold
@@ -32,9 +32,9 @@
.PARAMETER IncludePivotTable
Adds a Pivot table using the data in the worksheet
.PARAMETER PivotRows
Name(s) columns from the spreadhseet which will prvoide the row name(s) in the pivot table
Name(s) columns from the spreadhseet which will provide the row name(s) in the pivot table
.PARAMETER PivotColumns
Name(s) columns from the spreadhseet which will prvoide the Column name(s) in the pivot table
Name(s) columns from the spreadhseet which will provide the Column name(s) in the pivot table
.PARAMETER PivotData
Hash table in the form ColumnName = Average|Count|CountNums|Max|Min|Product|None|StdDev|StdDevP|Sum|Var|VarP to provide the data in the Pivot table
.PARAMETER PivotTableDefinition,
@@ -62,13 +62,13 @@
.PARAMETER TableStyle
Selects the style for the named table - defaults to 'Medium6'
.PARAMETER ExcelChartDefinition
A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPecent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts
A hash table containing ChartType, Title, NoLegend, ShowCategory, ShowPercent, Yrange, Xrange and SeriesHeader for one or more [non-pivot] charts
.PARAMETER HideSheet
Name(s) of Sheet(s) to hide in the workbook
.PARAMETER KillExcel
Closes Excel - prevents errors writing to the file because Excel has it open
.PARAMETER AutoNameRange
Makes each column a named range
Makes each column a named range
.PARAMETER StartRow
Row to start adding data. 1 by default. Row 1 will contain the title if any. Then headers will appear (Unless -No header is specified) then the data appears
.PARAMETER StartColumn
@@ -299,6 +299,8 @@
It then uses the package object to apply formatting, and then saves the workbook and disposes of the object before loading the document in Excel.
.EXAMPLE
Remove-Item -Path .\test.xlsx -ErrorAction Ignore
$excel = Get-Process | Select-Object -Property Name,Company,Handles,CPU,PM,NPM,WS | Export-Excel -Path .\test.xlsx -ClearSheet -WorkSheetname "Processes" -PassThru
$sheet = $excel.Workbook.Worksheets["Processes"]
$sheet.Column(1) | Set-Format -Bold -AutoFit
@@ -309,7 +311,7 @@
Set-Format -Address $sheet.Row(1) -Bold -HorizontalAlignment Center
Add-ConditionalFormatting -WorkSheet $sheet -Range "D2:D1048576" -DataBarColor Red
Add-ConditionalFormatting -WorkSheet $sheet -Range "G2:G1048576" -RuleType GreaterThan -ConditionValue "104857600" -ForeGroundColor Red
foreach ($c in 5..9) {Set-Format $sheet.Column($c) -AutoFit }
foreach ($c in 5..9) {Set-Format -Address $sheet.Column($c) -AutoFit }
Export-Excel -ExcelPackage $excel -WorkSheetname "Processes" -IncludePivotChart -ChartType ColumnClustered -NoLegend -PivotRows company -PivotData @{'Name'='Count'} -Show
This a more sophisticated version of the previous example showing different ways of using Set-Format, and also adding conditional formatting.
@@ -400,7 +402,8 @@
# [Parameter(ParameterSetName = 'TableNow')]
[Switch]$Now,
[Switch]$ReturnRange,
[Switch]$NoTotalsInPivot
[Switch]$NoTotalsInPivot,
[Switch]$ReZip
)
Begin {
@@ -443,7 +446,7 @@
{$_ -is [DateTime]} {
#region Save a date with an international valid format
$TargetCell.Value = $_
$TargetCell.Style.Numberformat.Format = 'm/d/yy h:mm'
$TargetCell.Style.Numberformat.Format = 'm/d/yy h:mm' # This is not a custom format, but a preset recognized as date and localized.
Write-Verbose "Cell '$Row`:$ColumnIndex' header '$Name' add value '$_' as date"
break
#endregion
@@ -712,7 +715,7 @@
$tbl = $ws.Tables.Add($targetRange, $TableName)
$tbl.TableStyle = $TableStyle
}
$PivotTableStartCell = "A1"
if($PivotFilter) {$PivotTableStartCell = "A3"}
@@ -1000,7 +1003,26 @@
$ws.Dimension.Address
}
$pkg.Save()
if ($ReZip) {
write-verbose "Re-Zipping $($pkg.file) using .NET ZIP library"
$zipAssembly = "System.IO.Compression.Filesystem"
try {
Add-Type -assembly $zipAssembly -ErrorAction stop
} catch {
write-error "The -ReZip parameter requires .NET Framework 4.5 or later to be installed. Recommend to install Powershell v4+"
continue
}
$TempZipPath = Join-Path -path ([System.IO.Path]::GetTempPath()) -ChildPath ([System.IO.Path]::GetRandomFileName())
[io.compression.zipfile]::ExtractToDirectory($pkg.File,$TempZipPath) | Out-Null
Remove-Item $pkg.File -Force
[io.compression.zipfile]::CreateFromDirectory($TempZipPath,$pkg.File) | Out-Null
}
$pkg.Dispose()
if ($Show) {
@@ -1036,4 +1058,4 @@ function New-PivotTableDefinition {
$parameters.Remove('PivotTableName')
@{$PivotTableName = $parameters}
}
}

View File

@@ -19,7 +19,7 @@ function Export-ExcelSheet {
$xl = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Path
$workbook = $xl.Workbook
$targetSheets = $workbook.Worksheets | Where {$_.Name -Match $SheetName}
$targetSheets = $workbook.Worksheets | Where-Object {$_.Name -Match $SheetName}
$params = @{} + $PSBoundParameters
$params.Remove("OutputPath")

View File

@@ -14,7 +14,7 @@ Param (
#Path to the Excel file whose chars we will export.
$Path = "C:\Users\public\Documents\stats.xlsx",
#If specified, output file objects representing the image files.
[switch]$passthru,
[switch]$Passthru,
#Format to write - JPG by default
[ValidateSet("JPG","PNG","GIF")]
$OutputType = "JPG",
@@ -23,15 +23,14 @@ Param (
)
#if no output folder was specified, set destination to the folder where the Excel file came from
if (-not $Destination) {$Destination = Split-Path -Path $path -Parent }
if (-not $Destination) {$Destination = Split-Path -Path $Path -Parent }
#Call up Excel and tell it to open the file.
try { $excelApp = New-Object -ComObject "Excel.Application" }
catch { Write-Warning "Could not start Excel application - which usually means it is not installed." ; return }
try { $excelWorkBook = $excelApp.Workbooks.Open($path) }
catch { Write-Warning "Could not start Excel application - which usually means it is not installed." ; return }
try { $excelWorkBook = $excelApp.Workbooks.Open($Path) }
catch { Write-Warning -Message "Could not Open $Path." ; return }
#For each worksheet, for each chart, jump to the chart, create a filename of "WorksheetName_ChartTitle.jpg", and export the file.
foreach ($excelWorkSheet in $excelWorkBook.Worksheets) {
@@ -41,11 +40,12 @@ foreach ($excelWorkSheet in $excelWorkBook.Worksheets) {
$excelApp.Goto($excelchart.TopLeftCell,$true)
$imagePath = Join-Path -Path $Destination -ChildPath ($excelWorkSheet.Name + "_" + ($excelchart.Chart.ChartTitle.Text -split "\s\d\d:\d\d,")[0] + ".$OutputType")
if ( $excelchart.Chart.Export($imagePath, $OutputType, $false) ) { # Export returs true/false for success/failure
if ($passThru) {Get-Item -Path $imagePath } # when succesful return a file object (-passthru) or print a verbose message, write warning for any failures
if ($Passthru) {Get-Item -Path $imagePath } # when succesful return a file object (-Passthru) or print a verbose message, write warning for any failures
else {Write-Verbose -Message "Exported $imagePath"}
}
else {Write-Warning -Message "Failure exporting $imagePath" }
}
}
$excelApp.Quit()
$excelApp.DisplayAlerts = $false
$excelWorkBook.Close($false,$null,$null)
$excelApp.Quit()

View File

@@ -2070,4 +2070,15 @@ Context 'special cases' {
}
}
}
}
Context 'General Tests' {
in $TestDrive {
Describe 'Get Help' {
it 'New-Plot' {
#Get-Help : Unable to find type [PSPlot].
{Help New-Plot} | Should -Not -Throw
}
}
}
}

View File

@@ -4,7 +4,7 @@
RootModule = 'ImportExcel.psm1'
# Version number of this module.
ModuleVersion = '4.0.11'
ModuleVersion = '5.0.0'
# ID used to uniquely identify this module
GUID = '60dd4136-feff-401a-ba27-a84458c57ede'

View File

@@ -1,240 +1,246 @@
Add-Type -Path "$($PSScriptRoot)\EPPlus.dll"
#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
. $PSScriptRoot\AddConditionalFormatting.ps1
. $PSScriptRoot\Charting.ps1
. $PSScriptRoot\ColorCompletion.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
. $PSScriptRoot\ConvertExcelToImageFile.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) {
. $PSScriptRoot\Plot.ps1
if ($PSVersionTable.PSVersion.Major -ge 5) {
. $PSScriptRoot\Plot.ps1
Function New-Plot {
[OutputType([PSPlot])]
Param()
Function New-Plot {
Param()
[PSPlot]::new()
}
[PSPlot]::new()
}
}
else {
Write-Warning 'PowerShell 5 is required for plot.ps1'
Write-Warning 'PowerShell Excel is ready, except for that functionality'
}
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
Create custom objects from the rows in an Excel worksheet.
<#
.SYNOPSIS
Create custom objects from the rows in an Excel worksheet.
.DESCRIPTION
The Import-Excel cmdlet creates custom objects from the rows in an Excel worksheet. Each row represents one object. All of this is possible without installing Microsoft Excel and by using the .NET library EPPLus.dll.
.DESCRIPTION
The Import-Excel cmdlet creates custom objects from the rows in an Excel worksheet. Each row represents one object. All of this is possible without installing Microsoft Excel and by using the .NET library EPPLus.dll.
By default, the property names of the objects are retrieved from the column headers. Because an object cannot have a blanc property name, only columns with column headers will be imported.
By default, the property names of the objects are retrieved from the column headers. Because an object cannot have a blanc property name, only columns with column headers will be imported.
If the default behavior is not desired and you want to import the complete worksheet as is, the parameter -NoHeader can be used. In case you want to provide your own property names, you can use the parameter -HeaderName.
If the default behavior is not desired and you want to import the complete worksheet as is, the parameter -NoHeader can be used. In case you want to provide your own property names, you can use the parameter -HeaderName.
.PARAMETER Path
Specifies the path to the Excel file.
.PARAMETER Path
Specifies the path to the Excel file.
.PARAMETER WorksheetName
Specifies the name of the worksheet in the Excel workbook to import. By default, if no name is provided, the first worksheet will be imported.
.PARAMETER WorksheetName
Specifies the name of the worksheet in the Excel workbook to import. By default, if no name is provided, the first worksheet will be imported.
.PARAMETER DataOnly
Import only rows and columns that contain data, empty rows and empty columns are not imported.
.PARAMETER DataOnly
Import only rows and columns that contain data, empty rows and empty columns are not imported.
.PARAMETER HeaderName
Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
.PARAMETER HeaderName
Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
In case you provide less header names than there is data in the worksheet, then only the data with a corresponding header name will be imported and the data without header name will be disregarded.
In case you provide less header names than there is data in the worksheet, then only the data with a corresponding header name will be imported and the data without header name will be disregarded.
In case you provide more header names than there is data in the worksheet, then all data will be imported and all objects will have all the property names you defined in the header names. As such, the last properties will be blanc as there is no data for them.
In case you provide more header names than there is data in the worksheet, then all data will be imported and all objects will have all the property names you defined in the header names. As such, the last properties will be blanc as there is no data for them.
.PARAMETER NoHeader
Automatically generate property names (P1, P2, P3, ..) instead of the ones defined in the column headers of the TopRow.
.PARAMETER NoHeader
Automatically generate property names (P1, P2, P3, ..) instead of the ones defined in the column headers of the TopRow.
This switch is best used when you want to import the complete worksheet as is and are not concerned with the property names.
This switch is best used when you want to import the complete worksheet as is and are not concerned with the property names.
.PARAMETER StartRow
The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
.PARAMETER StartRow
The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
When the parameters -NoHeader and -HeaderName are not provided, this row will contain the column headers that will be used as property names. When one of both parameters are provided, the property names are automatically created and this row will be treated as a regular row containing data.
When the parameters -NoHeader and -HeaderName are not provided, this row will contain the column headers that will be used as property names. When one of both parameters are provided, the property names are automatically created and this row will be treated as a regular row containing data.
.PARAMETER Password
Accepts a string that will be used to open a password protected Excel file.
.PARAMETER EndRow
By default all rows up to the last cell in the sheet will be imported. If specified, import stops at this row.
.PARAMETER Password
Accepts a string that will be used to open a password protected Excel file.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the column names defined in the first row. In case a column doesnt have a column header (usually in row 1 when -StartRow is not used), then the unnamed columns will be skipped and the data in those columns will not be imported.
----------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------
| A B C |
|1 First Name Address |
|2 Chuck Norris California |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors
First Name: Chuck
Address : California
First Name: Jean-Claude
Address : Brussels
Notice that column 'B' is not imported because there's no value in cell 'B1' that can be used as property name for the objects.
.EXAMPLE
Import the complete Excel worksheet as is by using the -NoHeader switch. One object is created for each row. The property names of the objects will be automatically generated (P1, P2, P3, ..).
----------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------
| A B C |
|1 First Name Address |
|2 Chuck Norris California |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -NoHeader
P1: First Name
P2:
P3: Address
P1: Chuck
P2: Norris
P3: California
P1: Jean-Claude
P2: Vandamme
P3: Brussels
Notice that the column header (row 1) is imported as an object too.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the column names defined in the first row. In case a column doesnt have a column header (usually in row 1 when -StartRow is not used), then the unnamed columns will be skipped and the data in those columns will not be imported.
Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the names defined in the parameter -HeaderName. The properties are named starting from the most left column (A) to the right. In case no value is present in one of the columns, that property will have an empty value.
----------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------
| A B C |
|1 First Name Address |
|2 Chuck Norris California |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Movies |
----------------------------------------------------------
| A B C D |
|1 The Bodyguard 1992 9 |
|2 The Matrix 1999 8 |
|3 |
|4 Skyfall 2012 9 |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies -HeaderName 'Movie name', 'Year', 'Rating', 'Genre'
First Name: Chuck
Address : California
Movie name: The Bodyguard
Year : 1992
Rating : 9
Genre :
First Name: Jean-Claude
Address : Brussels
Movie name: The Matrix
Year : 1999
Rating : 8
Genre :
Notice that column 'B' is not imported because there's no value in cell 'B1' that can be used as property name for the objects.
Movie name:
Year :
Rating :
Genre :
Movie name: Skyfall
Year : 2012
Rating : 9
Genre :
Notice that empty rows are imported and that data for the property 'Genre' is not present in the worksheet. As such, the 'Genre' property will be blanc for all objects.
.EXAMPLE
Import the complete Excel worksheet as is by using the -NoHeader switch. One object is created for each row. The property names of the objects will be automatically generated (P1, P2, P3, ..).
Import data from an Excel worksheet. One object is created for each row. The property names of the objects are automatically generated by using the switch -NoHeader (P1, P@, P#, ..). The switch -DataOnly will speed up the import because empty rows and empty columns are not imported.
----------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------
| A B C |
|1 First Name Address |
|2 Chuck Norris California |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Movies |
----------------------------------------------------------
| A B C D |
|1 The Bodyguard 1992 9 |
|2 The Matrix 1999 8 |
|3 |
|4 Skyfall 2012 9 |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -NoHeader
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies NoHeader -DataOnly
P1: First Name
P2:
P3: Address
P1: The Bodyguard
P2: 1992
P3: 9
P1: Chuck
P2: Norris
P3: California
P1: The Matrix
P2: 1999
P3: 8
P1: Jean-Claude
P2: Vandamme
P3: Brussels
P1: Skyfall
P2: 2012
P3: 9
Notice that the column header (row 1) is imported as an object too.
Notice that empty rows and empty columns are not imported.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects consist of the names defined in the parameter -HeaderName. The properties are named starting from the most left column (A) to the right. In case no value is present in one of the columns, that property will have an empty value.
.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.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Movies |
----------------------------------------------------------
| A B C D |
|1 The Bodyguard 1992 9 |
|2 The Matrix 1999 8 |
|3 |
|4 Skyfall 2012 9 |
----------------------------------------------------------
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------------------
| A B C D |
|1 Chuck Norris California |
|2 |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies -HeaderName 'Movie name', 'Year', 'Rating', 'Genre'
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -DataOnly -HeaderName 'FirstName', 'SecondName', 'City' StartRow 2
Movie name: The Bodyguard
Year : 1992
Rating : 9
Genre :
FirstName : Jean-Claude
SecondName: Vandamme
City : Brussels
Movie name: The Matrix
Year : 1999
Rating : 8
Genre :
Notice that only 1 object is imported with only 3 properties. Column B and row 2 are empty and have been disregarded by using the switch '-DataOnly'. The property names have been named with the values provided with the parameter '-HeaderName'. Row number 1 with Chuck Norris has not been imported, because we started the import from row 2 with the parameter -StartRow 2.
Movie name:
Year :
Rating :
Genre :
.LINK
https://github.com/dfinke/ImportExcel
Movie name: Skyfall
Year : 2012
Rating : 9
Genre :
Notice that empty rows are imported and that data for the property 'Genre' is not present in the worksheet. As such, the 'Genre' property will be blanc for all objects.
.EXAMPLE
Import data from an Excel worksheet. One object is created for each row. The property names of the objects are automatically generated by using the switch -NoHeader (P1, P@, P#, ..). The switch -DataOnly will speed up the import because empty rows and empty columns are not imported.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Movies |
----------------------------------------------------------
| A B C D |
|1 The Bodyguard 1992 9 |
|2 The Matrix 1999 8 |
|3 |
|4 Skyfall 2012 9 |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Movies NoHeader -DataOnly
P1: The Bodyguard
P2: 1992
P3: 9
P1: The Matrix
P2: 1999
P3: 8
P1: Skyfall
P2: 2012
P3: 9
Notice that empty rows and empty columns are not imported.
.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.
----------------------------------------------------------
| File: Movies.xlsx - Sheet: Actors |
----------------------------------------------------------
| A B C D |
|1 Chuck Norris California |
|2 |
|3 Jean-Claude Vandamme Brussels |
----------------------------------------------------------
PS C:\> Import-Excel -Path 'C:\Movies.xlsx' -WorkSheetname Actors -DataOnly -HeaderName 'FirstName', 'SecondName', 'City' StartRow 2
FirstName : Jean-Claude
SecondName: Vandamme
City : Brussels
Notice that only 1 object is imported with only 3 properties. Column B and row 2 are empty and have been disregarded by using the switch '-DataOnly'. The property names have been named with the values provided with the parameter '-HeaderName'. Row number 1 with Chuck Norris has not been imported, because we started the import from row 2 with the parameter -StartRow 2.
.LINK
https://github.com/dfinke/ImportExcel
.NOTES
#>
.NOTES
#>
[CmdLetBinding(DefaultParameterSetName)]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
Param (
[Alias('FullName')]
[Parameter(ValueFromPipelineByPropertyName, ValueFromPipeline, Position=0, Mandatory)]
@@ -245,39 +251,24 @@ Function Import-Excel {
[ValidateNotNullOrEmpty()]
[String]$WorksheetName,
[Parameter(ParameterSetName='B', Mandatory)]
[String[]]$HeaderName,
[String[]]$HeaderName ,
[Parameter(ParameterSetName='C', Mandatory)]
[Switch]$NoHeader,
[Switch]$NoHeader ,
[Alias('HeaderRow','TopRow')]
[ValidateRange(1, 9999)]
[Int]$StartRow,
[Int]$StartRow = 1,
[Alias('StopRow','BottomRow')]
[Int]$EndRow ,
[Alias('LeftColumn')]
[Int]$StartColumn = 1,
[Alias('RightColumn')]
[Int]$EndColumn ,
[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
@@ -312,7 +303,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
}
}
}
@@ -327,24 +318,24 @@ Function Import-Excel {
#region Open file
$Path = (Resolve-Path $Path).ProviderPath
Write-Verbose "Import Excel workbook '$Path' with worksheet '$Worksheetname'"
$Stream = New-Object -TypeName System.IO.FileStream -ArgumentList $Path, 'Open', 'Read', 'ReadWrite'
if ($Password) {
$Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage
Try {
$Excel.Load($Stream,$Password)
}
Catch {
throw "Password '$Password' is not correct."
}
Try {
$Excel.Load($Stream,$Password)
}
Catch {
throw "Password '$Password' is not correct."
}
}
else {
$Excel = New-Object -TypeName OfficeOpenXml.ExcelPackage -ArgumentList $Stream
}
#endregion
#region Select worksheet
if ($WorksheetName) {
if (-not ($Worksheet = $Excel.Workbook.Worksheets[$WorkSheetName])) {
@@ -355,74 +346,62 @@ 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'"
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 (-not $EndRow ) {$EndRow = $Worksheet.Dimension.End.Row }
if (-not $EndColumn) {$EndColumn = $Worksheet.Dimension.End.Column }
$endAddress = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[$EndRow]C[$EndColumn]",0,0)
if ($DataOnly) {
#If we are using headers startrow will be the headerrow so examine data from startRow + 1,
if ($NoHeader) {$range = "A" + ($StartRow ) + ":" + $endAddress }
else {$range = "A" + ($StartRow + 1 ) + ":" + $endAddress }
#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..$EndRow ).Where({$rowHash[$_]})
$columns = ($StartColumn..$EndColumn).Where({$colHash[$_]})
}
else {
#region Get rows and columns
if ($DataOnly) {
$CellsWithValues = $AllCells | where {$_.Value}
$Columns = $CellsWithValues.Start.Column | Sort-Object -Unique
$Rows = $CellsWithValues.Start.Row | Sort-Object -Unique
}
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)}
}
#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) {
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
if (-not $Rows) {
Write-Warning "Worksheet '$WorksheetName' in workbook '$Path' contains no data in the rows after top row '$StartRow'"
}
else {
#region Create one object per row
foreach ($R in $Rows) {
Write-Verbose "Import row '$R'"
$NewRow = [Ordered]@{}
foreach ($P in $PropertyNames) {
Add-Property -Name $P.Value -Value $Worksheet.Cells[$R, $P.Column].Value
}
[PSCustomObject]$NewRow
}
#endregion
}
$Columns = $StartColumn..$EndColumn ; if ($StartColumn -gt $EndColumn) {Write-Warning -Message "Selecting columns $StartColumn to $EndColumn might give odd results."}
if ($NoHeader) {$Rows = ( $StartRow)..$EndRow ; if ($StartRow -gt $EndRow) {Write-Warning -Message "Selecting rows $StartRow to $EndRow might give odd results."} }
else {$Rows = (1 + $StartRow)..$EndRow ; if ($StartRow -ge $EndRow) {Write-Warning -Message "Selecting $StartRow as the header with data in $(1+$StartRow) to $EndRow might give odd results."}}
}
#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-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
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'"
}
else {
#region Create one object per row
foreach ($R in $Rows) {
Write-Verbose "Import row '$R'"
$NewRow = [Ordered]@{}
foreach ($P in $PropertyNames) {
$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': $_"
@@ -498,7 +477,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")
@@ -517,10 +496,11 @@ function ConvertFrom-ExcelSheet {
$stream.Close()
$stream.Dispose()
$xl.Dispose()
$xl.Dispose()
}
function Export-MultipleExcelSheets {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
param(
[Parameter(Mandatory=$true)]
$Path,

View File

@@ -20,14 +20,19 @@ Begin {
Write-Verbose "$ModuleName module installation started"
$Files = @(
'AddConditionalFormatting.ps1',
'Charting.ps1',
'ColorCompletion.ps1',
'ConvertFromExcelData.ps1',
'ConvertFromExcelToSQLInsert.ps1',
'ConvertExcelToImageFile.ps1',
'ConvertToExcelXlsx.ps1',
'Copy-ExcelWorkSheet.ps1',
'EPPlus.dll',
'Export-charts.ps1',
'Export-Excel.ps1',
'Export-ExcelSheet.ps1',
'formatting.ps1',
'Get-ExcelColumnName.ps1',
'Get-ExcelSheetInfo.ps1',
'Get-ExcelWorkbookInfo.ps1',
@@ -43,9 +48,14 @@ Begin {
'New-ConditionalText.ps1',
'New-ExcelChart.ps1',
'New-PSItem.ps1',
'Open-ExcelPackage.ps1',
'Pivot.ps1',
'Plot.ps1',
'plot.ps1',
'Send-SqlDataToExcel.ps1',
'Set-CellStyle.ps1',
'Set-Column.ps1',
'Set-Row.ps1',
'SetFormat.ps1',
'TrackingUtils.ps1',
'Update-FirstObjectProperties.ps1'
)

View File

@@ -25,6 +25,7 @@ Begin {
'AddConditionalFormatting.ps1',
'Charting.ps1',
'ColorCompletion.ps1',
'Compare-Worksheet.ps1',
'ConvertFromExcelData.ps1',
'ConvertFromExcelToSQLInsert.ps1',
'ConvertExcelToImageFile.ps1',

471
Merge-worksheet.ps1 Normal file
View File

@@ -0,0 +1,471 @@
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 which 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 changed size will be highlighed in the output sheet. Changes to dates or other attributes will be ignored
.Example
merge-worksheet -RefO (dir .\ImportExcel\4.0.7) -DiffO (dir .\ImportExcel\4.0.8) -Pr Length | Out-GridView
This time no file is written and the results -which include all properties, not just length, are output and sent to Out-Gridview.
This version uses aliases to shorten the parameters,
(OutputFileName can be "outFile" and the sheet "OutSheet" : DifferenceObject & ReferenceObject 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)]
[parameter(ParameterSetName='E',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)]
[parameter(ParameterSetName='E',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')]
[parameter(ParameterSetName='E')]
[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)]
[parameter(ParameterSetName='E',Mandatory=$true)]
[Alias('RefObject')]
$ReferenceObject ,
[parameter(ParameterSetName='D',Mandatory=$true,Position=1)]
[Alias('DiffObject')]
$DifferenceObject ,
[parameter(ParameterSetName='D',Position=2)]
[parameter(ParameterSetName='E',Position=3)]
$DiffPrefix = "=>" ,
#File to hold merged data.
[parameter(Position=3)]
[Alias('OutFile')]
$OutputFile ,
#Name of worksheet to output - if none specified will use the reference worksheet name.
[parameter(Position=4)]
[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 $worksheetName 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) {$workSheet2 = $DiffPrefix = $WorkSheetName[1] ; $worksheet1 = $WorkSheetName[0] ; }
elseif ($WorkSheetName -is [string]) {$worksheet2 = $workSheet1 = $WorkSheetName ;
$DiffPrefix = (Split-Path -Path $Differencefile -Leaf) -replace "\.xlsx$","" }
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::$worksheet1 and/or $Differencefile::$worksheet2." ; return }
if ($NoHeader) {$firstDataRow = $Startrow } else {$firstDataRow = $Startrow + 1}
}
elseif ( $Differencefile) {
if ($WorkSheetName -isnot [string]) {Write-Warning -Message "You must provide a single worksheet name." ; return }
$params = @{WorkSheetName=$WorkSheetName; Path=$Differencefile; ErrorAction = [System.Management.Automation.ActionPreference]::Stop ;}
try {$DifferenceObject = Import-Excel @Params }
Catch {Write-Warning -Message "Could not read the worksheet '$WorkSheetName' from $Differencefile::$WorkSheetName." ; return }
if ($DiffPrefix -eq "=>" ) {
$DiffPrefix = (Split-Path -Path $Differencefile -Leaf) -replace "\.xlsx$",""
}
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 = @()
$DifferenceObject = $DifferenceObject | Update-FirstObjectProperties
$headings = $DifferenceObject[0].psobject.Properties.Name # This preserves the sequence - using get-member would sort them alphabetically! There may be extra properties in
if ($NoHeader -and "Name" -eq $Key) {$Key = "p1"}
if ($headings -notcontains $Key -and
('*' -ne $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) -and
('*' -ne $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
#If key is "*" we treat it differently , and we will create a script property which concatenates all the Properties in $Proplist
$ConCatblock = [scriptblock]::Create( ($proplist | ForEach-Object {'$this."' + $_ + '"'}) -join " + ")
#Build the list of the properties to output, in order.
$diffpart = @()
$refpart = @()
foreach ($p in $proplist.Where({$key -ne $_}) ) {$refPart += $p ; $diffPart += "$DiffPrefix $p" }
#Last reference column will be A if there the only one property (which might be the key), B if there are two properties, C if there 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)
if ($key -ne '*') {
$outputProps = @($key) + $refpart + $diffpart
#If we are using a single column as the key, don't duplicate it, so the last difference column will be A if there is one property, C if there are two, E if there are 3
$lastDiffCol = [char](63 + 2 * $propList.count)
}
else {
$outputProps = @( ) + $refpart + $diffpart
#If we not using a single column as a key all columns are duplicated so, the Last difference column will be B if there is one property, D if there are two, F 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, with the row from the reference sheet, but
#the row in the other sheet might be different so we will look up the row number from the key field - build a hash table for that here
#If we have "*" as the key ad the script property to concatenate the [selected] properties.
$Rowhash = @{}
$rowNo = $firstDataRow
foreach ($row in $ReferenceObject) {
if ($row._row -eq $null) {Add-Member -InputObject $row -MemberType NoteProperty -Value ($rowNo ++) -Name "_Row" }
else {$rowNo++ }
if ($Key -eq '*' ) {Add-Member -InputObject $row -MemberType ScriptProperty -Value $ConCatblock -Name "_All" }
}
$rowNo = $firstDataRow
foreach ($row in $DifferenceObject) {
Add-Member -InputObject $row -MemberType NoteProperty -Value $rowNo -Name "$DiffPrefix Row" -Force
if ($Key -eq '*' ) {
Add-Member -InputObject $row -MemberType ScriptProperty -Value $ConCatblock -Name "_All"
$Rowhash[$row._All] = $rowNo
}
else {$Rowhash[$row.$key] = $rowNo }
$rowNo ++
}
if ($Key -eq '*') {$key = "_ALL"}
#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. ??Might no longer need to preserve the field order
$hash = [ordered]@{}
foreach ($result in $_.Group) {
if ($result.SideIndicator -ne "=>") {$hash["_Row"] = $result._Row }
elseif (-not $hash["$DiffPrefix Row"]) {$hash["_Row"] = "" }
#if we have already set the side, be this must the second record, so set side to indicate "changed"
if ($hash.Side) {$hash.Side = "<>"} else {$hash["Side"] = $result.SideIndicator}
switch ($hash.side) {
'==' { $hash["$DiffPrefix is"] = 'Same' }
'=>' { $hash["$DiffPrefix is"] = 'Added' }
'<>' { if (-not $hash["_Row"]) {
$hash["$DiffPrefix is"] = 'Added'
}
else {
$hash["$DiffPrefix is"] = 'Changed'
}
}
'<=' { $hash["$DiffPrefix is"] = 'Removed'}
}
#find the number of the row in the the "difference" object which has this key. If it is the object is only the reference this will be blank.
$hash["$DiffPrefix Row"] = $Rowhash[$keyval]
$hash[$key] = $keyval
#Create FieldName and/or =>FieldName columns
foreach ($p in $result.psobject.Properties.name.where({$_ -ne $key -and $_ -ne "SideIndicator" -and $_ -ne "$DiffPrefix Row" })) {
if ($result.SideIndicator -eq "==" -and $p -in $propList)
{$hash[("$p")] = $hash[("$DiffPrefix $p")] = $result.$P}
elseif ($result.SideIndicator -eq "==" -or $result.SideIndicator -eq "<=")
{$hash[("$p")] = $result.$P}
elseif ($result.SideIndicator -eq "=>") { $hash[("$DiffPrefix $p")] = $result.$P}
}
}
[Pscustomobject]$hash
} | Sort-Object -Property "_row"
#Already sorted by reference row number, fill in any blanks in the difference-row column
for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i]."$DiffPrefix Row") {$expandedDiff[$i]."$DiffPrefix Row" = $expandedDiff[$i-1]."$DiffPrefix Row" } }
#Now re-Sort by difference row number, and fill in any blanks in the reference-row column
$expandedDiff = $expandedDiff | Sort-Object -Property "$DiffPrefix Row"
for ($i = 1; $i -lt $expandedDiff.Count; $i++) {if (-not $expandedDiff[$i]."_Row") {$expandedDiff[$i]."_Row" = $expandedDiff[$i-1]."_Row" } }
$AllProps = @("_Row") + $OutputProps + $expandedDiff[0].psobject.properties.name.where({$_ -notin ($outputProps + @("_row","side","SideIndicator","_ALL" ))})
if ($PassThru -or -not $OutputFile) {return ($expandedDiff | Select-Object -Property $allprops | Sort-Object -Property "_row", "$DiffPrefix Row" | Update-FirstObjectProperties ) }
elseif ($PSCmdlet.ShouldProcess($OutputFile,"Write Output to Excel file")) {
$expandedDiff = $expandedDiff | Sort-Object -Property "_row", "$DiffPrefix Row"
$xl = $expandedDiff | Select-Object -Property $OutputProps | Update-FirstObjectProperties |
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
}
}
Function Merge-MulipleSheets {
<#
.Synopsis
Merges worksheets into a single worksheet with differences marked up.
.Description
The Merge worksheet command combines 2 sheets. Merge-MultipleSheets is designed to merge more than 2.
So if asked to merge sheets A,B,C which contain Services, with a Name, Displayname and Start mode, where "name" is treated as the key
it calls Merge-Worksheet to merge Name, Displayname and Start mode,from sheets A and C the result has column headings
-Row, Name, DisplayName, Startmode, C-DisplayName, C-StartMode C-Is, C-Row
Then it calls merge-worsheet with this result and sheet B, comparing 'Name', 'Displayname' and 'Start mode' columns on each side and outputting
_Row, Name, DisplayName, Startmode, B-DisplayName, B-StartMode B-Is, B-Row, C-DisplayName, C-StartMode C-Is, C-Row
Any columns in the "reference" side which are not used in the comparison are appended on the right, which is we compare the sheets in reverse order
The "Is" column holds "Same", "Added", "Removed" or "Changed" and is used for conditional formatting in the output sheet (this is hidden by default),
and when the data is written to Excel the "reference" columns "DisplayName" and "Start" are renamed "A-DisplayName" and "A-Start"
Conditional formatting is also applied to the "key" column (name in this case) so the view can be filtered to rows with changes by filtering this column on color.
Note: the processing order can affect what is seen as a change. For example if there is an extra item in sheet B in the example above,
Sheet C will be processed and that row and nothing will be seen to be missing. When sheet B is processed it is marked as an addition, and the conditional formatting marks
the entries from sheet A to show that a values were added in at least one sheet.
However of Sheet B is the reference sheet, A and C will be seen to have an item removed; and if B is processed before C, the extra item is known when C is processed and
so C is considered to be missing that item.
.Example
dir Server*.xlsx | Merge-MulipleSheets -WorkSheetName Services -OutputFile Test2.xlsx -OutputSheetName Services -Show
We are auditing servers and each one has a workbook in the current directory which contains a "Services" worksheet (the result of
Get-WmiObject -Class win32_service | Select-Object -Property Name, Displayname, Startmode
No key is specified so the key is assumed to be the "Name" column. The files are merged and the result is opened on completion.
.Example
dir Serv*.xlsx | Merge-MulipleSheets -WorkSheetName Software -Key "*" -ExcludeProperty Install* -OutputFile Test2.xlsx -OutputSheetName Software -Show
The server audit files in the previous example also have "Software" worksheet, but no single field on that sheet works as a key.
Specifying "*" for the key produces a compound key using all non-excluded fields (and the installation date and file location are excluded).
.Example
Merge-MulipleSheets -Path hotfixes.xlsx -WorkSheetName Serv* -Key hotfixid -OutputFile test2.xlsx -OutputSheetName hotfixes -HideRowNumbers -Show
This time all the servers have written their hofix information to their own worksheets in a shared Excel workbook named "Hotfixes"
(the information was obtained by running Get-Hotfix | Sort-Object -Property description,hotfixid | Select-Object -Property Description,HotfixID)
This ignores any sheets which are not named "Serv*", and uses the HotfixID as the key ; in this version the row numbers are hidden.
#>
param (
[Parameter(Mandatory=$true,ValueFromPipeline=$true)]
[string[]]$Path ,
#The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
[int]$Startrow = 1,
#Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
[String[]]$Headername,
#Automatically generate property names (P1, P2, P3, ..) instead of the using the values the top row of the sheet.
[switch]$NoHeader,
#Name(s) of worksheets to compare,
$WorkSheetName = "Sheet1",
#File to write output to
[Alias('OutFile')]
$OutputFile = ".\temp.xlsx",
#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 = "Red",
#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 = "Orange",
#if Specified hides the columns in the spreadsheet that contain the row numbers
[switch]$HideRowNumbers ,
#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
)
begin { $filestoProcess = @() }
process { $filestoProcess += $Path}
end {
if ($filestoProcess.Count -eq 1 -and $WorkSheetName -match '\*') {
Write-Progress -Activity "Merging sheets" -CurrentOperation "Expanding * to names of sheets in $($filestoProcess[0]). "
$excel = Open-ExcelPackage -Path $filestoProcess
$WorksheetName = $excel.Workbook.Worksheets.Name.where({$_ -like $WorkSheetName})
Close-ExcelPackage -NoSave -ExcelPackage $excel
}
#Merge indentically named sheets in different work books;
if ($filestoProcess.Count -ge 2 -and $WorkSheetName -is "string" ) {
Get-Variable -Name 'HeaderName','NoHeader','StartRow','Key','Property','ExcludeProperty','WorkSheetName' -ErrorAction SilentlyContinue |
Where-Object {$_.Value} | ForEach-Object -Begin {$params= @{} } -Process {$params[$_.Name] = $_.Value}
Write-Progress -Activity "Merging sheets" -CurrentOperation "Comparing $($filestoProcess[-1]) against $($filestoProcess[0]). "
$merged = Merge-Worksheet @params -Referencefile $filestoProcess[0] -Differencefile $filestoProcess[-1]
$nextFileNo = 2
while ($nextFileNo -lt $filestoProcess.count -and $merged) {
Write-Progress -Activity "Merging sheets" -CurrentOperation "Comparing $($filestoProcess[-$nextFileNo]) against $($filestoProcess[0]). "
$merged = Merge-Worksheet @params -ReferenceObject $merged -Differencefile $filestoProcess[-$nextFileNo]
$nextFileNo ++
}
}
#Merge different sheets from one workbook
elseif ($filestoProcess.Count -eq 1 -and $WorkSheetName.Count -ge 2 ) {
Get-Variable -Name 'HeaderName','NoHeader','StartRow','Key','Property','ExcludeProperty' -ErrorAction SilentlyContinue |
Where-Object {$_.Value} | ForEach-Object -Begin {$params= @{} } -Process {$params[$_.Name] = $_.Value}
Write-Progress -Activity "Merging sheets" -CurrentOperation "Comparing $($WorkSheetName[-1]) against $($WorkSheetName[0]). "
$merged = Merge-Worksheet @params -Referencefile $filestoProcess[0] -Differencefile $filestoProcess[0] -WorkSheetName $WorkSheetName[0,-1]
$nextSheetNo = 2
while ($nextSheetNo -lt $WorkSheetName.count -and $merged) {
Write-Progress -Activity "Merging sheets" -CurrentOperation "Comparing $($WorkSheetName[-$nextSheetNo]) against $($WorkSheetName[0]). "
$merged = Merge-Worksheet @params -ReferenceObject $merged -Differencefile $filestoProcess[0] -WorkSheetName $WorkSheetName[-$nextSheetNo] -DiffPrefix $WorkSheetName[-$nextSheetNo]
$nextSheetNo ++
}
}
#We either need one worksheet name and many files or one file and many sheets.
else { Write-Warning -Message "Need at least two files to process" ; return }
#if the process didn't return data then abandon now.
if (-not $merged) {Write-Warning -Message "The merge operation did not return any data."; return }
Write-Progress -Activity "Merging sheets" -CurrentOperation "Creating output sheet '$OutputSheetName' in $OutputFile"
$excel = $merged | Sort-Object "_row" | Update-FirstObjectProperties |
Export-Excel -Path $OutputFile -WorkSheetname $OutputSheetName -ClearSheet -BoldTopRow -AutoFilter -PassThru
$sheet = $excel.Workbook.Worksheets[$OutputSheetName]
#We will put in a conditional format for "if all the others are not flagged as 'same'" to mark rows where something is added, removed or changed
$sameChecks = @()
#All the 'difference' columns in the sheet are labeled with the file they came from, 'reference' columns need their
#headers prefixed with the ref file name, $colnames is the basis of a regular expression to identify what should have $refPrefix appended
$colNames = @("_Row")
if ($key -ne "*")
{$colnames += $Key}
if ($filesToProcess.Count -ge 2) {
$refPrefix = (Split-Path -Path $filestoProcess[0] -Leaf) -replace "\.xlsx$"," "
}
else {$refPrefix = $WorkSheetName[0] }
Write-Progress -Activity "Merging sheets" -CurrentOperation "Applying formatting to sheet '$OutputSheetName' in $OutputFile"
#Find the column headings which are in the form "diffFile is"; which will hold 'Same', 'Added' or 'Changed'
foreach ($cell in $sheet.Cells[($sheet.Dimension.Address -replace "\d+$","1")].Where({$_.value -match "\sIS$"}) ) {
#Work leftwards across the headings applying conditional formatting which says
# 'Format this cell if the "IS" column has a value of ...' until you find a heading which doesn't have the prefix.
$prefix = $cell.value -replace "\sIS$",""
$columnNo = $cell.start.Column -1
$cellAddr = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R1C$columnNo",1,$columnNo)
while ($sheet.cells[$cellAddr].value -match $prefix) {
$condFormattingParams = @{RuleType='Expression'; BackgroundPattern='None'; WorkSheet=$sheet; Range=$([OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[1]C[$columnNo]:R[1048576]C[$columnNo]",0,0)) }
Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Added"' ) -BackgroundColor $AddBackgroundColor
Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Changed"') -BackgroundColor $ChangeBackgroundColor
Add-ConditionalFormatting @condFormattingParams -ConditionValue ($cell.Address + '="Removed"') -BackgroundColor $DeleteBackgroundColor
$columnNo --
$cellAddr = [OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R1C$columnNo",1,$columnNo)
}
#build up a list of prefixes in $colnames - we'll use that to set headers on rows from the reference file; and build up the "if the 'is' cell isn't same" list
$colNames += $prefix
$sameChecks += (($cell.Address -replace "1","2") +'<>"Same"')
}
#For all the columns which don't match one of the Diff-file prefixes or "_Row" or the 'Key' columnn name; add the reference file prefix to their header.
$nameRegex = $colNames -Join "|"
foreach ($cell in $sheet.Cells[($sheet.Dimension.Address -replace "\d+$","1")].Where({$_.value -Notmatch $nameRegex}) ) {
$cell.Value = $refPrefix + $cell.Value
$condFormattingParams = @{RuleType='Expression'; BackgroundPattern='None'; WorkSheet=$sheet; Range=[OfficeOpenXml.ExcelAddress]::TranslateFromR1C1("R[2]C[$($cell.start.column)]:R[1048576]C[$($cell.start.column)]",0,0)}
Add-ConditionalFormatting @condFormattingParams -ConditionValue ("OR(" +(($sameChecks -join ",") -replace '<>"Same"','="Added"') +")" ) -BackgroundColor $DeleteBackgroundColor
}
#We've made a bunch of things wider so now is the time to autofit columns. Any hiding has to come AFTER this, because it unhides things
$sheet.Cells.AutoFitColumns()
#if we have a key field (we didn't concatenate all fields) use what we built up in $sameChecks to apply conditional formatting to it (Row no will be in column A, Key in Column B)
if ($Key -ne '*') {
Add-ConditionalFormatting -WorkSheet $sheet -Range "B2:B1048576" -ForeGroundColor $KeyFontColor -BackgroundPattern 'None' -RuleType Expression -ConditionValue ("OR(" +($sameChecks -join ",") +")" )
$sheet.view.FreezePanes(2, 3)
}
else {$sheet.view.FreezePanes(2, 2) }
#Go back over the headings to find and hide the "is" columns;
foreach ($cell in $sheet.Cells[($sheet.Dimension.Address -replace "\d+$","1")].Where({$_.value -match "\sIS$"}) ) {
$sheet.Column($cell.start.Column).HIDDEN = $true
}
#If specified, look over the headings for "row" and hide the columns which say "this was in row such-and-such"
if ($HideRowNumbers) {
foreach ($cell in $sheet.Cells[($sheet.Dimension.Address -replace "\d+$","1")].Where({$_.value -match "Row$"}) ) {
$sheet.Column($cell.start.Column).HIDDEN = $true
}
}
Close-ExcelPackage -ExcelPackage $excel -Show:$Show
Write-Progress -Activity "Merging sheets" -Completed
}
}

View File

@@ -11,7 +11,7 @@
This will open the file at $xlPath, select sheet1 apply formatting to two blocks of the sheet and close the package
#>
[OutputType([OfficeOpenXml.ExcelPackage])]
Param ([Parameter(Mandatory=$true)]$path,
Param ([Parameter(Mandatory=$true)]$Path,
[switch]$KillExcel)
if($KillExcel) {

View File

@@ -1,7 +1,13 @@
PowerShell Import-Excel
-
This PowerShell Module allows you to read and write Excel files without installing Microsoft Excel on your system. No need to bother with the cumbersome Excel COM-objects thanks to the .NET EPPlus DLL (http://epplus.codeplex.com/) which is included in the module. Creating Tables, Pivot Tables, Charts and much more has just become a lot easier.
## Build Status
[![Build status](https://ci.appveyor.com/api/projects/status/21hko6eqtpccrkba?svg=true)](https://ci.appveyor.com/project/dfinke/importexcel)
Install from the [PowerShell Gallery](https://www.powershellgallery.com/packages/ImportExcel/).
This PowerShell Module allows you to read and write Excel files without installing Microsoft Excel on your system. No need to bother with the cumbersome Excel COM-object. Creating Tables, Pivot Tables, Charts and much more has just become a lot easier.
![](https://raw.githubusercontent.com/dfinke/ImportExcel/master/images/testimonial.png)
@@ -31,6 +37,47 @@ iex (new-object System.Net.WebClient).DownloadString('https://raw.github.com/dfi
# What's new
#### 06/08/2018
Thank you again to [James O'Neill](https://twitter.com/jamesoneill) for the lion share of these updates.
Most notably the performance gains in the `Import-Excel` function.
In addition, I have started to create tests and wired up Appveyor for continuous integration
This release will be bumped to 5.0.0 and will be published officially after more testing.
* added databar to Examples
* Fix databar example
* increased how long the import should take
* Renamed test directory
* Added PSVersion. Point to the new directory for tests
* Added EndRow, StartColumn, EndColumn to Import-Excel
* Added Merge-MultipleSheets to argument completers
* added build badge
* Making Merge-Worksheet, and Merge-MultipleWorksheet ready to release
* Added Merge Multiple worksheet
* Revert "Added Multiple Merge to Merge-Worksheet.ps1"
* Added Multiple Merge to Merge-Worksheet.ps1
* Added Merge-worksheet
* Force install of pester
* Checks version of pester on appveyor
* First step to wire up appveyor
* 13 days ago : Tidying of case, parameter clarity, removal of aliases. Added timeout to send-SqlDataToExcel Added Merge WorkSheet Fixed bugs in Compare-Worksheet
#### 4/22/2018
Thanks to the community yet again
- [ili101](https://github.com/ili101) for fixes and features
- Removed `[PSPlot]` as OutputType. Fixes it throwing an error
- [Nasir Zubair](https://github.com/nzubair) added `ConvertEmptyStringsToNull` to the function `ConvertFrom-ExcelToSQLInsert`
- If specified, cells without any data are replaced with NULL, instead of an empty string. This is to address behviors in certain DBMS where an empty string is insert as 0 for INT column, instead of a NULL value.
#### 4/10/2018
-New parameter `-ReZip`. It ReZips the xlsx so it can be imported to PowerBI
Thanks to [Justin Grote](https://github.com/JustinGrote) for finding and fixing the error that Excel files created do not import to PowerBI online. Plus, thank you to [CrashM](https://github.com/CrashM) for confirming the fix.
Super helpful!
#### 3/31/2018
- Updated `Set-Format`
* Added parameters to set borders for cells, including top, bottm, left and right

View File

@@ -1,5 +1,7 @@
Function Send-SQLDataToExcel {
<#
[CmdLetBinding()]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword","")]
<#
.Synopsis
Runs a SQL query and inserts the results into an ExcelSheet, more efficiently than sending it via Export-Excel
.Description
@@ -23,7 +25,7 @@
#>
#>
param (
#Database connection string; either DSN=ODBC_Data_Source_Name, a full odbc or SQL Connection string, or the name of a SQL server
[Parameter(ParameterSetName="SQLConnection", Mandatory=$true)]
@@ -41,6 +43,9 @@
#The SQL query to run
[Parameter(Mandatory=$true)]
[string]$SQL,
#Override the default query time of 30 seconds.
[int]$QueryTimeout,
#File name for the Excel File
$Path,
[String]$WorkSheetname = 'Sheet1',
[Switch]$KillExcel,
@@ -92,9 +97,9 @@
#We were either given a session object or a connection string (with, optionally a MSSQLServer parameter)
# If we got -MSSQLServer, create a SQL connection, if we didn't but we got -Connection create an ODBC connection
if ($MsSQLserver) {
if ($connection -notmatch "=") {$Connection = "server=$Connection;trusted_connection=true;timeout=60"}
if ($Connection -notmatch "=") {$Connection = "server=$Connection;trusted_connection=true;timeout=60"}
$Session = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $Connection
if ($Session.State -ne 'Open') {$session.Open()}
if ($Session.State -ne 'Open') {$Session.Open()}
if ($DataBase) {$Session.ChangeDatabase($DataBase) }
}
elseif ($Connection) {
@@ -102,30 +107,31 @@
}
#A session was either passed in or just created. If it's a SQL one make a SQL DataAdapter, otherwise make an ODBC one
if ($Session.gettype().name -match "SqlConnection") {
if ($Session.GetType().name -match "SqlConnection") {
$dataAdapter = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter -ArgumentList (
New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $sql, $Session)
New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $SQL, $Session)
}
else {
$dataAdapter = New-Object -TypeName System.Data.Odbc.OdbcDataAdapter -ArgumentList (
New-Object -TypeName System.Data.Odbc.OdbcCommand -ArgumentList $sql, $Session )
New-Object -TypeName System.Data.Odbc.OdbcCommand -ArgumentList $SQL, $Session )
}
if ($QueryTimeout) {$dataAdapter.SelectCommand.CommandTimeout = $ServerTimeout}
#Both adapter types output the same kind of table, create one and fill it from the adapter
$dataTable = New-Object -TypeName System.Data.DataTable
$rowCount = $dataAdapter.fill($dataTable)
Write-Verbose "Query returned $rowcount row(s)"
Write-Verbose -Message "Query returned $rowCount row(s)"
#ExportExcel user a -NoHeader parameter so that's what we use here, but needs to be the other way around.
$PrintHeaders = -not $NoHeader
$printHeaders = -not $NoHeader
if ($Title) {$r = $StartRow +1 }
else {$r = $StartRow}
#Get our Excel sheet and fill it with the data
$excelPackage = Export-Excel -Path $Path -WorkSheetname $WorkSheetname -PassThru
$excelPackage.Workbook.Worksheets[$WorkSheetname].Cells[$r,$StartColumn].LoadFromDataTable($dataTable, $PrintHeaders ) | Out-Null
$excelPackage.Workbook.Worksheets[$WorkSheetname].Cells[$r,$StartColumn].LoadFromDataTable($dataTable, $printHeaders ) | Out-Null
#Call export-excel with any parameters which don't relate to the SQL query
"Connection", "Database" , "Session", "MsSQLserver", "Destination" , "sql" ,"Path" | foreach-object {$null = $PSBoundParameters.Remove($_) }
"Connection", "Database" , "Session", "MsSQLserver", "Destination" , "SQL" ,"Path" | ForEach-Object {$null = $PSBoundParameters.Remove($_) }
Export-Excel -ExcelPackage $excelPackage @PSBoundParameters
#If we were not passed a session close the session we created.

View File

@@ -108,7 +108,7 @@
else { $cellData = $Value}
if ($cellData -match "^=") { $Worksheet.Cells[$Row, $Column].Formula = $cellData }
else { $Worksheet.Cells[$Row, $Column].Value = $cellData }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } # This is not a custom format, but a preset recognized as date and localized.
}}
#region Apply formatting
if ($Underline) {

View File

@@ -112,7 +112,7 @@
else{$cellData = $Value}
if ($cellData -match "^=") { $Worksheet.Cells[$Row, $column].Formula = $cellData }
else { $Worksheet.Cells[$Row, $Column].Value = $cellData }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' }
if ($cellData -is [datetime]) { $Worksheet.Cells[$Row, $Column].Style.Numberformat.Format = 'm/d/yy h:mm' } # This is not a custom format, but a preset recognized as date and localized.
}}
#region Apply formatting
if ($Underline) {

View File

@@ -83,7 +83,7 @@
begin {
#Allow Set-Format to take Worksheet and range parameters (like Add Contitional formatting) - convert them to an address
if ($WorkSheet -and $Range) {$Address = $WorkSheet.Cells[$Range] }
}
}
process {
if ($Address -is [Array]) {
@@ -150,7 +150,7 @@
if ($Address -is [OfficeOpenXml.ExcelRow] ) {$Address.Height = $Height }
elseif ($Address -is [OfficeOpenXml.ExcelRange] ) {
($Address.Start.Row)..($Address.Start.Row + $Address.Rows) |
ForEach-Object {$ws.Row($_).Height = $Height }
ForEach-Object {$Address.WorkSheet.Row($_).Height = $Height }
}
else {Write-Warning -Message ("Can set the height of a row or a range but not a {0} object" -f ($Address.GetType().name)) }
}

BIN
Testimonials/t1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

View File

@@ -0,0 +1,32 @@
Import-Module $PSScriptRoot\..\..\ImportExcel.psd1
Describe "Tests" {
BeforeAll {
$data = $null
$timer = Measure-Command {
$data = Import-Excel $PSScriptRoot\Simple.xlsx
}
}
It "Should have two items" {
$data.count | Should be 2
}
It "Should have items a and b" {
$data[0].p1 | Should be "a"
$data[1].p1 | Should be "b"
}
It "Should read fast < 1000 milliseconds" {
$timer.TotalMilliseconds | should BeLessThan 1000
}
It "Should read larger xlsx, 4k rows 1 col < 2000 milliseconds" {
$timer = Measure-Command {
$null = Import-Excel $PSScriptRoot\LargerFile.xlsx
}
$timer.TotalMilliseconds | should BeLessThan 2000
}
}

Binary file not shown.

BIN
appveyor.yml Normal file

Binary file not shown.

252
compare-WorkSheet.ps1 Normal file
View File

@@ -0,0 +1,252 @@
Function Compare-WorkSheet {
<#
.Synopsis
Compares two worksheets with the same name in different files.
.Description
This command takes two file names, a worksheet name and a name for a key column.
It reads the worksheet from each file and decides the column names.
It builds as hashtable of the key column values and the rows they appear in
It then uses PowerShell's compare object command to compare the sheets (explicity checking all column names which have not been excluded)
For the difference rows it adds the row number for the key of that row - we have to add the key after doing the comparison,
otherwise rows will be considered as different simply because they have different row numbers
We also add the name of the file in which the difference occurs.
If -BackgroundColor is specified the difference rows will be changed to that background.
.Example
Compare-WorkSheet -Referencefile 'Server56.xlsx' -Differencefile 'Server57.xlsx' -WorkSheetName Products -key IdentifyingNumber -ExcludeProperty Install* | format-table
The two workbooks in this example contain the result of redirecting a subset of properties from Get-WmiObject -Class win32_product to Export-Excel
The command compares the "products" pages in the two workbooks, but we don't want to register a differnce if if the software was installed on a
different date or from a different place, so Excluding Install* removes InstallDate and InstallSource.
This data doesn't have a "name" column" so we specify the "IdentifyingNumber" column as the key.
The results will be presented as a table.
.Example
compare-WorkSheet "Server54.xlsx" "Server55.xlsx" -WorkSheetName services -GridView
This time two workbooks contain the result of redirecting Get-WmiObject -Class win32_service to Export-Excel
Here the -Differencefile and -Referencefile parameter switches are assumed , and the default setting for -key ("Name") works for services
This will display the differences between the "services" sheets using a grid view
.Example
Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName Services -BackgroundColor lightGreen
This version of the command outputs the differences between the "services" pages and also highlights any different rows in the spreadsheet files.
.Example
Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName Services -BackgroundColor lightGreen -FontColor Red -Show
This builds on the previous example: this time Where two changed rows have the value in the "name" column (the default value for -key),
this version adds highlighting of the changed cells in red; and then opens the Excel file.
.Example
Compare-WorkSheet 'Pester-tests.xlsx' 'Pester-tests.xlsx' -WorkSheetName 'Server1','Server2' -Property "full Description","Executed","Result" -Key "full Description"
This time the reference file and the difference file are the same file and two different sheets are used. Because the tests include the
machine name and time the test was run the command specifies a limited set of columns should be used.
.Example
Compare-WorkSheet 'Server54.xlsx' 'Server55.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -GridView -ExcludeDifferent
The "General" page has a title and two unlabelled columns with a row forCPU, Memory, Domain, Disk and so on
So the command is instructed to starts at row 2 to skip the title and to name the columns: the first is "label" and the Second "Value";
the label acts as the key. This time we interested the rows which are the same in both sheets,
and the result is displayed using grid view. Note that grid view works best when the number of columns is small.
.Example
Compare-WorkSheet 'Server1.xlsx' 'Server2.xlsx' -WorkSheetName general -Startrow 2 -Headername Label,value -Key Label -BackgroundColor White -Show -AllDataBackgroundColor LightGray
This version of the previous command lightlights all the cells in lightgray and then sets the changed rows back to white; only
the unchanged rows are highlighted
#>
[cmdletbinding(DefaultParameterSetName)]
Param(
#First file to compare
[parameter(Mandatory=$true,Position=0)]
$Referencefile ,
#Second file to compare
[parameter(Mandatory=$true,Position=1)]
$Differencefile ,
#Name(s) of worksheets to compare.
$WorkSheetName = "Sheet1",
#Properties to include in the DIFF - supports wildcards, default is "*"
$Property = "*" ,
#Properties to exclude from the the search - supports wildcards
$ExcludeProperty ,
#Specifies custom property names to use, instead of the values defined in the column headers of the TopRow.
[Parameter(ParameterSetName='B', Mandatory)]
[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)]
[switch]$NoHeader,
#The row from where we start to import data, all rows above the StartRow are disregarded. By default this is the first row.
[int]$Startrow = 1,
#If specified, highlights all the cells - so you can make Equal cells one colour, and Diff cells another.
[System.Drawing.Color]$AllDataBackgroundColor,
#If specified, highlights the DIFF rows
[System.Drawing.Color]$BackgroundColor,
#If specified identifies the tabs which contain DIFF rows (ignored if -backgroundColor is omitted)
[System.Drawing.Color]$TabColor,
#Name of a column which is unique and will be used to add a row to the DIFF object, default is "Name"
$Key = "Name" ,
#If specified, highlights the DIFF columns in rows which have the same key.
[System.Drawing.Color]$FontColor,
#If specified opens the Excel workbooks instead of outputting the diff to the console (unless -passthru is also specified)
[Switch]$Show,
#If specified, the command tries to the show the DIFF in a Gridview and not on the console. (unless-Passthru is also specified). This Works best with few columns selected, and requires a key
[switch]$GridView,
#If specified -Passthrough full set of diff data is returned without filtering to the specified properties
[Switch]$PassThru,
#If specified the result will include equal rows as well. By default only different rows are returned
[Switch]$IncludeEqual,
#If Specified the result includes only the rows where both are equal
[Switch]$ExcludeDifferent
)
#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 mush have two different worksheet names. If we have two files we can 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 {
$Sheet1 = Import-Excel -Path $Referencefile -WorksheetName $WorkSheet1 @params
$Sheet2 = Import-Excel -Path $Differencefile -WorksheetName $WorkSheet2 @Params
}
Catch {Write-Warning -Message "Could not read the worksheet from $Referencefile and/or $Differencefile." ; return }
#Get Column headings and create a hash table of Name to column letter.
$headings = $Sheet1[-1].psobject.Properties.name # This preserves the sequence - using get-member would sort them alphabetically!
$headings | ForEach-Object -Begin {$columns = @{} ; $i=65 } -Process {$Columns[$_] = [char]($i ++) }
#Make a list of property headings using the Property (default "*") and ExcludeProperty parameters
if ($Key -eq "Name" -and $NoHeader) {$key = "p1"}
$propList = @()
foreach ($p in $Property) {$propList += ($headings.where({$_ -like $p}) )}
foreach ($p in $ExcludeProperty) {$propList = $propList.where({$_ -notlike $p}) }
if (($headings -contains $key) -and ($propList -notcontains $Key)) {$propList += $Key}
$propList = $propList | Select-Object -Unique
if ($propList.Count -eq 0) {Write-Warning -Message "No Columns are selected with -Property = '$Property' and -excludeProperty = '$ExcludeProperty'." ; return}
#Add RowNumber, Sheetname and file name to every row
$FirstDataRow = $startRow + 1
if ($Headername -or $NoHeader) {$FirstDataRow -- }
$i = $FirstDataRow ; foreach ($row in $Sheet1) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++)
Add-Member -InputObject $row -MemberType NoteProperty -Name "_Sheet" -Value $worksheet1
Add-Member -InputObject $row -MemberType NoteProperty -Name "_File" -Value $Referencefile}
$i = $FirstDataRow ; foreach ($row in $Sheet2) {Add-Member -InputObject $row -MemberType NoteProperty -Name "_Row" -Value ($i ++)
Add-Member -InputObject $row -MemberType NoteProperty -Name "_Sheet" -Value $worksheet2
Add-Member -InputObject $row -MemberType NoteProperty -Name "_File" -Value $Differencefile}
if ($ExcludeDifferent -and -not $IncludeEqual) {$IncludeEqual = $true}
#Do the comparison and add file,sheet and row to the result - these are prefixed with "_" to show they are added the addition will fail if the sheet has these properties so split the operations
[PSCustomObject[]]$diff = Compare-Object -ReferenceObject $Sheet1 -DifferenceObject $Sheet2 -Property $propList -PassThru -IncludeEqual:$IncludeEqual -ExcludeDifferent:$ExcludeDifferent |
Sort-Object -Property "_Row","File"
#if BackgroundColor was specified, set it on extra or extra or changed rows
if ($diff -and $BackgroundColor) {
#Differences may only exist in one file. So gather the changes for each file; open the file, update each impacted row in the shee, save the file
$updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property "_File"
foreach ($file in $updates) {
try {$xl = Open-ExcelPackage -Path $file.name }
catch {Write-warning -Message "Can't open $($file.Name) for writing." ; return}
if ($AllDataBackgroundColor) {
$file.Group._sheet | Sort-Object -Unique | ForEach-Object {
$ws = $xl.Workbook.Worksheets[$_]
if ($headerName) {$range = "A" + $startrow + ":" + $ws.dimension.end.address}
else {$range = "A" + ($startrow + 1) + ":" + $ws.dimension.end.address}
Set-Format -WorkSheet $ws -BackgroundColor $AllDataBackgroundColor -Range $Range
}
}
foreach ($row in $file.group) {
$ws = $xl.Workbook.Worksheets[$row._Sheet]
$range = $ws.Dimension -replace "\d+",$row._row
Set-Format -WorkSheet $ws -Range $range -BackgroundColor $BackgroundColor
}
if ($TabColor) {
foreach ($tab in ($file.group._sheet | Select-Object -Unique)) {
$xl.Workbook.Worksheets[$tab].TabColor = $TabColor
}
}
$xl.save() ; $xl.Stream.Close() ; $xl.Dispose()
}
}
#if font colour was specified, set it on changed properties where the same key appears in both sheets.
if ($diff -and $FontColor -and ($propList -contains $Key) ) {
$updates = $diff.where({$_.SideIndicator -ne "=="}) | Group-object -Property $Key | Where-Object {$_.count -eq 2}
if ($updates) {
$XL1 = Open-ExcelPackage -path $Referencefile
if ($oneFile ) {$xl2 = $xl1}
else {$xl2 = Open-ExcelPackage -path $Differencefile }
foreach ($u in $updates) {
foreach ($p in $propList) {
if($u.Group[0].$p -ne $u.Group[1].$p ) {
Set-Format -WorkSheet $xl1.Workbook.Worksheets[$u.Group[0]._sheet] -Range ($Columns[$p] + $u.Group[0]._Row) -FontColor $FontColor
Set-Format -WorkSheet $xl2.Workbook.Worksheets[$u.Group[1]._sheet] -Range ($Columns[$p] + $u.Group[1]._Row) -FontColor $FontColor
}
}
}
$xl1.Save() ; $xl1.Stream.Close() ; $xl1.Dispose()
if (-not $oneFile) {$xl2.Save() ; $xl2.Stream.Close() ; $xl2.Dispose()}
}
}
elseif ($diff -and $FontColor) {Write-Warning -Message "To match rows to set changed cells, you must specify -Key and it must match one of the included properties." }
#if nothing was found write a message which wont be redirected
if (-not $diff) {Write-Host "Comparison of $Referencefile::$worksheet1 and $Differencefile::$WorkSheet2 returned no results." }
if ($show) {
Start-Process -FilePath $Referencefile
if (-not $oneFile) { Start-Process -FilePath $Differencefile }
if ($GridView) { Write-Warning -Message "-GridView is ignored when -Show is specified" }
}
elseif ($GridView -and $propList -contains $key) {
if ($IncludeEqual -and -not $ExcludeDifferent) {
$GroupedRows = $diff | Group-Object -Property $key
}
else { #to get the right now numbers on the grid we need to have all the rows.
$GroupedRows = Compare-Object -ReferenceObject $Sheet1 -DifferenceObject $Sheet2 -Property $propList -PassThru -IncludeEqual |
Group-Object -Property $key
}
#Additions, deletions and unchanged rows will give a group of 1; changes will give a group of 2 .
#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
$Sheet2 | ForEach-Object -Begin {$Rowhash = @{} } -Process {$Rowhash[$_.$key] = $_._row }
$ExpandedDiff = ForEach ($g in $GroupedRows) {
#we're going to create a custom object from a hash table. We want the fields to be ordered
$hash = [ordered]@{}
foreach ($result IN $g.Group) {
# if result indicates equal or "in Reference" set the reference side row. If we did that on a previous result keep it. Otherwise set to "blank"
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[$g.Name]
#position the key as the next field (only appears once)
$Hash[$key] = $g.Name
#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 by reference row number, and fill in any blanks in the difference-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" } }
#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 we had to put the equal rows back, take them out; sort, make sure all the columns are present in row 1 so the grid puts them in, and output
if ( $ExcludeDifferent) {$ExpandedDiff = $ExpandedDiff.where({$_.side -eq "=="}) | Sort-Object -Property "<row" ,">row" }
elseif ( $IncludeEqual) {$ExpandedDiff = $ExpandedDiff | Sort-Object -Property "<row" ,">row" }
else {$ExpandedDiff = $ExpandedDiff.where({$_.side -ne "=="}) | Sort-Object -Property "<row" ,">row" }
$ExpandedDiff | Update-FirstObjectProperties | Out-GridView -Title "Comparing $Referencefile::$worksheet1 (<=) with $Differencefile::$WorkSheet2 (=>)"
}
elseif ($GridView ) {Write-Warning -Message "To use -GridView you must specify -Key and it must match one of the included properties." }
elseif (-not $PassThru) {return ($diff | Select-Object -Property (@(@{n="_Side";e={$_.SideIndicator}},"_File" ,"_Sheet","_Row") + $propList))}
if ( $PassThru) {return $diff }
}

BIN
dashboard.xlsx Normal file

Binary file not shown.

View File

@@ -224,12 +224,4 @@ Function ColorCompletion{
}
}
if (Get-Command -Name register-argumentCompleter -ErrorAction SilentlyContinue) {
Register-ArgumentCompleter -CommandName Export-Excel -ParameterName TitleBackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName ForeGroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName DataBarColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Add-ConditionalFormatting -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Format -ParameterName FontColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Format -ParameterName BackgroundColor -ScriptBlock $Function:ColorCompletion
Register-ArgumentCompleter -CommandName Set-Format -ParameterName PatternColor -ScriptBlock $Function:ColorCompletion
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

BIN
test1.xlsx Normal file

Binary file not shown.

BIN
testTable.xlsx Normal file

Binary file not shown.