Find a Row in Excel file based on a column value and Update the row value using ClosedXML - closedxml

I have an excel file as shown below. I would like to find a row in excel using a column Value (like SubmissionID == 2) and then update the email address of the particular row
using (XLWorkbook wb = new XLWorkbook(filestream))
{
var ws = wb.Worksheet(1);
var range = ws.RangeUsed();
var lastRow = range.LastRowUsed().RowNumber();
// not able to find the column
using (var rows = ws.RowsUsed(r => r.FirstCell().GetString() == "SubmissionID"))
{
foreach (var row in rows)
{
// Do something with the row...
}
}
}

I think you should be using
using (var rows = ws.RowsUsed(r => r.FirstCell().GetString() == "2"))
or
using (var rows = ws.RowsUsed(r => r.FirstCell().Value == 2))

Related

If column contains text add a checkbox in column apps script google sheets

I am copying data from a spreadsheet titled after the specific month and placing it in my main spreadsheet. I have successfully copied the data into range K80:K94 on my Daily Hub sheet.
In range K80:K94 I now want to add a checkbox in column M if there is a value in column K. For example if there is a value in K80 and K81 there would be a checkbox in M80 and M81. I feel like this should be fairly straightforward, however I have tried a few different options including using IsBlank() and nothing seems to be working.
function dailyhubhabits() {
var montha = new Array(12);
montha[0] = "JANUARY";
montha[1] = "FEBRUARY";
montha[2] = "MARCH";
montha[3] = "APRIL";
montha[4] = "MAY";
montha[5] = "JUNE";
montha[6] = "JULY";
montha[7] = "AUGUST";
montha[8] = "SEPTEMBER";
montha[9] = "OCTOBER";
montha[10] = "NOVEMBER";
montha[11] = "DECEMBER";
var dailyhabitshubmonth = new Date();
var getdhmonth = montha[dailyhabitshubmonth.getMonth()];
Logger.log(getdhmonth);
var mhs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(getdhmonth);
var monthhabitsogdata = mhs.getRange("C56:E70");
var gethabits = monthhabitsogdata.getValues();
Logger.log(gethabits);
var dhs = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("DAILY HUB");
var habitsarea = dhs.getRange("K80:K94");
monthhabitsogdata.copyTo(habitsarea);
//THIS IS WHERE I AM HAVING TROUBLE
var datavalues = dhs.getRange("K80:K94").getValues();
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0].length != 0) {
dhs.getRange(i+1,14).insertCheckboxes();
}
}
}
You want to insert a checkbox on Column M when there is a value in the same row of column K.
There are two problems with this part of your script:
evaluating whether the cell has a value
defining the target range for the checkbox
Does the cell have a value?
length returns the number of records in an array, but it is not a good method for determining whether a cell contains a value. This is a popular topic; you might care to read Google Spreadheets Scripts: check if cell is empty for several methods.
a better approach is !== ""
Defining the target cell
dhs.getRange(i+1,14).insertCheckboxes(); - there are two problems here
Column M is 13
i starts at zero, so the first range value would be .getRange(1,14) = Cell N1.
so you need a variable that defines the startRow, such as:
var startRow = 80
REPLACE
//THIS IS WHERE I AM HAVING TROUBLE
var datavalues = dhs.getRange("K80:K94").getValues();
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0].length != 0) {
dhs.getRange(i+1,14).insertCheckboxes();
}
}
WITH
var startRow = 80
var endRow = 94
var datavalues = dhs.getRange("K"+startRow+":K"+endRow).getValues()
var data_leng = datavalues.length;
for(var i=0; i<data_leng; i++) {
if(datavalues[i][0] !=="") {
dhs.getRange(i+startRow,13).insertCheckboxes()
}
}
SUGGESTION
In my understanding, here's your goal:
Check values in K80:K94
Insert a checkbox on a row in M that is adjacent to a row that isn't empty in the K80:K94 range.
Perhaps you could try this sample script to replace your current line on the section in inserting the check-boxes:
/** SUGGESTION
* 1. Iterate through the values in range K80:K94 & identify which aren't empty.
* 2. Get each non-empty values' row numbers.
* 3. To reduce runtime execution in the loop, if there are consecutive non-empty values, set them as a range (e.g. M80:M81). Otherwise a single value will be set as a single range (e.g. M83);
* 4. Iterate through these ranges & insert the checkboxes.
*/
var range = SpreadsheetApp.getActive().getRange('K80:K94');
var temp_values = range.getValues().map((x, i) => x != '' ? [x, (range.getLastRow() - (range.getNumRows() - i) + 1)].flat() : '*');
var ranges = temp_values.join().split('*').map(y => (y.replace(/[a-zA-Z,]+/g, '-')).split('-').filter(x => x != ''));
ranges.map(z => [...new Set([z[0], z[z.length - 1]])]).forEach(
row => row.length > 1 ? SpreadsheetApp.getActive().getRange(`M${row[0]}:M${row[1]}`).insertCheckboxes() :
SpreadsheetApp.getActive().getRange(`M${row[0]}`).insertCheckboxes()
);
/** End */
This sample script runs faster vs your current implementation as it shortens the data to be processed in the loop
Demo
Sample sheet
After running the script

