This is probably an easy fix but I'm not sure why the array of matching emails are being outputted horizontally and not vertically in Google Sheets. I want all the emails to be in a specific column so the must be outputted vertically with each email being in an individual cell. I tried using the split method to separate the array into individual cells but only the first email is displayed.
function getTheDesiredContacts() {
var SS =SpreadsheetApp.getActiveSpreadsheet();
var EmailSheet = SS.getSheetByName("Threads");
var Contacts = ContactsApp.getContactGroup("Employees").getContacts();
var EmailsArray = [];
var label = GmailApp.getUserLabelByName(LABEL);
var MaxColumnCount = 30;
var threads = label.getThreads();
for(var i = 0, n = Contacts.length, x = 0; i < n; i++){
if(i < MaxColumnCount){
EmailsArray.push([]);
}
EmailsArray[x] = Contacts[i].getEmailAddresses()[0];
x++;
}
if (threads.length > 0) {
for (var t=threads.length-1; t>=0; t-- ){
var labels = threads[t].getLabels();
}
for (var i in labels) {
var z= labels[i].getName();
}
if(z==LABEL){
var EMails = GmailApp.getMessagesForThreads(
GmailApp.search("label:" + labels[i].getName())).reduce(function(a, b){
return a.concat(b);}).map(function(EMails){
return EMails.getFrom();
});
var AEmail = EMails.sort().filter(function(el,j,a){
if(j==a.indexOf(el))
return 1;return 0
});
var deDup = new Array();
for(var k in AEmail) {
var str = AEmail[k].split("<").pop();
str=str.replace(">",'');
deDup.push(str);
}
}
}
var matchingEM = new Array();
for(var m in EmailsArray){
for(var o in deDup){
if(EmailsArray[m]==deDup[o]){
matchingEM.push(deDup);
}
}
}
var unq = matchingEM.filter((item, i, ar) => ar.indexOf(item) === i);
var unique = unq.join();
var uniqueOff = unique.split(',');
var uniqueemails= new Array;
uniqueemails.push([unique]);
Logger.log("These are the matching emails: " + uniqueemails);
EmailSheet.getRange(2,4, uniqueemails.length,1).setValues([uniqueemails]);
return uniqueOff
}
Modification points:
When I saw your script, it seems that uniqueemails of EmailSheet.getRange(2,4, uniqueemails.length,1).setValues([uniqueemails]); is [["value1,value2,,,"]] that value1,value2,,, is a string value. In this case, the values are put to a cell. This is due to var unique = unq.join();. In order to put the values to the row direction, the array is required to be like [["value1"],["value2"],,,].
For this, how about the following modification?
Modified script:
Please modify your script as follows.
From:
var unq = matchingEM.filter((item, i, ar) => ar.indexOf(item) === i);
var unique = unq.join();
var uniqueOff = unique.split(',');
var uniqueemails= new Array;
uniqueemails.push([unique]);
Logger.log("These are the matching emails: " + uniqueemails);
EmailSheet.getRange(2,4, uniqueemails.length,1).setValues([uniqueemails]);
To:
var uniqueemails = matchingEM.filter((item, i, ar) => ar.indexOf(item) === i)[0].map(e => [e]);
EmailSheet.getRange(2, 4, uniqueemails.length, 1).setValues(uniqueemails);
In above modified script, the values are put from the cell "D2" to the row direction. This is from your script. If you want to put the values to other rows, please modify above script.
Note:
In this modified script, it supposes that your script currently works with no errors. Please be careful this.
References:
map()
setValues(values)
Related
I am trying to pull the value from the cell above when a cell is blank, however, every-time I run my code it removes the formula from the cell above and makes it text. Can someone help me figure out how to keep the formula intact? Here is the code I utilized that worked with pulling a value down:
function fill_2(){
var ss = SpreadsheetApp.openByUrl('https://docs.google.com/spreadsheets/d/1UvBuVp9N866J-3677dXsPYYpZZRd7i1O4GIlPkBJWbA/edit#gid=713176516');
var sheet = ss.getSheetByName("All Client Log-ins");
var tracts = ss.getRange("All Client Log-ins!F2:F").getValues();
var allTractList = [];
var title;
var len = tracts.length;
for (var row = 0; row < len; row++) {
if (tracts[row] !='') {
title = tracts[row];
allTractList.push([title]);
Logger.log(title);
} else allTractList.push([title]);
}
Logger.log('allTractList' + allTractList);
sheet.getRange("F2").offset(0,0,allTractList.length).setValues(allTractList);
return allTractList;
}
Here's what I think your code boils down to:
function fill_2(){
const ss = SpreadsheetApp.openById('1UvBuVp9N866J-3677dXsPYYpZZRd7i1O4GIlPkBJWbA');
var sh1 = ss.getSheetByName("All Client Log-ins");
Logger.log(sh1.getRange("F2:F" + sh1.getLastRow()).getValues().flat().filter(e => e));
return sh1.getRange("F2:F" + sh1.getLastRow()).getDisplayValues().flat().filter(e => e);
}
I have a program that filters and updates data from an existing sheet.
The program works as follows:
1. Find and filter out the required value
2. Enter data in [Adjustment] column then update to database in Record sheet.
I tried to try but my program doesn't seem to work.
I tried to edit the program code but when run it will affect the other columns and the [adjustment] column value is entered wrong.
This is my link program
function Searchold(){
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var shtRecords = ss. getSheetByName ("RECORD");
var shtForm = ss. getSheetByName ("TEST") ;
var records = shtRecords. getDataRange () . getValues ();
var sField = shtForm. getRange ("A3").getValue ();
var sValue = shtForm.getRange ("A6").getValue();
var sCol = records [0].lastIndexOf(sField);
var results = records.filter(function(e){return sValue == e[sCol] });
if(results.length==0){SpreadsheetApp.getUi().alert("not found values");}
else{
shtForm.getRange(9,1,results.length,results[0].length).setValues(results);
}
}
function Updatenew(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var shtRecords = ss.getSheetByName("RECORD");
var shtForm = ss.getSheetByName("TEST");
var LastRow = shtForm.getRange("A8").getNextDataCell(SpreadsheetApp.Direction.DOWN).getLastRow();
var newData = shtForm.getRange(9,1,LastRow -1,7).getValues();
for(var i =0; i<newData.length;i++){
var oldData= shtRecords.getDataRange().getValues();
for(var j= 0;j<oldData.length;j++){
if(newData[i][0] ==oldData[j][0]){
var newData2 = [newData[i]];
shtRecords.getRange(j + 1,1,1,newData2[0].length).setValues(newData2);
}
}
}
}
Can you help me with the update program? Sincerely thank you
Modification points:
When I saw your showing script of Updatenew, I think that each row of var oldData = shtRecords.getDataRange().getValues() is used in each loop of for (var i = 0; i < newData.length; i++) {}. By this, each row is overwritten by each row of newData. By this, all searched rows in "RECORD" sheet are the same value. I thought that this might be the reason for your issue.
var oldData = shtRecords.getDataRange().getValues(); can be used one call.
In order to avoid this issue by modifying your script, as one of several methods, how about the following modification?
From:
for (var i = 0; i < newData.length; i++) {
var oldData = shtRecords.getDataRange().getValues();
for (var j = 0; j < oldData.length; j++) {
if (newData[i][0] == oldData[j][0]) {
var newData2 = [newData[i]];
shtRecords.getRange(j + 1, 1, 1, newData2[0].length).setValues(newData2);
}
}
}
To:
var oldData = shtRecords.getDataRange().getValues();
for (var j = 0; j < oldData.length; j++) {
for (var i = 0; i < newData.length; i++) {
if (newData[0][0] == oldData[j][0]) {
var newData2 = newData.splice(0, 1);
shtRecords.getRange(j + 1, 1, 1, newData2[0].length).setValues(newData2);
break;
}
}
}
Note:
At the above modification, setValues is used in a loop. In this case, the process cost becomes high. If you want to reduce the process cost of the script, how about using Sheets API? When Sheets API is used, how about the following modification? Please enable Sheets API at Advanced Google services.
To
var temp = newData.slice();
var data = shtRecords.getDataRange().getValues().reduce((ar, r, i) => {
if (temp[0][0] == r[0]) {
var t = temp.splice(0, 1);
t[0][2] = Utilities.formatDate(t[0][2], Session.getScriptTimeZone(), "dd/MM/yyyy");
t[0][4] = Utilities.formatDate(t[0][4], Session.getScriptTimeZone(), "dd/MM/yyyy");
ar.push({ range: `'RECORD'!A${i + 1}`, values: t });
}
return ar;
}, []);
Sheets.Spreadsheets.Values.batchUpdate({ data, valueInputOption: "USER_ENTERED" }, ss.getId());
I have a template sheet with checkboxes and I want to copy the checked ones to a new sheet. I have a working version that involves adding rows but I am looking for something faster. I thought getting a range of values on both the new and old sheets and working on the arrays would be best but I hit a error:
'Cannot covert Array to Object[][]".
I think the issue has to do with the fact that this is a new unpopulated sheet. The code below is the simplest example of what is happening. Am I doing something wrong, or is this just not possible?
function test(){
var s = SpreadsheetApp.getActiveSpreadsheet().insertSheet();
var r = s.getRange(1,1,5);
var v = r.getValues();
for ( var i=0; i < 5; i++) {
v[i] = i;
}
r.setValues(v); //ERROR: Cannot covert Array to Object[][]`enter code here`
}
It looks like the line v[i] = i; converts the Object[][] to an array. So , i think (bizarre) I need to create a new array[][] asfollows:
function test(){
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var r = s.getRange(1,1,5,1);
var v = r.getValues();
var ta = [];
for ( var i=0; i < 5; i++) {
ta[i] = [];
ta[i].push(i) ;
}
r.setValues(ta);
}
Ok. Here is the full solution.
The function looks for the sheet "Work" that has 2 columns; the first is a checkbox, the second is the string value of interest. For every checked box (value == true), the 2nd column's value, Font weight, and Font size are copied into appropriately 'shaped' structures.
Once constructed, a new sheet is created, a range in the new sheet is retrieved and used to set the values, weights and sizes of a single column.
function copyCheckedItems () {
var cl = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Work');
if (cl) {
var cnt = cl.getLastRow();
var range = cl.getRange(1,1, cnt, 2 );
var values = range.getValues();
var weights = range.getFontWeights();
var sizes = range.getFontSizes();
// Compute data needed for new sheet in the right shape.
var tv = [];
var tw = [];
var ts = [];
var newCnt = 0;
for (var row in values) {
if(values[row][0]){
tv[newCnt] = [];
ts[newCnt] = [];
tw[newCnt] = [];
tv[newCnt].push(values[row][1]);
tw[newCnt].push(weights[row][1]);
ts[newCnt].push(sizes[row][1]);
newCnt++;
}
}
// construct the new sheet in a minimum of calls
var name = Browser.inputBox('Enter WorkSteet name');;
var sheetOut = SpreadsheetApp.getActiveSpreadsheet().insertSheet(name);
var ro = sheetOut.getRange(1,1,newCnt,1);
ro.setValues(tv);
ro.setFontSizes(ts);
ro.setFontWeights(tw);
//Browser.msgBox("Done.");
}
else {
Browser.msgBox('Work sheet not found!');
}
}
Click here for Sample Sheet
I need a solution that matches a cell value (Sheet1! Q5) to a range in another tab/sheet (NegotiationData! A1:O1) and paste the relevant data fetched from the first sheet under the designated columns of the second sheet under the matched value.
For example, if Sheet1!Q5 matches with the name in NegotiationData! A1 then do the following
Fetch Sheet1! R6 and paste in NegotiationData!A3:A
Fetch Sheet1! Q6 and paste in NegotiationData!B3:B
Fetch Sheet1! Q7 and paste in NegotiationData!C3:C
Also, each time the script runs it should not overwrite data but find the next empty row and paste the values.
I have an incomplete script that I'm trying to achieve the above from my research from various posts but since I'm just a week old to coding I'm not able to go any further than where I have got with the below script.
I'm not finding how to match the value and fetch the relevant data and paste them below the matched value.
Please help!
The Incomplete / Incorrect Script (File Name: NegotiationSubmit)
function submitNegotiation() {
var sh, id, v, estNum, negotiation, negoNotes, i;
sh = SpreadsheetApp.getActive();
id = sh.getSheetByName('Sheet1').getRange('Q5').getValue();
v = sh.getRange('R6').getValue();
estNum = Number(sh.getRange('Q6').getValue().split(" ")[1]);
negoNotes = sh.getRange('Q7').getValue();
negotiation =sh.getSheetByName('NegotiationData').getRange('A1:O');
if(v && estNum) {
negotiation.setValues(negotiation.getValues()
.map(function (r, i) {
if (r[0] == id) {
r[1] = v;
r[2] = estNum;
r[3] = negoNotes;
}
return r;
})
)
}
}
How about this modification?
Modification points :
Retrieve values of "Q5:R7" at once, and the values are converted to the import values.
Use the destructuring assignment for retrieving each value.
Import the converted values using the number of column retrieved by ids[0][i] == id.
Modified script :
function submitNegotiation() {
var id, estNum, v, negoNotes;
var sh = SpreadsheetApp.getActive();
var values = sh.getSheetByName('Sheet1').getRange("Q5:R7").getValues();
[id] = values[0];
[estNum, v] = values[1];
[negoNotes] = values[2];
// estNum = Number(estNum.split(" ")[1]); // If you want to use "estNum = Number(sh.getRange('Q6').getValue().split(" ")[1]);", please use this line.
var sh2 = sh.getSheetByName('NegotiationData');
var ids = sh2.getRange("A1:O1").getValues();
for (var i=0; i<ids[0].length; i++) {
if (ids[0][i] == id) {
sh2.getRange(sh2.getLastRow() + 1, i + 1, 1, 3).setValues([[v, estNum, negoNotes]]);
}
}
}
Note :
I was confused to the following points.
In your script, estNum is Number(sh.getRange('Q6').getValue().split(" ")[1]);. But in your sample spreadsheet, Estimate 1 of cell "Q6" is used.
I commented this in modified script.
In your sample spreadsheet, "Story ID" is 1. But in your script, it's US-001 of cell "R6".
In this modified script, US-001 of cell "R6" was used.
If I misunderstand your question, I'm sorry.
Edit :
function submitNegotiation() {
var id, estNum, v, negoNotes;
var sh = SpreadsheetApp.getActive();
var values = sh.getSheetByName('Sheet1').getRange("Q5:R7").getValues();
[id] = values[0];
[estNum, v] = values[1];
[negoNotes] = values[2];
estNum = Number(estNum.split(" ")[1]); // If you want to use "estNum = Number(sh.getRange('Q6').getValue().split(" ")[1]);", please use this line.
var sh2 = sh.getSheetByName('NegotiationData');
var ids = sh2.getRange("A1:O1").getValues();
for (var i=0; i<ids[0].length; i++) {
if (ids[0][i] == id) {
var temp = sh2.getRange(1, i + 1, sh2.getLastRow(), 3).getValues();
for (var j=temp.length-1; j>=0; j--) {
if (temp[j].join("") != "") {
sh2.getRange(j + 2, i + 1, 1, 3).setValues([[v, estNum, negoNotes]]);
break;
}
}
}
}
}
I realize that this does not resemble your code. Sorry about that. I'm learning too. But I'm putting it up anyway to provide an alternative method that includes finding the last row of the appropriate column...
function submitNegotiation() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('Sheet1');
var negotiationData = ss.getSheetByName('NegotiationData');
// Sheet1 variables
var user = sheet1.getRange('Q5').getValue();
var storyId = Number(sheet1.getRange('R6').getValue().split("-")[1]);
var estimateNum = sheet1.getRange('Q6').getValue();
var note = sheet1.getRange('Q7').getValue();
var pointers = [storyId, estimateNum, note];
// NegotiationData variables
var range = negotiationData.getDataRange().getValues();
var columns = negotiationData.getLastColumn();
var users = negotiationData.getRange(1, 1, 1, columns).getValues();
for(var i = 0; i < columns; i++) {
// match user with users to get column number
if(users[0][i] == user) {
var col = negotiationData.getRange(1, i + 1).getColumn();
// count rows in col
var rowCount = 1;
for(var i = 1; i < range.length; i++) {
if (range[i][col - 1] != "") {
rowCount++;
}
}
// assign pointers
var newRow = rowCount + 1;
for(var j = 0; j < pointers.length; j++) {
negotiationData.getRange(newRow, col, 1, 1).setValue(pointers[j]);
col++;
}
}
}
}
I need this script to delete the row (within the Registration sheet) with the matching Registration Code to the Cancel Registration sheet's Registration code. As of now, this script only deletes a row if "sheetR.deleteRow(i);" is not inside "if (regCodeR === regCodeCR) {}". It doesn't delete the correct row either.
function rD() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetR = ss.getSheetByName("Registration");
var sheetCR = ss.getSheetByName("Cancel Registration")
var dataR = sheetR.getDataRange().getValues();
var dataCR = sheetCR.getDataRange().getValues();
var headerRow = 1;
for (var i = 1; i in dataR && i in dataCR; ++i) {
var rowR = dataR[i];
var rowCR = dataCR[i];
var duplicate = false;
var regCodeR = sheetR.getRange(headerRow + i, 10).getValues();
var regCodeCR = sheetCR.getRange(headerRow + i, 9).getValues();
if (rowR[9] === rowCR[8]) {
duplicate = true;
}
}
if (regCodeR === regCodeCR) {
sheetR.deleteRow(i);
}
}
I tried this code with simple data having one column with registration numbers. You can modify the main sheet range according to your data. Also make sure to change the sheet names.
Tried and tested the code below :
function deleteDup(){
var mainSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('regis');
var cancelSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('cancel');
var mainSheetValues = mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).getValues();
var cancelSheetValues = cancelSheet.getRange(2, 1, cancelSheet.getLastRow(),1).getValues();
var k = 0;
var del = [];
// Getting the row index of matching values
for (i = 0; i < cancelSheetValues.length; i++)
{
var cancelValue = cancelSheetValues[i][0];
Logger.log(cancelValue);
for (j = 0; j < mainSheetValues.length; j++)
{
if(mainSheetValues[j][0] == cancelValue){
del[k] = j;
k++;
}
}
}
del.sort();
var count =0;
// deleting the values from the main sheet values array
for (i = 0; i < k; i++ )
{
mainSheetValues.splice((del[i] - count), 1);
count++;
}
var len = mainSheetValues.length;
// Update the sheet with new values
mainSheet.getRange(2, 1, mainSheet.getLastRow(),1).clearContent();
mainSheet.getRange(2, 1, len,1).setValues(mainSheetValues);
}
But instead you can also do this way:
Set a flag value in the registration sheet if it matches with the cancellation sheet.
Then loop through the registration sheet data and delete the matching flag row.
Hope that helps!