Good morning,
I am using the following script right now to get the following situation: For a lead list which is dynamic so constantly moves, we want to extract certain information if they fall in a specific category. "C-Grade"
Essentially all data from all "C Grade" rated people, need to be copied from "Data info" onto "Lead info"
Normally I would use =vlookup
but since its dynamic and constantly changing, I don't see the option to use a formula.
App Script I am using right now:
function copyInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Data info");
var pasteSheet = ss.getSheetByName("Lead info");
// get source range
var source = copySheet.getRange(1,12);
// get destination range
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,2,12,2);
// copy values to destination range
source.copyTo(destination);
// clear source values
source.clearContent();
}
Here is the example file: Example file
I believe your goal is as follows.
You want to copy the rows which have the value of C Grade in the column "D" of "Data info" sheet to "Lead info" sheet.
In this case, how about the following modification?
Modified script:
function copyInfo() {
var check = "C Grade"; // This is from your question.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Data info");
var pasteSheet = ss.getSheetByName("Lead info");
var values = copySheet.getRange("A2:D" + copySheet.getLastRow()).getValues().filter(r => r[3] == check);
pasteSheet.getRange(pasteSheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}
When I saw your script, the value of "L1" of "Data info" is copied to "B1:C12" of "Lead info". When I saw your provided Spreadsheet, "L1" of "Data info" has no value.
In this modification, the values of "Data info" are retrieved and filtered by "C Grade" of the column "D", and the filtered values are put to "Lead info" sheet.
Reference:
filter()
Added 1:
When I saw your provided sample Spreadsheet again, I noticed that your script has been changed. And, I noticed that 2 points are different from your initial sample Spreadsheet.
In your initial Spreadsheet, "ABC Grade" was column "D". But, in your current Spreadsheet, that is colum "E".
In my proposed script, I proposed var values = copySheet.getRange("A2:D" + copySheet.getLastRow()).getValues().filter(r => r[3] == check);. But in your current script, that is changed to var values = copySheet.getRange("A1:D10" + copySheet.getLastRow()).getValues().filter(r => r[0] == check);/
I think that the reason for your current issue is that you changed your initial Spreadsheet and my proposed script. When your current Spreadsheet is used, the sample script is as follows.
Sample script:
function copyInfo() {
var check = "C Grade"; // This is from your question.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Data info");
var pasteSheet = ss.getSheetByName("Lead info");
var values = copySheet.getRange("A1:E" + copySheet.getLastRow()).getValues().filter(r => r[4] == check);
if (values.length == 0) return;
pasteSheet.getRange(pasteSheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}
When values has no value, an error occurs. So, I added if (values.length == 0) return;.
Added 2:
From your following new 2nd question,
Would there also be a way to only get certain colums. In this case A,B,D & E.
In this case, how about the following sample script?
Sample script:
function copyInfo() {
var check = "C Grade"; // This is from your question.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Data info");
var pasteSheet = ss.getSheetByName("Lead info");
var values = copySheet.getRange("A1:E" + copySheet.getLastRow()).getValues().filter(r => r[4] == check).map(r => [1, 2, 4, 5].map(e => r[e - 1]));
if (values.length == 0) return;
pasteSheet.getRange(pasteSheet.getLastRow() + 1, 1, values.length, values[0].length).setValues(values);
}
Related
I am very new to Google Appscript and support of this community will really be appreciated. Using different posts on the Stakeoverflow community I had designed an appscript to copy and paste the data from one worksheet to another and this is working fine (the data of worksheet named Copy is paste in worksheet named Paste). I had also created a worksheet named Trigger and in that I had created a dropdown in cell A1, now I want my script to run every time when the dropdonw in this sheet is changed. I had checked the different solutions on this community but as am new to appscirpt could not design a perfect solution to my case. Below is the link to the sheet and my current script.
https://docs.google.com/spreadsheets/d/1xy5eN8_PHXi9RPWK_EKg99dylaDjQ9lcilrSW0GP4B0/edit#gid=0
function Referesh() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
var source = copySheet.getRange(2,1,copySheet.getLastRow(),2);
var rub = copySheet.getRange(2,1,copySheet.getLastRow(),2);
var destination =
pasteSheet.getRange(pasteSheet.getLastRow()+1,2,copySheet.getLastRow(),2);
source.copyTo((destination), {contentsOnly: true});
rub.clearContent();
console.log(pasteSheet.getLastRow());
}
Any help on above will be appreciated.
I believe your goal is as follows.
You want to automatically run your script by changing the dropdown list of the cell "A1" of "Trigger" sheet.
In this case, how about using the OnEdit simple trigger as follows?
Modified script:
Please copy and paste the following script to the script editor of Spreadsheet and save the script. When you use this script, in this case, please change the dropdown list of the cell "A1" of "Trigger" sheet to "Run". By this, the script is run.
function onEdit(e) {
var sheetName = "Trigger";
var runScript = "Run";
var { range, source, value } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart != 1 || range.rowStart != 1 || value != runScript) return;
var ss = source;
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
var source = copySheet.getRange(2, 1, copySheet.getLastRow(), 2);
var rub = copySheet.getRange(2, 1, copySheet.getLastRow(), 2);
var destination = pasteSheet.getRange(pasteSheet.getLastRow() + 1, 2, copySheet.getLastRow(), 2);
source.copyTo((destination), { contentsOnly: true });
rub.clearContent();
console.log(pasteSheet.getLastRow());
range.setValue("Refresh"); // By this script, the value of "Run" is changed to "Refresh".
}
Note:
It seems that there are 2 values of Run and Refresh in your dropdown list. Unfortunately, I couldn't know the trigger value. So, in this sample script, when the dropdown list is changed to Run, the script is run by the simple trigger. If you want to change this, please modify the above script.
When you run directly onEdit with the script editor, an error occurs because of no event object. So, please be careful about this.
If you want to ignore the trigger value, I think that the following script might be able to be used. In this case, the dropdown list is changed, the script is run.
function onEdit(e) {
var sheetName = "Trigger";
var { range, source } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() != sheetName || range.columnStart != 1 || range.rowStart != 1) return;
var ss = source;
var copySheet = ss.getSheetByName("Copy");
var pasteSheet = ss.getSheetByName("Paste");
var source = copySheet.getRange(2, 1, copySheet.getLastRow(), 2);
var rub = copySheet.getRange(2, 1, copySheet.getLastRow(), 2);
var destination = pasteSheet.getRange(pasteSheet.getLastRow() + 1, 2, copySheet.getLastRow(), 2);
source.copyTo((destination), { contentsOnly: true });
rub.clearContent();
console.log(pasteSheet.getLastRow());
}
Reference:
Simple Triggers
The script inserted copies the values in range Sheet1!A5:F5 then transpose pastes into the first row and second column of Sheet2.
The script is able to copy and transpose paste under the last row of the second column on continuous use until it is reset.
.
Current Result Before Paste - Copies Range Sheet1 A5:F5
Current Result After Paste - Pastes to Range 2nd Column in Sheet2
.
.
.
My aim would be:
for the script to work with the checkbox in cell B2.
to create a target range for the values to be pasted to:
Sheet2! B7:B - hopefully still allowing the transpose paste to function through getLastRow()+1.
Expected Result Before Paste - Checkbox = True, Copying Range Sheet1 A5:F5
Expected Result After Paste - Paste to Range Sheet2 B7:B
Have tried inserting:
if (sheet.getSheetName() != "*DuplioMltSlds" || range.getA1Notation() != "D13" || !range.isChecked()) return;
as well as changing the function to onEdit.
Have also tried various var row = ss.getRange("Sheet2!B7:B") options, however, have not been able find a suitable result.
Sample Sheet
Script source and script:
function copyTransposeAndDelete () {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var source = ss.getRange("Sheet1!A5:F5");
var destSheet = ss.getSheetByName("Sheet2");
var destRange = destSheet.getRange(destSheet.getLastRow()+1, 2, source.getWidth(), source.getHeight());
destRange.setValues(transpose(source.getValues()));
}
function transpose(array) {
return array[0].map(function(row, i) {
return array.map(function(col) {
return col[i];
});
});
}
I believe your goal is as follows.
You want to copy and paste the cells "A5:F5" of "Sheet1" to the cell "B2" of "Sheet2". In this case, you want to copy the values by transposing.
When the values are copied again, you want to paste the transposed values to (the last row + 1) of column "B" of "Sheet2".
You want to run the script when the checkbox of cell "B2" of "Sheet1" is checked.
In this case, how about the following modification?
Modified script:
function onEdit(e) {
// Ref: https://stackoverflow.com/a/44563639
Object.prototype.get1stNonEmptyRowFromBottom = function (columnNumber, offsetRow = 1) {
const search = this.getRange(offsetRow, columnNumber, this.getMaxRows()).createTextFinder(".").useRegularExpression(true).findPrevious();
return search ? search.getRow() : offsetRow;
};
var { source, range } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() != "Sheet1" || range.getA1Notation() != "B2" || !range.isChecked()) return;
var srcRange = sheet.getRange("A5:F5");
var dstSheet = source.getSheetByName("Sheet2")
var row = dstSheet.get1stNonEmptyRowFromBottom(2) + 1;
var dstRange = dstSheet.getRange("B" + (row < 7 ? 7 : row));
srcRange.copyTo(dstRange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, true);
}
In your situation, I thought that copyTo(destination, copyPasteType, transposed) might be suitable. Because, in this method, the values can be copied by transposing.
When the OnEdit trigger is used, you can use the event object. By this, the process cost will become low a little. Ref
Note:
When you use this script, please check the checkbox of "B2" of "Sheet1". By this, the script is run. When you directly run the script with the script editor, an error like TypeError: Cannot destructure property 'source' of 'e' as it is undefined. occurs. Please be careful about this.
References:
copyTo(destination, copyPasteType, transposed)
Event Objects
Added:
If you want to put the values to the column "B" of "Sheet2" even when the column "B" of "Sheet2" has already had the values after row 7, how about the following sample script?
Sample script:
function onEdit(e) {
// Ref: https://stackoverflow.com/a/44563639/7108653
Object.prototype.get1stEmptyRowFromTop = function (columnNumber, offsetRow = 7) {
const range = this.getRange(offsetRow, columnNumber, 2);
const values = range.getDisplayValues();
if (values[0][0] && values[1][0]) {
return range.getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow() + 1;
} else if (values[0][0] && !values[1][0]) {
return offsetRow + 1;
}
return offsetRow;
};
var { source, range } = e;
var sheet = range.getSheet();
if (sheet.getSheetName() != "Sheet1" || range.getA1Notation() != "B2" || !range.isChecked()) return;
var srcRange = sheet.getRange("A5:F5");
var dstSheet = source.getSheetByName("Sheet2")
var row = dstSheet.get1stEmptyRowFromTop(2);
var dstRange = dstSheet.getRange("B" + row);
srcRange.copyTo(dstRange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, true);
}
function myfunk() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet1")
const vs = sh.getRange("A4:F4").getValues().flat().map(e => [e]);
const dsh = ss.getSheetByName("Sheet2");
dsh.getRange(7,2,vs.length,vs[0].length).setValues(vs)
}
My code below works, but I thinking that there's a much more elegant way of doing it. I want to copy a portion of a selected row (D:R) on the Customer sheet (source) to the Test sheet (destination) to the first blank row in that sheet (C:Q).
The code works, but just looks non-performant/non-elegant.
function rowSelected() {
var customer_sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Customer_Info");
var invoice_sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Test");
var customer_selection_row = customer_sheet.getActiveRange().getRow();
//Browser.msgBox(customer_selection_row);
var customer_selection_start = 'D' + customer_selection_row;
var customer_selection_end = 'R' + customer_selection_row;
var customer_selection_range = customer_selection_start + ':' + customer_selection_end;
var source_range = customer_sheet.getRange(customer_selection_range);
customer_sheet.setActiveRange(source_range);
//Browser.msgBox(source_range.getA1Notation());
var invoice_selection_row = getFirstEmptyRowWholeRow();
var invoice_selection_start = 'C' + invoice_selection_row;
var invoice_selection_end = 'Q' + invoice_selection_row;
var invoice_selection_range = invoice_selection_start + ':' + invoice_selection_end;
var destination_range = invoice_sheet.getRange(invoice_selection_range);
invoice_sheet.setActiveRange(destination_range);
//Browser.msgBox(destination_range.getA1Notation());
customer_sheet.setActiveRange(source_range).copyTo((destination_range), {contentsOnly:true});
}
I'm thinking there is some way of doing this with less code.
You want to copy the column "D" to "R" at the row of the active range of the sheet Customer_Info to the column "C" to "Q" at the row retrieved by getFirstEmptyRowWholeRow() of the sheet Test.
You want to activate the source range.
If my understanding is correct, how about this modification?
Modification points:
SpreadsheetApp.getActiveSpreadsheet() is declared one time as `ss``.
getRange(row, column, numRows, numColumns) is used instead of getRange(a1Notation).
About destination_range, when the start cell is set, the source range can be copied from it.
When the active sheet is Customer_Info, the script is run.
Modified script:
function rowSelected() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var customer_sheet = ss.getActiveSheet();
if (customer_sheet.getSheetName() == "Customer_Info") {
var invoice_sheet = ss.getSheetByName("Test");
var activeRange = ss.getActiveRange();
var source_range = customer_sheet.getRange(activeRange.getRow(), 4, activeRange.getNumRows(), 15).activate();
var destination_range = invoice_sheet.getRange(getFirstEmptyRowWholeRow(), 3); // or invoice_sheet.getRange("C" + getFirstEmptyRowWholeRow())
source_range.copyTo(destination_range, {contentsOnly: true});
}
}
getFirstEmptyRowWholeRow() of your script is also used for above modified script.
In this modified script, if you want to select the range of "A1:A3" on the sheet Customer_Info and run the script, the values of cells "D1:R3" are copied from the cell of the column "C" and the row of getFirstEmptyRowWholeRow().
References:
getRange(row, column, numRows, numColumns)
activate()
If I misunderstood your question and this was not what you want, I apologize.
In this spreadsheet: https://docs.google.com/spreadsheets/d/1givNbMvgzD8lbk6NAcwjkpp4-A_D8MetltHjEpinOAI/edit#gid=0
I'd like to Combine only unique Links and Category into Combined sheet.
Right now, with my script, it can only combine all existing data:
function combine() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
var sourceID = '1givNbMvgzD8lbk6NAcwjkpp4-A_D8MetltHjEpinOAI';
var targetID = '1givNbMvgzD8lbk6NAcwjkpp4-A_D8MetltHjEpinOAI';
var sheetExclude = ["Combined"];
var sheetExcludeIndex = new Array(sheetExclude.length);
for (var s in allsheets) {
var sheet = allsheets[s];
for (var e in sheetExclude) {
if (String(sheet.getName() == sheetExclude[e])) {
sheetExcludeIndex[e] = sheet.getIndex;
}
}
}
allsheets.splice(sheetExcludeIndex, sheetExclude.length);
for (var s in allsheets) {
var sheet = allsheets[s];
updateSourceToTarget(sourceID, sheet.getName(), targetID, 'Combined');
}
}
function updateSourceToTarget(sourceID, sourceName, targetID, targetname) {
Logger.log(sourceID + ' ' + sourceName + ' ' +targetname);
var source = SpreadsheetApp.openById(sourceID).getSheetByName(sourceName);
var destination = SpreadsheetApp.openById(targetID).getSheetByName(targetname);
var sourcelastRow = source.getLastRow();
var sourcelastCol = source.getLastColumn();
var destinationlastRow = destination.getLastRow();
var destinationlastCol = destination.getLastColumn();
var sourcedata = source.getRange(2, 9, sourcelastRow, 10).getValues();
destination.getRange(destinationlastRow + 1, 2, sourcelastRow, sourcelastCol).setValues(sourcedata);
}
However, I'd like to only combine unique links from Sheet2 and Sheet3:
In red is unique data
Sheet2:
Sheet3:
How can I efficiently add only unique values to Combined from Sheet2& Sheet3?
You want to put the values, which removed the duplicated links, to the target sheet (in this case, it's Combined sheet.).
The duplicated links are checked from the target sheet and source sheets. In this case, the target sheet and source sheets are Combined, Sheet2 and Sheet3, respectively.
In your sample Spreadsheet, you want to put the following rows to the target sheet.
https://thehill.com/policy/national-security/department-of-homeland-security/460158-new-hampshire-border-patrol BorderSecurity
https://abcnews.go.com/International/climate-change-frontier-worlds-northernmost-town/story?id=65381362 ClimateChange
You want to achieve this by modifying your Google Apps Script.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modified script:
Please modify your script as follows. In this modification, your function of updateSourceToTarget() is not used.
From:
for (var s in allsheets) {
var sheet = allsheets[s];
updateSourceToTarget(sourceID, sheet.getName(), targetID, 'Combined');
}
To:
// Retrieve values from the target sheet.
var targetSheet = ss.getSheetByName(sheetExclude[0]);
var targetValues = targetSheet.getRange("B2:C" + targetSheet.getLastRow()).getValues();
// Retrieve values from all source sheets. <--- Modified
var sourceValues = allsheets.reduce(function(ar, sheet) {
var v = sheet.getRange(2, 9, sheet.getLastRow() - 1, 10).getValues().filter(function(r) {return r[0] && r[1]});
if (v.length > 0) {
v = v.filter(function(e) {return !ar.some(function(f) {return e[0] === f[0]})});
Array.prototype.push.apply(ar, v);
}
return ar;
}, []);
// Remove the duplication values between the target sheet and all source sheets.
var dstValues = sourceValues.filter(function(e) {return !targetValues.some(function(f) {return e[0] === f[0]})});
// Add the result values to the target sheet.
if (dstValues.length > 0) {
var destination = SpreadsheetApp.openById(targetID).getSheetByName(sheetExclude[0]);
destination.getRange(destination.getLastRow() + 1, 2, dstValues.length, dstValues[0].length).setValues(dstValues);
}
The flow of this modified script is as follows.
Retrieve values from the target sheet.
Retrieve values from all source sheets.
Remove the duplication values between the target sheet and all source sheets.
Add the result values to the target sheet.
Note:
When your shared Spreadsheet is used as the target (Combined) and source sheets (Sheet2 and Sheet3), the following rows are added to the target sheet.
https://thehill.com/policy/national-security/department-of-homeland-security/460158-new-hampshire-border-patrol BorderSecurity
https://abcnews.go.com/International/climate-change-frontier-worlds-northernmost-town/story?id=65381362 ClimateChange
References:
reduce()
filter()
some()
If I misunderstood your question and this was not the direction you want, I apologize.
Added:
In this additional script, a hash table is used for this situation, as mentioned by TheMaster's comment. For example, a sample can be also seen at this thread. In your situation, at first, all values are retrieved from all sheets including Combined sheet, and the hash table is created. By this, the duplicated values are removed. Then, the converted values to an array are put to the Spreadsheet.
Sample script:
Please modify your script as follows.
From:
allsheets.splice(sheetExcludeIndex, sheetExclude.length);
for (var s in allsheets) {
var sheet = allsheets[s];
updateSourceToTarget(sourceID, sheet.getName(), targetID, 'Combined');
}
To:
// allsheets.splice(sheetExcludeIndex, sheetExclude.length); // In this script, this line is not used.
// Retrieve values from the target sheet.
var targetSheet = ss.getSheetByName(sheetExclude[0]);
var targetValues = targetSheet.getRange("B2:C" + targetSheet.getLastRow()).getValues();
// Retrieve values from all source sheets.
// Remove the duplication values between the target sheet and all source sheets.
var sourceValues = allsheets.reduce(function(obj, sheet) {
var v = sheet.getRange(2, 9, sheet.getLastRow() - 1, 10).getValues().filter(function(r) {return r[0] && r[1]});
if (v.length > 0) v.forEach(function(e) {if (!(e[0] in obj)) obj[e[0]] = e[1]});
return obj;
}, {});
var dstValues = Object.keys(sourceValues).map(function(e) {return [e, sourceValues[e]]});
// Add the result values to the target sheet.
if (dstValues.length > 0) {
var destination = SpreadsheetApp.openById(targetID).getSheetByName(sheetExclude[0]);
destination.getRange(2, 2, destination.getLastRow(), 2).clearContent();
destination.getRange(2, 2, dstValues.length, dstValues[0].length).setValues(dstValues);
}
The proposed 2 sample scripts can be used for your situation. So please select one of them.
I need some assistance with Google Apps Script. I am working in google sheets and currently have the following script:
function transpose() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('C3:N3').activate();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Sheet21'), true);
spreadsheet.getRange("'A/P'!C3:N3").copyTo(spreadsheet.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_NORMAL, true);
};
Rather than just paste in A1 of Sheet 21, I would like it to find the bottom of column F. I am also wondering how to copy data from whichever sheet I am in, not just the sheet named A/P.
Thank you in advance!
You want to add the values of C3:N3 in the active sheet to the next row of last row on column F in Sheet21. If my understanding is correct, how about this modification?
Modification points :
In your script, if the active sheet is not Sheet21, the active range becomes "A1", because spreadsheet.getRange('C3:N3').activate() at other sheet is changed by spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Sheet21'), true). By this, the values of "'A/P'!C3:N3" are copied for "A1" of Sheet21.
Flow of modified script:
Retrieve the range of source (Active sheet)
Retrieve the destination (Sheet21) sheet.
Retrieve the destination range.
Copy
Modified script : Pattern 1
If the address of last row of column "F" in "Sheet21" is smaller than that of other columns, please use this.
function transpose() {
var spreadsheet = SpreadsheetApp.getActive();
var srcRange = spreadsheet.getActiveSheet().getRange('C3:N3');
var dstSheet = spreadsheet.getSheetByName('Sheet21');
var range = dstSheet.getRange('F1:F');
var values = range.getValues();
var formulas = range.getFormulas();
var i;
for (i = values.length - 1; i >= 0; i--) {
if (values[i][0] != "" || formulas[i][0] != "") break;
}
var dstRange = dstSheet.getRange("F" + (i + 2));
srcRange.copyTo(dstRange, SpreadsheetApp.CopyPasteType.PASTE_NORMAL, true);
};
Modified script : Pattern 2
If the address of last row of column "F" in "Sheet21" is larger or the same with that of other columns, please use this.
function transpose() {
var spreadsheet = SpreadsheetApp.getActive();
var srcRange = spreadsheet.getActiveSheet().getRange('C3:N3');
var dstSheet = spreadsheet.getSheetByName('Sheet21');
var dstRange = dstSheet.getRange("F" + (dstSheet.getLastRow() + 1));
srcRange.copyTo(dstRange, SpreadsheetApp.CopyPasteType.PASTE_NORMAL, true);
};
If I misunderstand your question, please tell me. I would like to modify it.