Vlookup + indexOf to find values in a CSV via Google App Script without using loop

The main idea is not to need looping to generate a VLOOKUP because it generates a huge slowdown when the amount of data is very large.
To VLOOKUP on data directly in the sheet I do as follows:
function myFunction() {
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var searchValue = s.getRange("Test!A1").getValue();
var data = SpreadsheetApp.openById("XXXXXXXXXXXX").getSheetByName("Test 2");
var dataValues = data.getRange("A1:A").getValues();
var dataList = dataValues.join("ღ").split("ღ");
var index = dataList.indexOf(searchValue);
if (index === -1) {
s.getRange("Test!B1").setValue('off');
} else {
var row = index + 1;
var foundValue = data.getRange("D"+row).getValue();
s.getRange("Test!B1").setValue(foundValue);
}
}
But there is a big problem in this method, because when many different accounts try to access this sheet at the same time, the error type error: could not connect sheet xxxxx appears or causes huge delay sometimes.
So what was the solution I found? Publish spreadsheet pages as CSV so they can be used and this error doesn't happen when many accounts call the same spreadsheet.
Currently, as I haven't found a way to use indexOf using the first column when I import the CSV with several columns of data, I had to create a spreadsheet page only with the copy data of column A, and then I got to the final result of VLOOKUP like this:
(the value in var searchValue in this example case will be two)
function myFunction() {
var s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var url_columnA = 'AAAAA';
var url_vlookup = 'BBBBB';
var dataSearch = Utilities.parseCsv(UrlFetchApp.fetch(url_columnA));
var dataList = dataSearch.join("ღ").split("ღ");
var searchValue = s.getRange("Test!A1").getValue();
var index = dataList.indexOf(searchValue);
if (index === -1) {
s.getRange("Test!B1").setValue('off');
} else {
var row = index;
var dataVlookup = Utilities.parseCsv(UrlFetchApp.fetch(url_vlookup));
var foundValue = dataVlookup[row][3];
s.getRange("Test!B1").setValue(foundValue);
}
}
Return example:
other number
var url_vlookup:
Col A
Col B
Col C
Col D
home
1
a
win
away
2
b
loose
one
3
c
number
two
4
d
other number
three
5
e
number again?
var url_columnA:
Col A
home
away
one
two
three
Is there any way to handle var url_vlookup data for search the value in column A so that it's not necessary to use this page var url_columnA separated or is the only way to do it without looping?
The first column can easily be separated after parsing using Array.map:
const dataVlookup = Utilities.parseCsv(UrlFetchApp.fetch(url_vlookup));
const url_columnA = dataVlookup.map(row => row[0])

How do you create a Uniq ID in Google Sheets using apps script?

