使用 Apps 脚本识别未定义工作表中相同列中的重复值
Identify duplicated values in the same columns across undefined sheets using Apps Script
我有一个价差sheet undefined sheet。
我想 突出显示相同列 中所有未定义 sheets 中的重复值 。
我可以用条件格式来做到这一点,但是随着 sheet 的数量会增长很多,这种方法是无效的。
我试过类似的方法:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
function readSheetData() {
var rowRange = sheet.getRange(1, 1, lastRow, lastColumn);
var rangeArray = rowRange.getValues();
// Convert to a one dimensional array
rangeArray = [].concat.apply([], rangeArray);
return rangeArray;
}
// Creates an array with data from a chosen column
function readColumnData(column) {
var columnRange = sheet.getRange(1, column, lastRow);
var rangeArray = columnRange.getValues();
// Convert to one dimensional array
rangeArray = [].concat.apply([], rangeArray);
return rangeArray;
}
// Creates an array with data from a chosen row
function readRowData(row) {
var rowRange = sheet.getRange(row, 1, 1, lastColumn);
var rangeArray = rowRange.getValues();
// Convert to one dimensional array
rangeArray = [].concat.apply([], rangeArray);
Logger.log(rangeArray);
return rangeArray;
}
// Sort data and find duplicates
function findDuplicates(data) {
var sortedData = data.slice().sort();
var duplicates = [];
for (var i = 0; i < sortedData.length - 1; i++) {
if (sortedData[i + 1] == sortedData[i] && sortedData[i] != "") {
duplicates.push(sortedData[i]);
}
}
return duplicates;
}
// Find locations of all duplicates
function getIndexes(data, duplicates) {
var column = 2;
var indexes = [];
i = -1;
// Loop through duplicates to find their indexes
for (var n = 0; n < duplicates.length; n++) {
while ((i = data.indexOf(duplicates[n], i + 1)) != -1) {
indexes.push(i);
}
}
return indexes;
}
// Highlight all instances of duplicate values in a sheet
function highlightSheetDuplicates(indexes) {
var row;
for (n = 0; n < indexes.length; n++) {
row = 1;
if (indexes[n] > lastColumn) {
row = Math.floor(indexes[n] / lastColumn);
indexes[n] = indexes[n] - lastColumn * row;
row++;
}
sheet.getRange(row, indexes[n] + 1).setBackground("red");
}
}
// Highlight all instances of duplicate values in a column
function highlightColumnDuplicates(column, indexes) {
for (n = 0; n < indexes.length; n++) {
sheet.getRange(indexes[n] + 1, column).setBackground("red");
}
}
// Highlight all instances of duplicate values in a row
function highlightRowDuplicates(row, indexes) {
for (n = 0; n < indexes.length; n++) {
sheet.getRange(row, indexes[n] + 1).setBackground("red");
}
}
//----------- Main -------------
function sheetMain() {
var data = readSheetData();
var duplicates = findDuplicates(data);
var indexes = getIndexes(data, duplicates);
highlightSheetDuplicates(indexes);
}
function columnMain(column) {
var data = readColumnData(column);
var duplicates = findDuplicates(data);
var indexes = getIndexes(data, duplicates);
highlightColumnDuplicates(column, indexes);
}
function rowMain(row) {
var data = readRowData(row);
var duplicates = findDuplicates(data);
var indexes = getIndexes(data, duplicates);
highlightRowDuplicates(row, indexes);
}
// ---------- Menu ----------
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('DUPLICATED')
.addItem('Sheet', 'sheetMain')
.addItem('Row', 'showRowPrompt')
.addItem('Column', 'showColumnPrompt')
.addToUi();
}
// ---------- Prompt ----------
function showColumnPrompt() {
var ui = SpreadsheetApp.getUi();
var response = ui.prompt(
'Find Duplicates',
'Enter letter of column to search:',
ui.ButtonSet.OK_CANCEL);
// Get user response, run main
var button = response.getSelectedButton();
var text = response.getResponseText();
if (button == ui.Button.OK) {
text = sheet.getRange(text + "1");
text = text.getColumn();
columnMain(text);
}
}
function showRowPrompt() {
var ui = SpreadsheetApp.getUi();
var response = ui.prompt(
'Find Duplicates',
'Enter number of row to search:',
ui.ButtonSet.OK_CANCEL);
// Get user response, run main
var button = response.getSelectedButton();
var text = response.getResponseText();
if (button == ui.Button.OK) {
rowMain(text);
}
}
但它只适用于 活动 sheet 的 columns/rows。
它还启用了一个非常有用的触发菜单。
预期结果:
- 运行 菜单中的脚本
- 在Sheet_1 A:A, Sheet_2 A:A, Sheet_3 A:A...值。
- 对 Sheet_1 D:D、Sheet_2 D:D、Sheet_3 D:D
中的值执行相同操作
- 对 Sheet_1 J:J、Sheet_2 J:J、Sheet_3 J:J
中的值执行相同操作
- 手动删除一些突出显示的行
- 运行 再次编写脚本
- 深呼吸,喝点茶
P.S。我不想在第一行中查找重复项(已修复 header)
我会超级感激如果有人能帮我解决这个问题,我已经工作了三天没有找到正确的解决方案,我尝试了不同的方法,但我找不到理想的解决方案。
谢谢!
假设:
原始值来自最早的 sheets,因此大多数副本在后来的 sheets 中找到,或者至少在第一个 sheet.
中找到最后一行
function findDuplicateInMultipleColumnsAndMultipleSheets() {
const names = ['Sheet1', 'Sheet2', 'Sheet3'];//sheet names included
const colors= ['#ff0000','#ffff00','#00ffff'];//added different colors for each column
const cols = [1, 4, 10];//columns
const ss = SpreadsheetApp.getActive();
const shts = ss.getSheets().filter(s => { return ~names.indexOf(s.getName()) });//only gets the sheets with names in the names array
let uA = new Array(cols.length);//create unique array for all three cols
cols.forEach((c, i) => {
uA[i]= new Array(1);
shts.forEach((sh, j) => {
let vs = sh.getRange(2, c, sh.getLastRow() - 1, 1).getValues().flat();
vs.forEach((v, k) => {
if (!~uA[i].indexOf(v)) {
uA[i].push(v);//unique array for each column for all three sheets
}
});
})
let obj={};//used to record first matches which are assumed to be originals
shts.forEach((sh,j)=>{
let vs = sh.getRange(2, c, sh.getLastRow() - 1, 1).getValues().flat();
vs.forEach((e, k) => {
if (~uA[i].indexOf(e)) {
if(!obj.hasOwnProperty(e)) {
obj[e]=1;//first one gets recorded
} else {
if(e) {
sh.getRange(k+2,c).setBackground(colors[i]);//copies get background changed if they're not blank
}
}
}
});
})
});
}
我的数据:
工作表 1:
COL1
COL2
COL3
COL4
COL5
COL6
COL7
COL8
COL9
COL10
string1
25
25
string1
3
2
24
0
1
string1
string2
9
22
string2
23
3
11
20
4
string2
string3
7
28
string3
19
19
22
7
3
string3
string4
10
21
string4
25
12
11
0
0
string4
string5
2
9
string5
7
6
29
15
4
string5
string6
21
26
string6
21
18
20
1
6
string6
string7
7
25
string7
3
21
5
28
29
string7
string8
17
2
string8
6
19
20
26
2
string8
string9
9
26
string9
12
21
20
19
18
string9
string4
9
26
string4
12
21
20
19
18
string4
9
26
12
21
20
19
18
工作表 2:
COL1
COL2
COL3
COL4
COL5
COL6
COL7
COL8
COL9
COL10
string8
28
6
string8
14
28
0
9
29
string8
string9
13
24
string9
27
1
26
22
21
string9
string10
20
17
string10
9
26
10
24
16
string10
string11
12
24
string11
17
28
17
29
24
string11
string12
18
27
string12
4
23
6
12
11
string12
string13
8
29
string13
21
18
1
24
7
string13
string14
8
21
string14
14
29
2
7
19
string14
string15
23
5
string15
2
20
8
8
9
string15
string16
1
12
string16
22
23
19
5
27
string16
1
12
22
23
19
5
27
工作表 3:
COL1
COL2
COL3
COL4
COL5
COL6
COL7
COL8
COL9
COL10
string15
21
14
string15
27
26
3
23
24
string15
string16
10
25
string16
22
6
20
25
21
string16
string17
3
10
string17
6
18
28
3
10
string17
string18
19
20
string18
6
25
1
11
16
string18
string19
5
0
string19
14
29
27
2
21
string19
string20
8
8
string20
13
9
27
13
5
string20
string21
18
6
string21
1
24
22
3
7
string21
string22
6
4
string22
26
5
25
5
4
string22
string23
5
28
string23
6
7
17
19
25
string23
5
28
6
7
17
19
25
我正在做其他事情,我用这个问题作为例子,这种方法工作得很好,你可以改变它工作的 sheets 和你想包含的列将它们添加到适当的数组中。我
function getCopyData() {
const ss = SpreadsheetApp.getActive();
const names = ['Sheet1', 'Sheet2', 'Sheet3'];//sheetames to include
const colors = ['#ffff00', '#ff00ff', '#00ffff'];//colors to use
const cols = [1, 4, 10];columns to check you can add more
const shts = ss.getSheets().filter(s => names.includes(s.getName()));
const vals = shts.map(s => s.getRange(2, 1, s.getLastRow() - 1, s.getLastColumn()).getValues()).map(a => { return a.map(r => { let row = []; cols.forEach((c, i) => { row.push(r[c - 1]) }); return row; }) });//only gets values for columns that you want
let uA = Array.from(cols, u => ({}));//create an array of independent objects
vals.forEach((a, i) => {
a.forEach((r, j) => {
cols.forEach((c, k) => {
if (r[k]) {//doesn't do anything if value is empty
if (!uA[k].hasOwnProperty(r[k])) {//checks to see if this value has already been found
uA[k][r[k]] = 0;//if it hasn't been found then it's the original
} else {
uA[k][r[k]] += 1;//if it has then it's a copy
shts[i].getRange(j + 2, c).setBackground(colors[k%colors.length]);//sets the background color for copies
}
}
});
})
});
}
我有一个价差sheet undefined sheet。 我想 突出显示相同列 中所有未定义 sheets 中的重复值 。 我可以用条件格式来做到这一点,但是随着 sheet 的数量会增长很多,这种方法是无效的。
我试过类似的方法:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
function readSheetData() {
var rowRange = sheet.getRange(1, 1, lastRow, lastColumn);
var rangeArray = rowRange.getValues();
// Convert to a one dimensional array
rangeArray = [].concat.apply([], rangeArray);
return rangeArray;
}
// Creates an array with data from a chosen column
function readColumnData(column) {
var columnRange = sheet.getRange(1, column, lastRow);
var rangeArray = columnRange.getValues();
// Convert to one dimensional array
rangeArray = [].concat.apply([], rangeArray);
return rangeArray;
}
// Creates an array with data from a chosen row
function readRowData(row) {
var rowRange = sheet.getRange(row, 1, 1, lastColumn);
var rangeArray = rowRange.getValues();
// Convert to one dimensional array
rangeArray = [].concat.apply([], rangeArray);
Logger.log(rangeArray);
return rangeArray;
}
// Sort data and find duplicates
function findDuplicates(data) {
var sortedData = data.slice().sort();
var duplicates = [];
for (var i = 0; i < sortedData.length - 1; i++) {
if (sortedData[i + 1] == sortedData[i] && sortedData[i] != "") {
duplicates.push(sortedData[i]);
}
}
return duplicates;
}
// Find locations of all duplicates
function getIndexes(data, duplicates) {
var column = 2;
var indexes = [];
i = -1;
// Loop through duplicates to find their indexes
for (var n = 0; n < duplicates.length; n++) {
while ((i = data.indexOf(duplicates[n], i + 1)) != -1) {
indexes.push(i);
}
}
return indexes;
}
// Highlight all instances of duplicate values in a sheet
function highlightSheetDuplicates(indexes) {
var row;
for (n = 0; n < indexes.length; n++) {
row = 1;
if (indexes[n] > lastColumn) {
row = Math.floor(indexes[n] / lastColumn);
indexes[n] = indexes[n] - lastColumn * row;
row++;
}
sheet.getRange(row, indexes[n] + 1).setBackground("red");
}
}
// Highlight all instances of duplicate values in a column
function highlightColumnDuplicates(column, indexes) {
for (n = 0; n < indexes.length; n++) {
sheet.getRange(indexes[n] + 1, column).setBackground("red");
}
}
// Highlight all instances of duplicate values in a row
function highlightRowDuplicates(row, indexes) {
for (n = 0; n < indexes.length; n++) {
sheet.getRange(row, indexes[n] + 1).setBackground("red");
}
}
//----------- Main -------------
function sheetMain() {
var data = readSheetData();
var duplicates = findDuplicates(data);
var indexes = getIndexes(data, duplicates);
highlightSheetDuplicates(indexes);
}
function columnMain(column) {
var data = readColumnData(column);
var duplicates = findDuplicates(data);
var indexes = getIndexes(data, duplicates);
highlightColumnDuplicates(column, indexes);
}
function rowMain(row) {
var data = readRowData(row);
var duplicates = findDuplicates(data);
var indexes = getIndexes(data, duplicates);
highlightRowDuplicates(row, indexes);
}
// ---------- Menu ----------
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('DUPLICATED')
.addItem('Sheet', 'sheetMain')
.addItem('Row', 'showRowPrompt')
.addItem('Column', 'showColumnPrompt')
.addToUi();
}
// ---------- Prompt ----------
function showColumnPrompt() {
var ui = SpreadsheetApp.getUi();
var response = ui.prompt(
'Find Duplicates',
'Enter letter of column to search:',
ui.ButtonSet.OK_CANCEL);
// Get user response, run main
var button = response.getSelectedButton();
var text = response.getResponseText();
if (button == ui.Button.OK) {
text = sheet.getRange(text + "1");
text = text.getColumn();
columnMain(text);
}
}
function showRowPrompt() {
var ui = SpreadsheetApp.getUi();
var response = ui.prompt(
'Find Duplicates',
'Enter number of row to search:',
ui.ButtonSet.OK_CANCEL);
// Get user response, run main
var button = response.getSelectedButton();
var text = response.getResponseText();
if (button == ui.Button.OK) {
rowMain(text);
}
}
但它只适用于 活动 sheet 的 columns/rows。 它还启用了一个非常有用的触发菜单。
预期结果:
- 运行 菜单中的脚本
- 在Sheet_1 A:A, Sheet_2 A:A, Sheet_3 A:A...值。
- 对 Sheet_1 D:D、Sheet_2 D:D、Sheet_3 D:D 中的值执行相同操作
- 对 Sheet_1 J:J、Sheet_2 J:J、Sheet_3 J:J 中的值执行相同操作
- 手动删除一些突出显示的行
- 运行 再次编写脚本
- 深呼吸,喝点茶
P.S。我不想在第一行中查找重复项(已修复 header)
我会超级感激如果有人能帮我解决这个问题,我已经工作了三天没有找到正确的解决方案,我尝试了不同的方法,但我找不到理想的解决方案。
谢谢!
假设:
原始值来自最早的 sheets,因此大多数副本在后来的 sheets 中找到,或者至少在第一个 sheet.
function findDuplicateInMultipleColumnsAndMultipleSheets() {
const names = ['Sheet1', 'Sheet2', 'Sheet3'];//sheet names included
const colors= ['#ff0000','#ffff00','#00ffff'];//added different colors for each column
const cols = [1, 4, 10];//columns
const ss = SpreadsheetApp.getActive();
const shts = ss.getSheets().filter(s => { return ~names.indexOf(s.getName()) });//only gets the sheets with names in the names array
let uA = new Array(cols.length);//create unique array for all three cols
cols.forEach((c, i) => {
uA[i]= new Array(1);
shts.forEach((sh, j) => {
let vs = sh.getRange(2, c, sh.getLastRow() - 1, 1).getValues().flat();
vs.forEach((v, k) => {
if (!~uA[i].indexOf(v)) {
uA[i].push(v);//unique array for each column for all three sheets
}
});
})
let obj={};//used to record first matches which are assumed to be originals
shts.forEach((sh,j)=>{
let vs = sh.getRange(2, c, sh.getLastRow() - 1, 1).getValues().flat();
vs.forEach((e, k) => {
if (~uA[i].indexOf(e)) {
if(!obj.hasOwnProperty(e)) {
obj[e]=1;//first one gets recorded
} else {
if(e) {
sh.getRange(k+2,c).setBackground(colors[i]);//copies get background changed if they're not blank
}
}
}
});
})
});
}
我的数据: 工作表 1:
COL1 | COL2 | COL3 | COL4 | COL5 | COL6 | COL7 | COL8 | COL9 | COL10 |
---|---|---|---|---|---|---|---|---|---|
string1 | 25 | 25 | string1 | 3 | 2 | 24 | 0 | 1 | string1 |
string2 | 9 | 22 | string2 | 23 | 3 | 11 | 20 | 4 | string2 |
string3 | 7 | 28 | string3 | 19 | 19 | 22 | 7 | 3 | string3 |
string4 | 10 | 21 | string4 | 25 | 12 | 11 | 0 | 0 | string4 |
string5 | 2 | 9 | string5 | 7 | 6 | 29 | 15 | 4 | string5 |
string6 | 21 | 26 | string6 | 21 | 18 | 20 | 1 | 6 | string6 |
string7 | 7 | 25 | string7 | 3 | 21 | 5 | 28 | 29 | string7 |
string8 | 17 | 2 | string8 | 6 | 19 | 20 | 26 | 2 | string8 |
string9 | 9 | 26 | string9 | 12 | 21 | 20 | 19 | 18 | string9 |
string4 | 9 | 26 | string4 | 12 | 21 | 20 | 19 | 18 | string4 |
9 | 26 | 12 | 21 | 20 | 19 | 18 |
工作表 2:
COL1 | COL2 | COL3 | COL4 | COL5 | COL6 | COL7 | COL8 | COL9 | COL10 |
---|---|---|---|---|---|---|---|---|---|
string8 | 28 | 6 | string8 | 14 | 28 | 0 | 9 | 29 | string8 |
string9 | 13 | 24 | string9 | 27 | 1 | 26 | 22 | 21 | string9 |
string10 | 20 | 17 | string10 | 9 | 26 | 10 | 24 | 16 | string10 |
string11 | 12 | 24 | string11 | 17 | 28 | 17 | 29 | 24 | string11 |
string12 | 18 | 27 | string12 | 4 | 23 | 6 | 12 | 11 | string12 |
string13 | 8 | 29 | string13 | 21 | 18 | 1 | 24 | 7 | string13 |
string14 | 8 | 21 | string14 | 14 | 29 | 2 | 7 | 19 | string14 |
string15 | 23 | 5 | string15 | 2 | 20 | 8 | 8 | 9 | string15 |
string16 | 1 | 12 | string16 | 22 | 23 | 19 | 5 | 27 | string16 |
1 | 12 | 22 | 23 | 19 | 5 | 27 |
工作表 3:
COL1 | COL2 | COL3 | COL4 | COL5 | COL6 | COL7 | COL8 | COL9 | COL10 |
---|---|---|---|---|---|---|---|---|---|
string15 | 21 | 14 | string15 | 27 | 26 | 3 | 23 | 24 | string15 |
string16 | 10 | 25 | string16 | 22 | 6 | 20 | 25 | 21 | string16 |
string17 | 3 | 10 | string17 | 6 | 18 | 28 | 3 | 10 | string17 |
string18 | 19 | 20 | string18 | 6 | 25 | 1 | 11 | 16 | string18 |
string19 | 5 | 0 | string19 | 14 | 29 | 27 | 2 | 21 | string19 |
string20 | 8 | 8 | string20 | 13 | 9 | 27 | 13 | 5 | string20 |
string21 | 18 | 6 | string21 | 1 | 24 | 22 | 3 | 7 | string21 |
string22 | 6 | 4 | string22 | 26 | 5 | 25 | 5 | 4 | string22 |
string23 | 5 | 28 | string23 | 6 | 7 | 17 | 19 | 25 | string23 |
5 | 28 | 6 | 7 | 17 | 19 | 25 |
我正在做其他事情,我用这个问题作为例子,这种方法工作得很好,你可以改变它工作的 sheets 和你想包含的列将它们添加到适当的数组中。我
function getCopyData() {
const ss = SpreadsheetApp.getActive();
const names = ['Sheet1', 'Sheet2', 'Sheet3'];//sheetames to include
const colors = ['#ffff00', '#ff00ff', '#00ffff'];//colors to use
const cols = [1, 4, 10];columns to check you can add more
const shts = ss.getSheets().filter(s => names.includes(s.getName()));
const vals = shts.map(s => s.getRange(2, 1, s.getLastRow() - 1, s.getLastColumn()).getValues()).map(a => { return a.map(r => { let row = []; cols.forEach((c, i) => { row.push(r[c - 1]) }); return row; }) });//only gets values for columns that you want
let uA = Array.from(cols, u => ({}));//create an array of independent objects
vals.forEach((a, i) => {
a.forEach((r, j) => {
cols.forEach((c, k) => {
if (r[k]) {//doesn't do anything if value is empty
if (!uA[k].hasOwnProperty(r[k])) {//checks to see if this value has already been found
uA[k][r[k]] = 0;//if it hasn't been found then it's the original
} else {
uA[k][r[k]] += 1;//if it has then it's a copy
shts[i].getRange(j + 2, c).setBackground(colors[k%colors.length]);//sets the background color for copies
}
}
});
})
});
}