I'm trying to achieve the following result:
So if column A has duplicate values, I want to show these duplicate values on column B. Here is what I have tried so far. But the code just makes Apple to show two times in a column B. How would I need to alter my code to make it work the way I want?
function findDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getActiveSheet()
var lastRow = sheet.getLastRow()
var data = sheet.getRange(1,1,lastRow).getValues()
var dataUnique = data.reduce(function (out, item) {
return out.concat(out.filter(function (comp) {
return item.toString() != comp.toString();
}).length ? [] : [item])
}, []);
var newValues = sheet.getRange(1,2,dataUnique.length).setValues(dataUnique)
}
This is the result when I run the code:
This is a spreadsheet created specifically to address this question. It contains the following code that will take values from A and output the result in column B:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSheet();
var input = sheet.getRange('A1:A').getValues().filter(e=>e[0]).map(e=>e[0]);
var inputLower = input.map(e=>e.toLowerCase());
var output = new Array();
input.forEach(function (e,i,arr){if(inputLower.indexOf(e.toLowerCase())!=i && output.indexOf(e)==-1){output.push(e)}});
sheet.getRange(1,2,output.length,1).setValues(output.map(e=>[e]));
}
Related
I'm new to scripting, I'm trying for personal use to automate the copy of data according to a main sheet and I'm stuck on a trick.
I just adapted this script to copy data from one sheet to another, however when it runs the same data is copied in a loop in addition to the new ones. I just try to copy only the new rows when I run the script.
Thx for your help
function CopyDataGames() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Source");
var topsheet = ss.getSheetByName("Copie");
var topsheetLastRow = topsheet.getLastRow();
let lastRow = sheet1.getLastRow();
let sortRange = sheet1.getSheetValues(2,2,lastRow,44);
Logger.log(lastRow)
Logger.log(typeof(sortRange))
Logger.log(sortRange.length);
let topCounter = 1;
for (var i = 1; i <= sortRange.length; i++){
let name = sheet1.getRange(i,2).getValue();
console.log(i + " - " + name);
// find the name
if (name =="PoinPOin"){
let rowValues = sheet1.getRange(i,1,1,44).getValues();
topsheet.getRange(topsheetLastRow+topCounter,1,1,44).setValues(rowValues);
topCounter++; }
}
}
Modification points:
About however when it runs the same data is copied in a loop in addition to the new ones., I think that the reason for this issue is due to that your script doesn't confirm the existing values of "Copie" sheet. In order to achieve your goal, I think that it is required to confirm the values of "Copie" sheet.
In your script, getValues and setValues are used in a loop. In this case, the process cost will become high. Ref
When these points are reflected in your script, how about the following modification?
Modified script:
function CopyDataGames() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Source");
var topsheet = ss.getSheetByName("Copie");
// I modified below script.
var srcValues = sheet1.getDataRange().getValues();
var dstObj = topsheet.getDataRange().getValues().reduce((o, r) => (o[r.join()] = r, o), {});
var res = srcValues.filter(r => r[1] == "PoinPOin" && !dstObj[r.join()]);
if (res.length == 0) return;
topsheet.getRange(topsheet.getLastRow() + 1, 1, res.length, res[0].length).setValues(res);
}
When this script is run, the values of the "Source" and "Copie" sheets are retrieved. And, the duplicated values are checked. And then, the new values are put into the "Copie" sheet.
Note:
If you want to check only the last row of "Source" sheet, how about the following modified script?
function CopyDataGames() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("Source");
var topsheet = ss.getSheetByName("Copie");
// I modified below script.
var lastRowValue = sheet1.getRange(sheet1.getLastRow(), 1, 1, sheet1.getLastColumn()).getValues()[0];
var dstObj = topsheet.getDataRange().getValues().reduce((o, r) => (o[r.join()] = r, o), {});
if (lastRowValue[1] != "PoinPOin" || dstObj[lastRowValue.join()]) return;
topsheet.appendRow(lastRowValue);
}
References:
reduce()
filter()
Here is the description of my problem.
I have a range of placeholder text and its associated values in a spreadsheet. The placeholder text also exists in a slide presentation which gets replaced using the replacealltext function. However, the colors in the spreadsheet for the values do not change. Please see the examples below.
Google Apps Script:
// Google Sheets
var masterSheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = masterSheet.getSheetByName('Monthly Report'); //gets the data from the active pubfile
var range = sheet.getRange('S1:T110');
var data = range.getValues();
var titleName = sheet.getRange('AA14:AA14').getValues().toString();
data.shift(); //removes the headers in the sheets
// Creating the new slide
var spreadsheetID = SpreadsheetApp.getActiveSpreadsheet().getId(); //Ensure Code is applied to active Pubfile
var spreadsheetFolder = DriveApp.getFileById(spreadsheetID);
var parentFolder = spreadsheetFolder.getParents();
var folderID = parentFolder.next().getId();
var reportTemplate = DriveApp.getFileById('SLIDE ID'); // Gets the report Template
var copiedTemplate = reportTemplate.makeCopy(titleName, DriveApp.getFolderById(folderID)); //Makes a copy of the orginal and saves it in the specified folder
var newPresoID = copiedTemplate.getId();
var skeleton = SlidesApp.openById(newPresoID); //opens the new template presentation
var slides = skeleton.getSlides(); //calls the new slides
return newSlide1();
function newSlide1() {
var slide1new = slides[0]; // defining the location of the new slide 1
data.forEach(function (row) {
var templateVariable = row[0]; // First column contains variable names
var templateValue = row[1]; // Second column contains values
slide1new.replaceAllText(templateVariable, templateValue); //replaces all the text that matches the placeholders
slide1new.
});
As you can see, this code just replaces the value but does not bring in the source formatting.
Here are snapshots of the original spreadsheet data and the slides:
Source Data
Destination Slide with Placeholders
Current Result
Desired Result
Please suggest a way to fix this issue. Thank you!
Here is an example of applying the colors of a spreadsheet cell to the table cells of a slide.
My spreadsheet looks like this.
My slide looks like this.
Code.gs
function myFunction() {
try {
let spread = SpreadsheetApp.openById("xxxxxxxxxxxxxxxxxxxxxxxxxxx");
let sheet = spread.getSheetByName("Sheet1");
let range = sheet.getRange(2,1,sheet.getLastRow()-1,2);
let values = range.getDisplayValues();
let colors = range.getFontColors();
let present = SlidesApp.getActivePresentation();
let slide = present.getSlides()[0];
let table = slide.getTables()[0];
for( let i=0; i<table.getNumRows(); i++ ) {
let row = table.getRow(i);
for( let j=1; j<row.getNumCells(); j++ ) {
let text = row.getCell(j).getText();
values.some( (value,k) => {
let l = text.replaceAllText(value[0],value[1]);
if( l > 0 ) {
text.getTextStyle().setForegroundColor(colors[k][1]);
return true;
}
return false;
}
);
}
}
}
catch(err) {
console.log(err);
}
}
And the final results look like this.
Reference
TableCell
Array.some()
https://i.stack.imgur.com/7VAJk.png
i want to copy data from "dB" sheet A5:A29 and paste to correct column.
so i use the script to find the correct column.
there range B2:CX2 have 0(not-correct) or 1(correct) value, so i use 'for' & 'if'
BUT!! It's too delay!!
i use console.time() and i get 25909ms(timecheck2 value) !!!
please help me.....
here is my code
function save(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('dB');
console.time("timecheck1");
//find last row
var copyrangeO = sheet.getRange(5,1,25,1).getValues();
var lastrowO = copyrangeO.filter(String).length;
var copyrange = sheet.getRange(5,1,lastrowO,1);
console.timeEnd("timecheck1");
//my dB data start "B2".
var cv = 1;
//find correct value(1). B2 ~ CX2 (#100)
console.time("timecheck2");
for (var i=2; i<101;i++){
if(sheet.getRange(2,i).getValue()===1){
cv = i;
}
}
console.timeEnd("timecheck2");
//if data isn't correct, cv===1. so error msg print.
console.time("timecheck3");
if(cv ===1){
Browser.msgBox("ERROR")
}else {
//data copy and paste.
var columnToCheck = sheet.getRange(4,cv,1000).getValues();
var lastrow = getLastRowSpecial(columnToCheck);
var pasterange = sheet.getRange(lastrow+4,cv);
copyrange.copyTo(pasterange, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
Browser.msgBox(lastrowO + " saved!");
}
console.timeEnd("timecheck3");
}
Issue:
If I understand your situation correctly, you want to find the cell in B2:CX2 in which the value is 1, but the script is taking too much time for this.
The problem here is that you are using getRange and getValue in a loop (sheet.getRange(2,i).getValue()===1). This greatly increases the amount of calls to the Sheets service, which slows down your script, as you can see at Minimize calls to other services.
Solution:
In that case, I'd suggest doing the following:
Get the values from all columns at once using getValues().
Use findIndex to get the column index for which value is 1.
In order to do that, replace this:
var cv = 1;
//find correct value(1). B2 ~ CX2 (#100)
console.time("timecheck2");
for (var i=2; i<101;i++){
if(sheet.getRange(2,i).getValue()===1){
cv = i;
}
}
With this:
var ROW_INDEX = 2;
var FIRST_COLUMN = 2; // Column B
var LAST_COLUMN = 102; // Column CX
var columnValues = sheet.getRange(ROW_INDEX, FIRST_COLUMN, 1, LAST_COLUMN-FIRST_COLUMN+1).getValues()[0];
var cv = columnValues.findIndex(columnValue => columnValue === 1) + FIRST_COLUMN;
Note:
If there's no cell in the range with value 1, findIndex returns -1 which, added to FIRST_COLUMN, results in 1. That's appropriate for your current script, but won't work if the FIRST_COLUMN stops being 2, so be careful with this (either change the condition if(cv ===1){ to something less strict, or don't assign the resulting value to cv if findIndex returns -1).
The function will spend most of its time in the for loop because it repeats the Range.getValue() call many times. You can speed things up quite a bit by getting all values with one Range.getValues() call, like this:
let cv = 1;
console.time("timecheck2");
sheet.getRange('B2:B100').getValues().flat()
.some((value, index) => (cv = 2 + index) && value === 1);
console.timeEnd("timecheck2");
Note that this is not a cleanest way of finding cv, but it should help illustrate why you have a performance issue. You may want to do a complete rewrite of the code, using declarative instead of imperative style.
Try this:
I don't know what you're doing in the save because to did not supply the helper function code.
function save(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('dB');
var vs0 = sh.getRange(5,1,25,1).getValues();
var lr0 = vs0.filter(String).length;
var crg = sh.getRange(5,1,lr0,1);
var cv = 1;
const vs1 = sh.getRange(2,2,1,99).getValues().forEach((c,i) => {
if(c == 1)cv = i + 2
})
if(cv == 1){
Browser.msgBox("ERROR")
}else {
var vs2 = sh.getRange(4,cv,1000).getValues();
var lastrow = getLastRowSpecial(vs2);
var drg = sh.getRange(lastrow+4,cv);
crg.copyTo(drg, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
Browser.msgBox(lr0 + " saved!");
}
}
I'm using Google Scripts with the aim of calling an already filled cell using the ID. Once the data has been called, the user would be able to update and send it back to the main "data", in case some update is necessary.
The code isn't giving any error, but is not updating the cells either. See below an example of the code:
function Update() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // From sheet
var dataS = ss.getSheetByName("Results"); // Get the data already inputted
var str = formS.getRange("D6").getValue(); // The ID to find and to update in the "Inputs" sheet
var values = dataS.getDataRange().getValues();
for (var i = 0; i<=values.length; i++) {
var rowFinder = values[i];
if (rowFinder[columnIndex] == str) {
var rowNumber = i+1;
var updatedValues = [[formS.getRange("D9").getValue(),
formS.getRange("D13").getValue(),
formS.getRange("D14").getValue(),
formS.getRange("D15").getValue(),
formS.getRange("D16").getValue(),
formS.getRange("D17").getValue(),
formS.getRange("D18").getValue(),
formS.getRange("D19").getValue()]];
dataS.getRange(rowNumber, 2, 1, 17).setValues(updatedValues);
}
break;
}
}
Find also an example of the sheet that I'm using, where you could also see the whole code (everything else is working fine). Spreadhsheet here
Why function is not updating the data? I've tried to put the dataS.getRange(rowNumber, 2, 1, 17).setValues(updatedValues); in different parts of the code and it doesn't work either. Any ideas?
It is best to get the values of a range all at once and iterate through the entire range than pulling individual cells.
I tested this on your sample spreadsheet (Thanks for including!)
function SubmitData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // Add the name of the sheet where the data is going to be retrieved
var dataS = ss.getSheetByName("Results"); // Name of the sheet where the data should appear
//get the entire form range
var values = formS.getRange(6, 3, 14, 2).getValues();
//Create an array to paste into the Results tab
var pasteArr =[];
//iterate through the two columns on the App tab, only add values to the paste array
//if Col C is not blank and does not equal "Choose Params"
for (var i in values) {
//the values array looks like this [[row1Col1, row1Col2],[row2Col1, row2Col2]..]
//so to get to the value in row i, column 1 you use values[i][0]
let field = values[i][0];
if (field != "" && field != "Choose Params") {
let val = values[i][1];
pasteArr.push(val);
}
};
Logger.log(pasteArr);
var dataLastRow = dataS.getLastRow() + 1;
//Build the range to paste in - use the length of pasteArr to get the proper number of columns
//then to make pasteArr a 2D array put pasteArr inside another array by using square brackets
dataS.getRange(dataLastRow, 1, 1, pasteArr.length).setValues([pasteArr]);
Logger.log("Done");
ClearCell();
}
Your other functions have similar issues.
function Update() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formS = ss.getSheetByName("App"); // From sheet
var dataS = ss.getSheetByName("Results"); // Get the data already inputted
var dataVals = dataS.getDataRange().getValues();
//get the entire form range
var values = formS.getRange(6, 3, 14, 2).getValues();
//Create an array to paste into the Results tab
var pasteArr =[];
//iterate through the two columns on the App tab, only add values to the paste array
//if Col C is not blank and does not equal "Choose Params"
for (var i in values) {
let field = values[i][0];
if (field != "" && field != "Choose Params") {
let val = values[i][1];
pasteArr.push(val);
}
}
let numCols = pasteArr.length;
//iterate through the rows on the data tab, update the row with the matching ID
for (var j in dataVals) {
let dataTableId = dataVals[j][0];
let formID = pasteArr[0];
Logger.log(dataTableId + " , "+formID);
if (dataTableId === formID){
//have to convert j from 0 index to columns by adding 1. The + before the j and the 1 force app script to treat it as a number.
dataS.getRange(+j+ +1, 1, 1, numCols).setValues([pasteArr]);
Logger.log("dataTableId matches");
}
}
Logger.log("Done");
}
I have a list of 290 items with 4 columns, which I need to duplicate. I have in a Spreadsheet Row some departements and under it a lot of systems. for each system I need to duplicated the 290 values and add one index column at the front, the department in column 6 and the system in column 7.
I am using the following code:
const ssOrg = SS.getSheetByName("OrgStructure");
function myFunction() {
var afinal = [];
var aDevs = ssDeliverables.getDataRange().getValues();
aDevs.shift();
var lastRow = ssOrg.getLastRow();
var lastColum = ssOrg.getLastColumn();
var count = 1
for (var spalte = 1; spalte <lastColum; spalte++){
var squad = ssOrg.getRange(3,spalte).getValue();
for (var reihe=5; reihe <lastRow; reihe++) {
var system = ssOrg.getRange(reihe,spalte).getValue();
if (system !== ""){
aDevs.map(function(row){
row[0] = count;
row[5] = squad;
row[6] = system;
count ++
return row
})
Logger.log(system);
afinal = afinal.concat(aDevs);
}
}
}
var lastDataRow = ssAssessmentLogic.getLastRow();
ssAssessmentLogic.getRange(2,1,lastDataRow-1,10).clearContent();
var rngResult = ssAssessmentLogic.getRange(2,1,afinal.length,7);
rngResult.setValues(afinal);
}
The problem is that the array at the end (16000 rows) has the same value for each row in column 6 and 7. It is allways the last system & department combination that appears in all 16000 rows.
Where am I wrong?
The question was a little confusing for me but I followed your specifics in the comments section where you explain what exactly info to copy and how and where to paste it.
This gets the job done:
const ss = SpreadsheetApp.getActive();
// this gets the "deliverables" and the "departments", this last ones in a list
// and for each department runs the function to add the complete new modified array to the spreadsheet
function fillsNewSheet(){
var newSheet = ss.getSheetByName('List_DeliverablesALL');
// hardcoded the titles
newSheet.getRange(1, 1, 1, 6).setValues([['taskHasDeliverableNumber', 'Deliverable Description', 'SDP Task', 'SDP Milestone', 'Group', 'Department']])
var deliverables = getDeliverables();
var departments = getDepartments();
for(i=0;i<departments.length;i++){
var departmentsGroup = departments[i];
for(j=0;j<departmentsGroup.length;j++){
addsNewSection(deliverables, departmentsGroup[j])
}
}
}
// this just gets de array of values we are gonna paste for each department in the structure sheet.
function getDeliverables(){
var deliSheet =ss.getSheetByName('Deliverables1');
var deliValues = deliSheet.getRange(2, 2, deliSheet.getLastRow()-1, 4).getValues();
return deliValues;
}
// As the departments are in different columns with different row counts,
// I go over the whole columns and rows and create a single list with all "department" and "group" pairs
function getDepartments(){
var structureSheet = ss.getSheetByName('OrgStructure');
var cols = structureSheet.getLastColumn();
var groups = structureSheet.getRange(3, 1, 1, cols).getValues()[0]
var departments = [];
for(i=1;i<=cols;i++){
var group = groups[i-1];
var groupDeps = structureSheet.getRange(5, i, structureSheet.getLastRow(), 1).getValues();
var subDeps = []
for(j=0;j<groupDeps.length;j++){
subDeps.push([group, groupDeps[j][0]])
}
var filtered = subDeps.filter( function (data) { return data[1] != "" });
departments.push(filtered);
}
return departments;
}
// finally this gets the complete list of "deliverables" from the first sheet, and one specific department.
function addsNewSection(deliverables, department){
var newSheet = ss.getSheetByName('List_DeliverablesALL');
// and in every row of the deliverables list we add the corresponding department/group pair to get the new modified array.
var newSection = []
for(k=0;k<deliverables.length;k++){
var newRow = deliverables[k].concat(department)
newSection.push(newRow)
}
// when this is complete I paste the whole new array in the third sheet.
newSheet.getRange(newSheet.getLastRow()+1, 1, newSection.length, 6).setValues(newSection)
}