I want to check for new names in a top 30 ranking from an API that refreshes daily, and then append every new name to an other column if it isn't already in there.
I think a for-loop would be the solution. This is what I got so far.
function appendValues(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var top30Names = ss.getRange("A4:A33").getValues();
var eligibleNames = ss.getRange("P4:P300").getValues();
for (i = 0; i < 30; i++){
var searchKey = top30Names[i]; // search if the eligible name is in the top30names
if (isInArray(searchKey, eligibleNames)){
// do nothing
}
else{
getFirstEmptyRow();
ss.getActiveCell().setValue(searchKey);
}
}
}
function isInArray(value, array) {
return array.indexOf(value) > -1;
}
function getFirstEmptyRow() {
var sheet = SpreadsheetApp.getActiveSheet(),
values = sheet.getRange("P4:P300") // the range to search for the first blank cell
.getValues(),
row = 0; //start with the first array element in the 2D array retrieved by getValues()
for (row; row < values.length; row++) {
if (!values[row].join("")) break;
}
return sheet.setActiveSelection("P" + (row + 4)).getRow();//.getLastRow() // column between "" and row + starting_row in range
}
This appends the full top 30 each time, but I only need the new values.
I've found a work around using setFormula. If anyone has a more elegant solution, I'd be happy learn.
function appendNewName(){
setFormula();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var newNames = ss.getRangeByName("newEntries").getValues();
for (i = 0; i < 30; i++){
getFirstEmptyRow();
var x = newNames[i][0];
// Logger.log(x); // What does this do???
if (x.length > 1) {
ss.getActiveCell().setValue(newNames[i]);
}
}
}
function setFormula(){
// first run this
clearFormula();
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getRangeByName("newEntries").setFormula("=IF(ISNUMBER(MATCH(A4,AppendNew,0)),\"\",A4)"); // Sets a formula to the range that will show the new daily entries in top 30
}
function clearFormula(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.getRangeByName("newEntries").clear();
}
function getFirstEmptyRow() {
var sheet = SpreadsheetApp.getActiveSheet(),
values = sheet.getRange("appendNew") // the range to search for the first blank cell
.getValues(),
row = 0; //start with the first array element in the 2D array retrieved by getValues()
for (row; row < values.length; row++) {
if (!values[row].join("")) break;
}
return sheet.setActiveSelection("P" + (row + 4)).getRow(); // column between "" and row + starting_row in range
}
Related
I have a range with data where I need a script to look for duplicates in the first column, and clear contents of the hole row in that range.
From this
To this
I've found this script, but it deletes the whole row. I need it to only clear content.
function ClearDuplicates() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("03");
var rows = sheet.getLastRow();
var firstColumn = sheet.getRange("A:A").getValues();
for (var i = rows; i >= 2; i--) {
if (firstColumn[i-1][0] == firstColumn[i-2][0]) {
sheet.deleteRow(i);
}
}
}
You need to retrieve the array data from column A and iterate through it comparing with the same array. When a match happens, apply clearContent() function [1] to clear the contents on that range (First get the range you want to clear in that row).
function ClearDuplicates() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var rows = sheet.getLastRow();
var firstColumnValues = sheet.getRange("A1:A" + rows).getValues();
for (var i = 0; i < firstColumnValues.length; i++) {
var value = firstColumnValues[i][0];
for (var j = 0; j < firstColumnValues.length; j++) {
var comparisonValue = firstColumnValues[j][0];
if ((value == comparisonValue) && (j>i)) {
//You need to change the number of columns to which you need to clear the contents, in this case is 10
sheet.getRange(j+1, 1, 1, 10).clearContent();
}
}
}
}
[1] https://developers.google.com/apps-script/reference/spreadsheet/range#clearcontent
You can just grab the range and clear it instead as seen below:
sheet.getRange("R"+i+"C").clear();
I have script that searches through an entire workbook for a specific name and returns all the data on that name. The script works, but only collects data from 1 sheet within the workbook.
I searched for some code to assist me getting all the sheet names. So I have code that does that, but for some reason it still only returns from 1 sheet.
The code below collects all the sheet names.
This function is then called in the query function.
I Suspect that this is where the issue is occuring
function sheetnames() {
var out = new Array()
var sheets = SpreadsheetApp.getActiveSpreadsheet().getSheets();
for (var i=0 ; i < sheets.length ; i++) {
var name = sheets[i].getName();
var data = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(name);
var values = data.getRange(4, 1, data.getLastRow(),
data.getLastColumn()).getValues();
out.push(values);
}
return out;
}
This function then searches for the requested data.
function query() {
var Sheet = SpreadsheetApp.getActiveSpreadsheet();
var searchSheet = Sheet.getSheetByName("Search");
var searchByName = searchSheet.getRange(4, 8).getValue();
var uses = sheetnames();
var output = new Array();
var i = 0;
var r = 0;
do{
var from = uses[i];
do{
var row = from[r];
if(row == null){
r++;
continue;
}
if(searchByName != null ){
var newName = row[7];
if(newName == searchByName){
output.push(row);
}
}
r ++;
}while(r < from.length);
i ++;
}while(i < uses.length);
return output;
}
This part just prints the data into the cells and is attached to a search drawing, which runs the function in the sheet.
function search() {
var Sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Search");
var data = query();
var count1 = 0;
do{
var subData = data[count1];
var count2 = 0;
do{
var setTo = subData[count2];
Sheet.getRange((count1 + 5), (count2 + 1)).setValue(setTo);
count2 ++;
}while(count2 < subData.length);
count1 ++;
}while(count1 < data.length);
}
The sheet is called the "Daily Payments Sheet." As you can imagine there is A LOT of data. Each sheet name is named by the month and the year that the payment occurred. The more consistent customers would obviously make purchases in more than one month.
So when searching for a customers name, I only get 1 month (1 sheet's data) returned. We have data from May 2018 till date, so again, the script doesn't collect from all the sheets.
Your code is not very readable so I figured some things on my own and simplified it. Things I assume - your search term is in 'Search' sheet column H4 and you want to search all sheets for this term in H4 column and write those out in 'Search' sheet after 4th row. Try this.
// return all rows from all sheets except Search sheet
function sheetValues(ss) {
var out = [];
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
var sheet = sheets[i];
if (sheet.getName() == 'Search') continue;
var values = sheet.getRange(4, 1, sheet.getLastRow() - 3, sheet.getLastColumn()).getValues();
out.concat(values);
}
return out;
}
// search all rows for given term and return results
// look for term in H column of every row
function query(ss, term) {
if (!term) return;
var values = sheetValues(ss);
var output = [];
for (var i = 0; i < values.length; i++) {
var row = values[i];
var name = row[7]; // 7 = col H
if (name == term) {
output.push(row);
}
}
return output;
}
// get search results and print into Search sheet
function search() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Search');
var searchByName = sheet.getRange(4, 8).getValue(); // search term is in H4 cell
var data = query(ss, searchByName);
sheet.getRange(5, 1, sheet.getLastRow() - 4, sheet.getLastColumn()).clearContent();
sheet.getRange(5, 1, data.length, data[0].length).setValues(data);
}
To start out, here is my sheet that gives two examples of sets of data and the desired results for each.
https://docs.google.com/spreadsheets/d/1MPppt2yztfPtz2iSssSfIuBoYccxQ4gs7PZdygdz1Z8/edit?usp=sharing
Here is the code that I have right now. It is not in a working state, but it seems to be close. Will edit as I experiment different solutions.
// on open, add menu and bttons to test functions
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{name: 'Analyze and flip data selection', functionName: 'flipSelection'},
{name: 'Check for first empty cell', functionName: 'firstEmptyCell'}
];
spreadsheet.addMenu('Scripts', menuItems);
}
// put data from a column into a row. on a blank line, write to new row
function flipSelection() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var testSheet = ss.getSheetByName("test");
var sheet = ss.getSheetByName("main");
var range = sheet.getActiveRange();
var numRows = range.getNumRows();
var numCols = range.getNumColumns();
var data = [];
// check active range(current selection) for values and store them in data array
for (var i = 1; i < numRows; i++){
for (var j = 1; j < numCols; j++) {
var currentValue = range.getCell(i,j).getValue();
data.push(currentValue);
}
}
// log array to verify accurate data
for (var k = 0; k < data.length; k++){
ss.toast(data[k].toString());
Logger.log(data[k].toString());
}
// for each string in data array, write to next cell. if the string is blank, go to next row and write to next cell (recursion??)
for (var n = 0; n < data.length; n++) {
// Do we have data? If not, move on
if (data[n] == "") {
//target next empty row (need help here!)
continue;
}
const colA = testSheet.getRange('A:A').getValues().join().split(",");
const rowIndex = colA.indexOf('');
const colIndex = firstEmptyCell(rowIndex); // assuming firstEmptyCell works correctly
const targetCell = testSheet.getRange(rowIndex, colIndex);
targetCell.setValue(data[n]);
}
}
// a way to find first empty cell
function firstEmptyCell (emptyRow) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var testSheet = ss.getSheetByName("test");
var range = testSheet.getRange(emptyRow, 1, 1, testSheet.getMaxColumns());
var vals = range.getValues();
// Get the index of the first empty cell from the waarden array of values
var emptyCell = vals[0].indexOf("");
// log the index of empty cell
ss.toast("The index of the first empty cell is: " + emptyCell.toString());
Logger.log("The index of the first empty cell is: %s", emptyCell);
return emptyCell;
}
try this code:
function onOpen() {
var spreadsheet = SpreadsheetApp.getActive();
var menuItems = [
{name: 'Analyze and flip data selection', functionName: 'flipSelection'},
];
spreadsheet.addMenu('Scripts', menuItems);
}
function flipSelection()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet= ss.getSheetByName("src");
var destSheet= ss.getSheetByName("dest");
//get datas
var datas = sheet.getDataRange().getValues();
var destDatas = [];
var row = [];
var max = 0;
var i = 0;
//start is to check if it's the beginning of parse meaning no previous data
var start = true;
//parse columns first then rows
for (var c = 0; c < datas[0].length; c++)
{
for (var r = 0; r < datas.length; r++)
{
//if cell is blank or beginning of new column
if (datas[r][c] == "" || !r)
{
i = 1;
//if it's not the beginning of program then add previous row to final data array.
if (!start)
{
destDatas.push(row);
}
//if it's a blank cell then add skip it
if (r)
r++;
//create a new row with first value inside of it
row = [datas[r][c]];
start = false;
}
//if it's not the beginning of a new column and the cell is not empty
else
{
//increment index columns of one
i++;
// Basically if there's more value than last time then set column length to column index
if (i >= max)
max = i;
//add new data to row array
row.push(datas[r][c])
}
}
}
//condition to push previous and last row into final data array
if(row)
destDatas.push(row);
//set final data array length to the same size
for(var i = 0; i < destDatas.length; i ++)
for (var j = (destDatas[i].length); j < max; j++)
destDatas[i].push("");
//set destination range and set values.
destSheet.getRange(1, 1, destDatas.length, max).setValues(destDatas);
}
I got the following script from #Mike Grace's website:
// Deletes rows in the active spreadsheet that contain 'Yes' in column A
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var rowsDeleted = 0;
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
if (row[0] == 'Yes') {
sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
rowsDeleted++;
}
}
}
The script works well, but I would like to make a change to it, instead of calling .deleteRow() I want to use .hideRow() instead.
Because the .hideRow() method only accepts a range as far as I understand, I'm getting the following error:
How do I modify this script so that it hides the row instead of deleting them?
there are different hideRow / s () methods, one of them takes a row number as argument.
your code can be simplified like this :
function hideRowsWithYes() {
var sheet = SpreadsheetApp.getActiveSheet();
var values = sheet.getDataRange().getValues();
for (var i = 0; i < values.length; i++) {
if (values[i][0] == 'Yes') {
sheet.hideRows(1+i);
}
}
}
As usual, autocomplete makes the job easier...
Edit following comment :
to unhide row we don't have a simple method with row index so we need to define the range... code goes like this (i has a +1 because values is an array that starts from 0 while sheets are indexed from 1)
function unHideRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var values = sheet.getDataRange().getValues();
var row;
var maxCol = sheet.getMaxColumns();
for (var i = 0; i < values.length; i++) {
if (values[i][0] == 'Yes') {
row = sheet.getRange(1+i, 1, 1, maxCol);
sheet.unhideRow(row);
}
}
}
I'm looking for some assistance on find the row number for a cell that contains a specific value.
The spreadsheet has lots of data across multiple rows & columns. I'm looping over the .getDataRange and can located the cell containing the value I'm looking for. Once found I'd like to know the row that his cell is in, so that I can further grab additional cell values since I know exactly how many rows down and/or columns over the additional information is from the found cell.
Here is a bit of the code for finding the cell containing a specific string.
function findCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
var row = "";
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == "User") {
row = values[i][j+1];
Logger.log(row);
}
}
}
}
When outputting Logger.log(row) it gives me the values I'm looking for. I want to determine what row each value is in, so I can then go down X number of rows and over X columns to get the contents of other cells.
I don't know much about google apps but it looks like [i] is your row number in this circumstance.
So if you did something like:
function findCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
var row = "";
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == "User") {
row = values[i][j+1];
Logger.log(row);
Logger.log(i); // This is your row number
}
}
}
}
This method finds the row number in a particular column for a given value:
function rowOf(containingValue, columnToLookInIndex, sheetIndex) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets();
var dataRange = sheet[sheetIndex].getDataRange();
var values = dataRange.getValues();
var outRow;
for (var i = 0; i < values.length; i++)
{
if (values[i][columnToLookInIndex] == containingValue)
{
outRow = i+1;
break;
}
}
return outRow;
}
But if you do it like that, it won't refresh unless you change any of the parameters, that's because a custom function should be deterministic, that is, for the same parameters, it should always give the same result.
So instead, it's better to do it this way:
function rowOf(containingValue, range) {
var outRow = null;
if(range.constructor == Array)
{
for (var i = 0; i < range.length; i++)
{
if(range[i].constructor == Array && range[i].length > 0)
{
if (range[i][0] == containingValue)
{
outRow = i+1;
break;
}
}
}
}
return outRow;
}
In this case, you need to pass the full column range your looking for like so:
rowOf("MyTextToLookFor", 'MySheetToLookIn'!A1:A)
Where you would replace A by the colum of your choice, and MySheetToLookIn by your sheet's name and MyTextToLookFor by the text you are looking for.
This will allow it to refresh on adding rows and removing rows.
Since 2018 this can be done without a loop using flat().
function findRow(searchVal) {
var sheet = SpreadsheetApp.getActiveSheet();
var data = sheet.getDataRange().getValues();
var columnCount = sheet.getDataRange().getLastColumn();
var i = data.flat().indexOf(searchVal);
var columnIndex = i % columnCount
var rowIndex = ((i - columnIndex) / columnCount);
Logger.log({columnIndex, rowIndex }); // zero based row and column indexes of searchVal
return i >= 0 ? rowIndex + 1 : "searchVal not found";
}
ES6 gave us a one liner for this (but expanded for full detail).
function findRow() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss. getActiveSheet(); // OR GET DESIRED SHEET WITH .getSheetByName()
const dataRange = sheet.getDataRange();
const values = dataRange.getValues();
const columnIndex = 3 // INDEX OF COLUMN FOR COMPARISON CELL
const matchText = "User"
const index = values.findIndex(row => row[columnIndex] === matchText)
const rowNumber = index + 1
return rowNumber
}
This can be done with the Text Finder:
function findRow(searchValue){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var thisSheet = ss.getActiveSheet();
var tf = thisSheet.createTextFinder(searchValue)
var thisRow = tf.findNext().getRow()
return thisRow
}
Discovered through this post: Google App Scripts find text in spreadsheet and return location index
Implementing: https://developers.google.com/apps-script/reference/spreadsheet/text-finder
To find a row in a Google Sheets sheet based on the value of a specific column (in this case, the invoice_id column) and edit another column (in this case, the send column) in that row, you can use the following script:
// Replace "Sheet1" with the name of your sheet
var sheet = SpreadsheetApp.getActive().getSheetByName("Sheet1");
// Replace "A" with the column letter for the invoice_id column
// and "B" with the column letter for the send column
var invoiceIdColumn = "A";
var sendColumn = "B";
// Replace "12345" with the invoice_id you want to search for
var invoiceIdToSearch = "12345";
// Find the row number of the row with the matching invoice_id
var data = sheet.getDataRange().getValues();
var row = data.findIndex(row => row[invoiceIdColumn - 1] == invoiceIdToSearch) + 1;
// If a row with the matching invoice_id was found
if (row) {
// Set the value of the send column in that row to "true"
sheet.getRange(row, sendColumn).setValue(true);
}
This script will search through the entire sheet for a row with an invoice_id value that matches the invoiceIdToSearch variable, and then set the value of the send column in that row to true.
Note that this script assumes that the invoice_id column is the first column in the sheet (column A) and the send column is the second column (column B). If your columns are in different positions, you will need to adjust the invoiceIdColumn and sendColumn variables accordingly.