如何以编程方式在多个工作表之间的 Excel 单元格中查找重复值
How to find duplicate values in Excel cells between multiple sheets programmatically
例如,我有一个名为 EmployeeSheet 的 sheet,它只是公司中每位员工姓名的一列。并且假设此列表格式正确并且没有重复项,因此此 sheet 中的每个单元格都是唯一的。
现在我公司每个部门都有一个sheet,比如FinanceSheet、ITSheet、销售表。每个 sheet 在某处(因为每个 sheet 没有相同的布局)每个部门的员工列表。但是,任何 1 个员工姓名 应该 只在所有部门 sheet 之间出现一次(这不包括 EmployeeSheet)。
这是我能想到但不知道如何实现的解决方案,那就是制作一个多维数组(在学校学过一点点,但依稀记得如何使用)。
伪代码类似于:
arrEmployees = {"Tom Hanks", "Burt Reynolds", "Your Mom"}
arrFinance = {"Tom Hanks"}
arrIT = {"Burt Reynolds"}
arrSales = {"Your Mom"}
arrSheets = {arrEmployees, arrFinance, arrIT, arrSales}
虽然我已经能够通过使用
获取单个单元格值和范围作为字符串
Sheets shts = app.Worksheets;
Worksheet ws = (Worksheet)sheets.get_Item("EmployeeSheet");
Excel.Range empRange = (Excel.Range)worksheet.get_range("B2");
string empVal = empRange.Value2.ToString();
但是通过将单个单元格值获取到字符串的过程,我不知道如何将其放入数组的元素中,更不用说值的范围了。
我确信我的方法不是最有效的,甚至可能不可能,但这就是我来这里寻求帮助的原因,所以任何提示都将受到赞赏。
编辑:这是最终为我工作的解决方案。感谢 Ian Edwards 解决方案。
Dictionary<string, List<Point>> fields = new Dictionary<string, List<Point>>();
fields["Finance"] = new List<Point>() { new Point(2,20)};
fields["Sales"] = new List<Point>();
for (int row = 5; row <= 185; row += 20) {fields["Sales"].Add(new Point(2,row));}
List<string> names = new List<string>();
List<string> duplicates = new List<string>();
foreach (KeyValuePair<string, List<Point>> kp in fields)
{
Excel.Worksheet xlSheet = (Excel.Worksheet)workbook.Worksheets[kp.Key];
foreach (Point p in kp.Value)
{
if ((xlSheet.Cells[p.Y, p.X] as Excel.Range.Value != null)
{
string cellVal = ((xlSheet.Cells[p.Y,p.X] as Excel.Range).Value).ToString();
if (!names.Contains(cellVal))
{ names.Add(cellVal)) }
else { duplicates.Add(cellVal); } } } }
- 您可以使用
.Range
定义多个单元格(即.Range["A1", "F500"]
)
https://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.worksheet.range.aspx
- 然后您可以使用
.get_Value
获取该范围内所有单元格的 contents/values。根据 dotnetperls.com get_Value() is much faster than get_Range()(参见 'Performance' 部分)。使用多个范围 + get_value 的组合 肯定会 比使用 get_range. 的许多单一范围调用表现更好
我将它们存储在 Object Array.
(object[,])yourexcelRange.get_Value(Excel.XlRangeValueDataType.xlRangeValueDefault);
从那里您可以编写自己的比较方法来比较多个数组。一个怪癖是这样做 returns 一个索引为 1 的数组,而不是标准的基于 0 的索引。
这是我拼凑的一个小例子 - 评论应该逐行解释发生了什么。
您可以声明要检查姓名的工作表的名称,以及在 'worksheets' 字典中从何处开始查找姓名。
我假设您不知道每个列表中有多少个名字 - 它会继续向下移动每个列表,直到遇到空白单元格。
// Load the Excel app
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
// Open the workbook
var xlWorkbook = xlApp.Workbooks.Open("XLTEST.xlsx");
// Delcare the sheets and locations to look for names
Dictionary<string, Tuple<int, int>> worksheets = new Dictionary<string, Tuple<int, int>>()
{
// Declare the name of the sheets to look in and the 1 base X,Y index of where to start looking for names on each sheet (i.e. 1,1, = A1)
{ "Sheet1", new Tuple<int, int>(1, 1) },
{ "Sheet2", new Tuple<int, int>(2, 3) },
{ "Sheet3", new Tuple<int, int>(4, 5) },
{ "Sheet4", new Tuple<int, int>(2, 3) },
};
// List to keep track of all names in all sheets
List<string> names = new List<string>();
// Iterate over every sheet we need to look at
foreach(var worksheet in worksheets)
{
string workSheetName = worksheet.Key;
// Get this excel worksheet object
var xlWorksheet = (Microsoft.Office.Interop.Excel.Worksheet)xlWorkbook.Worksheets[workSheetName];
// Get the 1 based X,Y cell index
int row = worksheet.Value.Item1;
int column = worksheet.Value.Item2;
// Get the string contained in this cell
string name = (string)(xlWorksheet.Cells[row, column] as Microsoft.Office.Interop.Excel.Range).Value;
// name is null when the cell is empty - stop looking in this sheet and move on to the next one
while(name != null)
{
// Add the current name to the list
names.Add(name);
// Get the next name in the cell below this one
name = (string)(xlWorksheet.Cells[++row, column] as Microsoft.Office.Interop.Excel.Range).Value;
}
}
// Compare the number of names to the number of unique names
if (names.Count() != names.Distinct().Count())
{
// You have duplicate names!
}
例如,我有一个名为 EmployeeSheet 的 sheet,它只是公司中每位员工姓名的一列。并且假设此列表格式正确并且没有重复项,因此此 sheet 中的每个单元格都是唯一的。
现在我公司每个部门都有一个sheet,比如FinanceSheet、ITSheet、销售表。每个 sheet 在某处(因为每个 sheet 没有相同的布局)每个部门的员工列表。但是,任何 1 个员工姓名 应该 只在所有部门 sheet 之间出现一次(这不包括 EmployeeSheet)。
这是我能想到但不知道如何实现的解决方案,那就是制作一个多维数组(在学校学过一点点,但依稀记得如何使用)。
伪代码类似于:
arrEmployees = {"Tom Hanks", "Burt Reynolds", "Your Mom"}
arrFinance = {"Tom Hanks"}
arrIT = {"Burt Reynolds"}
arrSales = {"Your Mom"}
arrSheets = {arrEmployees, arrFinance, arrIT, arrSales}
虽然我已经能够通过使用
获取单个单元格值和范围作为字符串Sheets shts = app.Worksheets;
Worksheet ws = (Worksheet)sheets.get_Item("EmployeeSheet");
Excel.Range empRange = (Excel.Range)worksheet.get_range("B2");
string empVal = empRange.Value2.ToString();
但是通过将单个单元格值获取到字符串的过程,我不知道如何将其放入数组的元素中,更不用说值的范围了。
我确信我的方法不是最有效的,甚至可能不可能,但这就是我来这里寻求帮助的原因,所以任何提示都将受到赞赏。
编辑:这是最终为我工作的解决方案。感谢 Ian Edwards 解决方案。
Dictionary<string, List<Point>> fields = new Dictionary<string, List<Point>>();
fields["Finance"] = new List<Point>() { new Point(2,20)};
fields["Sales"] = new List<Point>();
for (int row = 5; row <= 185; row += 20) {fields["Sales"].Add(new Point(2,row));}
List<string> names = new List<string>();
List<string> duplicates = new List<string>();
foreach (KeyValuePair<string, List<Point>> kp in fields)
{
Excel.Worksheet xlSheet = (Excel.Worksheet)workbook.Worksheets[kp.Key];
foreach (Point p in kp.Value)
{
if ((xlSheet.Cells[p.Y, p.X] as Excel.Range.Value != null)
{
string cellVal = ((xlSheet.Cells[p.Y,p.X] as Excel.Range).Value).ToString();
if (!names.Contains(cellVal))
{ names.Add(cellVal)) }
else { duplicates.Add(cellVal); } } } }
- 您可以使用
.Range
定义多个单元格(即.Range["A1", "F500"]
)
https://msdn.microsoft.com/en-us/library/microsoft.office.tools.excel.worksheet.range.aspx
- 然后您可以使用
.get_Value
获取该范围内所有单元格的 contents/values。根据 dotnetperls.com get_Value() is much faster than get_Range()(参见 'Performance' 部分)。使用多个范围 + get_value 的组合 肯定会 比使用 get_range. 的许多单一范围调用表现更好
我将它们存储在 Object Array.
(object[,])yourexcelRange.get_Value(Excel.XlRangeValueDataType.xlRangeValueDefault);
从那里您可以编写自己的比较方法来比较多个数组。一个怪癖是这样做 returns 一个索引为 1 的数组,而不是标准的基于 0 的索引。
这是我拼凑的一个小例子 - 评论应该逐行解释发生了什么。
您可以声明要检查姓名的工作表的名称,以及在 'worksheets' 字典中从何处开始查找姓名。
我假设您不知道每个列表中有多少个名字 - 它会继续向下移动每个列表,直到遇到空白单元格。
// Load the Excel app
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
// Open the workbook
var xlWorkbook = xlApp.Workbooks.Open("XLTEST.xlsx");
// Delcare the sheets and locations to look for names
Dictionary<string, Tuple<int, int>> worksheets = new Dictionary<string, Tuple<int, int>>()
{
// Declare the name of the sheets to look in and the 1 base X,Y index of where to start looking for names on each sheet (i.e. 1,1, = A1)
{ "Sheet1", new Tuple<int, int>(1, 1) },
{ "Sheet2", new Tuple<int, int>(2, 3) },
{ "Sheet3", new Tuple<int, int>(4, 5) },
{ "Sheet4", new Tuple<int, int>(2, 3) },
};
// List to keep track of all names in all sheets
List<string> names = new List<string>();
// Iterate over every sheet we need to look at
foreach(var worksheet in worksheets)
{
string workSheetName = worksheet.Key;
// Get this excel worksheet object
var xlWorksheet = (Microsoft.Office.Interop.Excel.Worksheet)xlWorkbook.Worksheets[workSheetName];
// Get the 1 based X,Y cell index
int row = worksheet.Value.Item1;
int column = worksheet.Value.Item2;
// Get the string contained in this cell
string name = (string)(xlWorksheet.Cells[row, column] as Microsoft.Office.Interop.Excel.Range).Value;
// name is null when the cell is empty - stop looking in this sheet and move on to the next one
while(name != null)
{
// Add the current name to the list
names.Add(name);
// Get the next name in the cell below this one
name = (string)(xlWorksheet.Cells[++row, column] as Microsoft.Office.Interop.Excel.Range).Value;
}
}
// Compare the number of names to the number of unique names
if (names.Count() != names.Distinct().Count())
{
// You have duplicate names!
}