Im looking at creating a simple Uniq ID after column A has information entered and need for the ID to show on column I. I want to call it Trip Number and display Driver-John0001 and so forth using google sheets script. Sorry I'm new to this so I don't know the lingo
The current code I had found works but now I need it a bit different. This is what my results show Driver:1611710811706
I would like to pull Driver-John0001. Where the name John is generated by the column labeled Driver or column D
How do I change it to add the value on column D + 4 digit numbers that don't repeat?
function TripID () {
{ var sheetVals = [
["DriverLog","Driver:","",9,1]
];}
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//Loop through every sheet value
for(var i = 0; i < sheetVals.length; i++){
var sheetID = sheetVals[i][0],
frontStr = sheetVals[i][1],
backStr = sheetVals[i][2],
IDcol = sheetVals[i][3],
editCol = sheetVals[i][4];
var offset = IDcol - editCol;
if(sheet.getName() === sheetID){
var date = new Date().getTime();
var newID = frontStr+date+backStr;
//Check the location of the active cell
var selectedCell = ss.getActiveCell();
if( selectedCell.getColumn() === editCol) {
//Update the ID Column
var cellToChange = selectedCell.offset(0,offset);
if(cellToChange.isBlank()){
cellToChange.setValue(newID);
};
};
};
};
};
Assuming that you will never need more than 9999 IDs, then you could modify your code with the following steps to accomplish this. Note that this solution will allow you to create a (nearly) unlimited number of ID numbers, but after 9999, the length of the id numbers will vary.
Use the PropertiesService to initialize a counter for creating IDs
function createIdProperty() {
PropertiesService.getScriptProperties().setProperty('idCounter', '0');
}
// run this once to create the idCounter property
createIdProperty();
// To test that your property was created you can run this
Logger.log(PropertiesService.getScriptProperties().getProperty('idCounter')); // logs '0'
Create a function to get the next counterId number from Script Properties as a string with 0s replacing empty digits ( 1 => '0001', 2 => '0002', 3 => '0003')
function getUniqueIdNumber() {
var currentIdNum = +PropertiesService.getScriptProperties().getProperty('idCounter'); //get counter as a number
currentIdNum++; // increment counter by 1
currentIdNum+=''; // convert counter back to a string
PropertiesService.getScriptProperties().setProperty('idCounter', currentIdNum); // store the new counter in properies
while (currentIdNum.length < 4) {
currentIdNum = '0'+currentIdNum;
}
return currentIdNum;
}
modify your original code to use getUniqueIdNumber instead of date for creating your id
Change
var newID = frontStr+date+backStr;
To
var idNumber = getUniqueIdNumber();
var newID = frontStr+idNumber;
Note that you can replace frontStr and backStr with any variables you want.

Copy/paste values using google app script

