javascript - 使用 Apps 脚本识别未定义工作表中相同列中的重复值
问题描述
我有一个电子表格,其工作表的数量正在增长。我想突出显示所有未定义工作表中相同列中的重复值。我可以使用条件格式来做到这一点,但是随着工作表的数量会增加很多,这种方法是无效的。
我试过类似的东西:
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_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 中的值执行相同操作
- 手动删除一些突出显示的行
- 再次运行脚本
- 深呼吸,喝点茶
PS我不想在第一行中寻找重复项(固定标题)
如果有人可以帮助我解决这个我已经工作了三天但没有找到正确解决方案的问题,我将非常感激,我尝试了不同的方法,但我找不到理想的解决方案。
谢谢!</p>
解决方案
假设:
原始值来自最早的工作表,因此大多数副本可以在以后的工作表中找到,或者至少在第一张工作表的最后一行中找到。
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
}
}
}
});
})
});
}
我的数据:Sheet1:
COL1 | COL2 | COL3 | COL4 | COL5 | COL6 | COL7 | COL8 | COL9 | COL10 |
---|---|---|---|---|---|---|---|---|---|
字符串1 | 25 | 25 | 字符串1 | 3 | 2 | 24 | 0 | 1 | 字符串1 |
字符串2 | 9 | 22 | 字符串2 | 23 | 3 | 11 | 20 | 4 | 字符串2 |
字符串3 | 7 | 28 | 字符串3 | 19 | 19 | 22 | 7 | 3 | 字符串3 |
字符串4 | 10 | 21 | 字符串4 | 25 | 12 | 11 | 0 | 0 | 字符串4 |
字符串5 | 2 | 9 | 字符串5 | 7 | 6 | 29 | 15 | 4 | 字符串5 |
字符串6 | 21 | 26 | 字符串6 | 21 | 18 | 20 | 1 | 6 | 字符串6 |
字符串7 | 7 | 25 | 字符串7 | 3 | 21 | 5 | 28 | 29 | 字符串7 |
字符串8 | 17 | 2 | 字符串8 | 6 | 19 | 20 | 26 | 2 | 字符串8 |
字符串9 | 9 | 26 | 字符串9 | 12 | 21 | 20 | 19 | 18 | 字符串9 |
字符串4 | 9 | 26 | 字符串4 | 12 | 21 | 20 | 19 | 18 | 字符串4 |
9 | 26 | 12 | 21 | 20 | 19 | 18 |
表2:
COL1 | COL2 | COL3 | COL4 | COL5 | COL6 | COL7 | COL8 | COL9 | COL10 |
---|---|---|---|---|---|---|---|---|---|
字符串8 | 28 | 6 | 字符串8 | 14 | 28 | 0 | 9 | 29 | 字符串8 |
字符串9 | 13 | 24 | 字符串9 | 27 | 1 | 26 | 22 | 21 | 字符串9 |
字符串 10 | 20 | 17 | 字符串 10 | 9 | 26 | 10 | 24 | 16 | 字符串 10 |
字符串 11 | 12 | 24 | 字符串 11 | 17 | 28 | 17 | 29 | 24 | 字符串 11 |
字符串 12 | 18 | 27 | 字符串 12 | 4 | 23 | 6 | 12 | 11 | 字符串 12 |
字符串 13 | 8 | 29 | 字符串 13 | 21 | 18 | 1 | 24 | 7 | 字符串 13 |
字符串 14 | 8 | 21 | 字符串 14 | 14 | 29 | 2 | 7 | 19 | 字符串 14 |
字符串 15 | 23 | 5 | 字符串 15 | 2 | 20 | 8 | 8 | 9 | 字符串 15 |
字符串 16 | 1 | 12 | 字符串 16 | 22 | 23 | 19 | 5 | 27 | 字符串 16 |
1 | 12 | 22 | 23 | 19 | 5 | 27 |
表 3:
COL1 | COL2 | COL3 | COL4 | COL5 | COL6 | COL7 | COL8 | COL9 | COL10 |
---|---|---|---|---|---|---|---|---|---|
字符串 15 | 21 | 14 | 字符串 15 | 27 | 26 | 3 | 23 | 24 | 字符串 15 |
字符串 16 | 10 | 25 | 字符串 16 | 22 | 6 | 20 | 25 | 21 | 字符串 16 |
字符串 17 | 3 | 10 | 字符串 17 | 6 | 18 | 28 | 3 | 10 | 字符串 17 |
字符串 18 | 19 | 20 | 字符串 18 | 6 | 25 | 1 | 11 | 16 | 字符串 18 |
字符串 19 | 5 | 0 | 字符串 19 | 14 | 29 | 27 | 2 | 21 | 字符串 19 |
字符串20 | 8 | 8 | 字符串20 | 13 | 9 | 27 | 13 | 5 | 字符串20 |
字符串 21 | 18 | 6 | 字符串 21 | 1 | 24 | 22 | 3 | 7 | 字符串 21 |
字符串22 | 6 | 4 | 字符串22 | 26 | 5 | 25 | 5 | 4 | 字符串22 |
字符串23 | 5 | 28 | 字符串23 | 6 | 7 | 17 | 19 | 25 | 字符串23 |
5 | 28 | 6 | 7 | 17 | 19 | 25 |
我正在做其他事情,我以这个问题为例,这种方式工作得很好,你可以通过将它们添加到适当的数组来更改它工作的工作表和你想要包含的列。我
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
}
}
});
})
});
}
推荐阅读
- c# - Linq 更新加入
- javascript - 使用脚本关闭 hta 文件
- python - 为什么 Python 的 Event.wait() 会被某些系统上的信号打断,而其他系统上却不能?
- python - 为除数创建工具
- angular - Angular 6 / Rxjs - 如何基础:observables 成功,错误,最后
- javascript - 我们可以通过本地服务器将 chrome 应用程序代码简单地作为 Web 应用程序运行吗?
- python-3.x - 指示行的日期时间是否在日期范围内
- java - Spring Boot 从异常处理程序返回 401 状态自定义对象
- sql - 将 From 语句添加到 Join 语句
- ruby-on-rails - 如何使用 ps_search gem for Rails 5 仅搜索已批准的帖子