Here is the sheet with an example of the data that I'm using
https://docs.google.com/spreadsheets/d/1_k3xclEgdREfMMks7H2-uk0tzxXqCaoeVW_G8ase-3o/edit?usp=sharing
I only put the formulas on the rows without color, and the information about the schedule comes from other tab.
the colors in green are the ones with dates, where I store inside the numbers, and the blue ones also contain formulas and I wanna skip those columns when pasting the values because is correlated to another worksheet, so I can't paste the values
I've tryied two ways
this first one I receive an erro message: Exception: The parameters (number[]) don't match the method signature for SpreadsheetApp.Range.setValues.
//destination = sheet.getRange(1,colA[j],table.length,1)
//destination.setValues(table); // where we paste the values by column
For this one, it paste on other tab on the first columns
//sheet.getRange(1,colA[j],table.length,1).copyTo(sheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
Here is the script that Im using
function PasteValues() {
var ss = SpreadsheetApp.openById("...");
var sheet = ss.getSheetByName("Testsheet");
var rows = sheet.getDataRange().getValues();
var dates = rows[2];
//Logger.log(dates)
var yesterday = new Date(Date.now() - 864e5);
var numbers = [];
for(var i = 2; i < dates.length; i++) {
let columns = i
if (dates[i]!=="" && dates[i] !== null){
numbers.push(columns);
}
if (dates[i]==="") {
continue;
}
if (dates[i].getDate() == yesterday.getDate() && dates[i].getMonth() == yesterday.getMonth() ){
break;
}
}
colA=numbers.slice(-5)
var table = [];
Logger.log(rows.length)
Logger.log(colA)
for(var j=0;j<colA.length;j++)
{
table =[];
for (var i = 0; i < rows.length;i++ )
{
table[i] = rows[i][colA[j]];
}
Logger.log("the number of the column is: "+colA[j]);
Logger.log(table);
// where I paste the data
}
}
This is the example on how my data is to copy/paste it based on the column number
When you retrieve values from the spreadsheet, getValues() already returns them to you in a 2-D array - there is no need to manually transfer them into another array
You can either do:
var table = sheet.getDataRange().getValues();
destination = sheet.getRange(1,statColumn,table.length,table[0].length);
destination.setValues(table);
Or:
sheet.getDataRange.copyTo(sheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
UPDATE
Exception: The parameters (number[]) don't match the method signature
for SpreadsheetApp.Range.setValues.
means that you are trying to assign a row (1-D array) to a range (2-D array).
Also, table.length will retrieve you the number of columns and not rows if table is a row.
This can be easily solved by defining:
table = [table];
Sample snippet:
for(var j=0;j<colA.length;j++)
{
table =[];
for (var i = 0; i < rows.length;i++ )
{
table[i] = rows[i][colA[j]];
}
Logger.log("the number of the column is: "+colA[j]);
table = [table];
Logger.log(table);
// where I paste the data
destination = sheet.getRange(1,colA[j],table.length,1)
destination.setValues([table]); // where we paste the values by column
}
UPDATE
If what you want is to copy paste selected data column by column, you need to create a 2D array table and populate it as following:
for(var i = 2; i < dates.length; i++) {
let columns = i
if (dates[i]!=="" && dates[i] !== null){
numbers.push(columns);
}
if (dates[i]==="") {
continue;
}
if (dates[i] instanceof Date && dates[i].getDate() == yesterday.getDate() && dates[i].getMonth() == yesterday.getMonth() ){
break;
}
}
colA=numbers.slice(-5)
var table = [];
Logger.log(rows.length)
Logger.log(colA)
for(var j=0;j<colA.length;j++)
{
for (var i = 0; i < rows.length;i++ )
{
table[i] =[];
table[i][0] = rows[i][colA[j]];
}
Logger.log("the number of the column is: "+colA[j]);
Logger.log(table);
// where I paste the data
destination = sheet.getRange(1,colA[j],table.length,1)
destination.setValues(table); // where we paste the values by column
}
It is important to make sure that the array is 2-dimensional and that its dimensions (rows and columns) correspond to the dimensions of the range into which you want to set the data.

Automatically move data from one sheet to another in google docs

i have a spreadsheet that i keep track of tasks i need to do, once complete i enter a date in the last column. What i want is for that completed task to be moved to sheet 2.
At present i have sheet 1 named SUD_schedule and i want the completed row of data to be moved to sheet 2 named SUD_archive. I've looked through the forum posts already and i've tried a variation of scripts but so far no luck. The closest i have come is this script:
function onEdit() {
var sheet1 = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();//Original sheet
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheets()[1];//target sheet
// to act on only one sheet, check the sheet name here:
//If it si not first sheet, it will do nothing
if (sheet1.getSheetName() != "SUD_schedule") {
return;
}
//Get Row and column index of active cell.
var rowIndex = sheet1.getActiveRange().getRowIndex();
var colIndex = sheet1.getActiveRange().getColumnIndex();
//If the selected column is 10th and it is not a header row
if (colIndex == 16 && rowIndex > 1) {
//Get the data from the current row
var data = sheet1.getRange(rowIndex,1,1,9).getValues();
var lastRow2;
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
//Copy the data to the lastRow+1th row in target sheet
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
}
}
Column P (16) is the task complete date, row 1 is frozen and contains column headers.
Can anybody help show where i'm going wrong.
Kind regards
Den
Your code is not generic and you are more complicating your objective. Below will work out your need.
function onEdit(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName('SUD_schedule');
var sheet2 = ss.getSheetByName('SUD_archive');
var dateColumn = "16";
var array = []
var range = sheet1.getRange(1, 1, sheet1.getLastRow(), dateColumn);
for (var i = 2; i <= sheet1.getLastRow(); i++) //i iterates from 2 as you say R1 is header
{
if(isValidDate(range.getCell(i, dateColumn).getValue()) == true) //checking if any values on column16 is valid date
{
data = sheet1.getRange(i, 1, 1, dateColumn).getValues(); //Getting the range values of particular row where C16 is date
for (var j = 0; j < dateColumn; j++) //Adding the row in array
{
array.push(data[0][j]);
}
}
if(array.length > 0)
{
sheet2.appendRow(array); //Appending the row in sheet2
array = [];
sheet1.deleteRow(i); //deleting the row in sheet as you said you want to move, if you copy remove this and next line
i=i-1; //managing i value after deleting a row.
}
}
}
//Below function return true if the given String is date, else false
function isValidDate(d) {
if ( Object.prototype.toString.call(d) !== "[object Date]" )
return false;
return !isNaN(d.getTime());
}
I am not sure that the syntax you have as used below is entirely correct.
(sheet2.getLastRow()==0)?lastRow2=1:lastRow2=sheet2.getLastRow()+1;
sheet2.getRange(lastRow2,1,1,data[0].length).setValues(data);
What I know will work for certain is if you omit the variable lastRow2 all together and use this instead.
sheet2.getRange(getLastRow+1,1,1,data[0].length).setValues(data);
To complement Joachin's answer, here is how you can adapt that code if you don't have the date in the last row. In the below shown part of the code replace Lastcolumnumber with your last column.
//Getting the range values of particular row where C16 is date
data = sheet1.getRange(i, 1, 1, LASTCOLUMNNUMBER).getValues();
//Adding the row in array
for (var j = 0; j < LASTCOLUMNNUMBER; j++)