I have this code where I can get which one is the current selected cell and use it to modify its value:
theSelection = ThisComponent.CurrentSelection
theSelection.setString("some value")
Now I want to move to the next column to the right, if it was Microsoft excel VBA I could just use something like theSelection.Offset(0,1) but that's not the case. So I'm doing some workarounds of course:
nextCell = oActiveSheet.getCellByPosition( ???currentColumn + 1, ???currentRow)
ThisComponent.CurrentController.select( nextCell )
I just want to know the simplest way to replace these ??? to the actual values of the theSelection var to move to the next column to the right.
I also tried this:
nextCell = oActiveSheet.getCellByPosition( column() + 1, row())
But I don't know why it is always returning column() = 1 and row() = 1 in regardless of which is the value of the CurrentSelection. Thanks in advance for the help.
Get the cell address.
Sub ChangeAndThenGoToCellToRightOfSelection
oActiveSheet = ThisComponent.getCurrentController().getActiveSheet()
oSels = ThisComponent.getCurrentSelection()
If oSels.supportsService("com.sun.star.sheet.SheetCell") Then
'A single cell is selected.
oSels.setString("some value")
address = oSels.getCellAddress()
nextCell = oActiveSheet.getCellByPosition(address.Column + 1, address.Row)
ThisComponent.CurrentController.select(nextCell)
End If
End Sub
To see what an object can do, use an introspection tool such as XrayTool or MRI.
Related
I have a script that looks into values in column G and if the correspondent cell in column A is empty, sends me an email.
--- WHAT WORKS --
It works ok for static values: it sends one email per each not empty cell in column G for which there is no value in column A
--- WHAT DOESN'T WORK --
It sends several emails for what I assume it's every Column G cell (empty or not) when the column A values are fetched from another tab. That way it's like all G and A cells have data, so I get multiple unwanted emails.
This is the script code:
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet to send emails');
const data = sh.getRange('A2:G'+sh.getLastRow()).getValues();
data.forEach(r=>{
let overdueValue = r[0];
if (overdueValue === ""){
let name = r[6];
let message = 'Text ' + name;
let subject = 'TEXT.'
MailApp.sendEmail('myemail#gmail.com', subject, message);
}
});
}
And this is the link to the test sheet:
https://docs.google.com/spreadsheets/d/1OKQlm0PjEjDB7PXvt34Og2fa4vPZWnvLazTEawEtOXg/edit?usp=sharing
In this test case, I "should" only get one email, related to ID 55555. With the script as is, I get one related to 55555 and several others "undefined".
To avoid e-mail spam, I didn't add the script to that sheet but it shows the "Vlookup" idea.
Can anyone give me a hand, please?
Thank you in advance
Issue:
The issue with your original script is that the sh.getLastRow returns 1000 (it also processes those rows that doesn't have contents, result to undefined)
Fix 1: Get specific last row of column G:
const gValues = sh.getRange('G1:G').getValues();
const gLastRow = gValues.filter(String).length;
or
Fix 2: Filter data
const data = sh.getRange('A2:G' + sh.getLastRow()).getValues().filter(r => r[6]);
Note:
As Kris mentioned in the comments, there is a specific case where getting the last row above will fail (same with getNextDataCell). This will not properly get the last row WHEN there are blank rows in between the first and last row of the column. If you have this kind of data, then use the 2nd method which is filtering the data.
If your data in column G does not have blank cells in between the first and last row, then any method should work.
I checked your test sheet, and sh.getLastRow() is 1000.
OPTION 1
If column G won't have empty cells between filled ones, then you can do this:
const ss = SpreadsheetApp.getActive();
const sheet = ss.getSheetByName("Sheet to send emails");
// get the first cell in column G
var gHeader = sheet.getRange(1, 7);
// equivelent of using CTRL + down arrow to find the last da
var lastRow = gcell.getNextDataCell(SpreadsheetApp.Direction.DOWN).getRow();
const data = sheet.getRange(2, 1, lastRow, 7).getValues();
OPTION 2
Add another condition to your code - like this:
data.forEach(r=>{
let overdueValue = r[0];
let name = r[6]
// check if the value in col A is blankd and col G is not blank
if (overdueValue === "" && name !== ""){
let message = 'Text ' + name;
let subject = 'TEXT.'
MailApp.sendEmail('myemail#gmail.com', subject, message);
}
});
And to speed it up, use a named range to limit how many rows it has to iterate through:
const ss = SpreadsheetApp.getActive();
const data = ss.getRangeByName("Your_NamedRange_Here").getValues();
I'm working on an Apps Script project for Sheets and I don't know if it's because I just never really worked with Sheets or Excel, but I don't know how to set a formula for a whole column through code.
var cell = sheet.getRange([i], 2);
var cell2 = sheet.getRange([i], 1);
var inhoud = cell2.getValue();
cell.setFormula("=(" + inhoud/86400000 + "DATE(1970,1,1)");
I want every B of a row to do something with the A of that same row. In the sheet self it's easy to just "drag the function down", to make it apply to every row, but I don't know how to get that to work in code as I can't use A2, for example, or A2:A30. Part of the problem may be that it's in a for loop:
var subsie = [];
for (i = 0; i < subscriptions.length; i++) {
var subscription = subscriptions[i];
creationdate = subscription.creationTime;
if (subscription.plan.planName == 'ANNUAL' && subscription.renewalSettings.renewalType == 'AUTO_RENEW') {
subsie.push([creationdate, ' ', subscription.plan.planName]);
Logger.log(subsie);
var cell = sheet.getRange([i], 2);
var cell2 = sheet.getRange([i], 1);
var inhoud = cell2.getValue();
cell.setFormula("=(A1:A100/86400000) + DATE(1970,1,1)");
} }
sheet.getRange(sheet.getLastRow()+1, 1, subsie.length, subsie[0].length).setValues(subsie);
The actual goal is to convert the epoch values of A into dates, which I tried in a lot of different ways but turned out to be more difficult than I expected. This was the only formula that seemed to work for my output, which was like this: 1433235478178. How can I make this code work? Thanks in advance!
Solved it :)
creationdate = (subscription.creationTime/86400000)+25567;
So I am working on a macro in Basic, and where I'm at now I need to compare two columns for duplicates.
Here's what I have going so far:
for i = 0 To 5
Cell1 = Sheet.getCellByPosition(0,i)
for j = 0 to 5
Cell2 = Sheet.getCellByPosition(1,j)
rem COMPARISON WOULD HAPPEN HERE
Next j
Next i
I would like to do something along the lines of: if Cell1.String == Cell2.String then ...
This is my first attempt at writing a macro and so I would greatly appreciate any help and/or guidance.
Thanks!
Also on a side note if anyone know of good tutorials.documentation for this other than wiki, I would be extremely grateful for the link
You should store all values of the first column into an array and then compare every value of the second column with all entries of the array using a simple recursion.
A code that may work is
Dim firstcolumn(5) As String
For i = 0 to 5
firstcolumn(i) = Sheet.getCellByPosition(0,i)
Next i
For j = 0 to 5
Cell2 = Sheet.getCellByPosition(1,j)
for i = 0 to 5
if Cell2 = firstcolumn(i) then
MsgBox("The value of the cell " + i + " in the first column is the same with the value of the cell " + j + " of the second column")
Next i
Next j
The best place to look for code samples is the openoffice forum https://forum.openoffice.org/en/forum/
I hope that the above will help you.
I have a spreadsheet with three sheets. Two are called 2012 and 2011 and have a bunch of similar data. The last sheet does comparisons between the data.
To be able to choose year, I'm using a cell (D1) where I can I can write either 2011 or 2012. The formulas then use the INDIRECT function to include this cell as part of the reference.
INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!F:F")
This is not a pretty solution and makes the formula quite long and complex.
=IFERROR(SUM(FILTER( INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!M:M") ; (INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!B:B")=$A4)+(INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!B:B")=$A5)+(INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!B:B")=$A6)+(INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!B:B")=$A7)+(INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!B:B")=$A8); MONTH(INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!D:D"))=$B$1 ; INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!F:F")=D$3));0)
Is there a better way of doing this?
I've tried to create a separate spreadsheet for the calculations sheet and importing (IMPORTRANGE) the data from the two sheets together on one sheet with VMERGE (custom function from the script gallery) but there is quite a lot of of data in these two sheets and the import takes a long time. Any changes (like changing year) also take a long time to recalculate.
Database functions tend to be cleaner when doing this kind of thing.
https://support.google.com/docs/bin/static.py?hl=en&topic=25273&page=table.cs&tab=1368827
Database functions take a while to learn, but they are powerful.
Or
You could put INDIRECT(CHAR(39)&$D$1&CHAR(39)&"!B:B") in a cell on its own.
I think that you have two years of information where the schema is identical (column C has the same type of information on both sheets). Also, I'm assuming that column B tracks the year.
If so, consider holding all of your information on one sheet and and use the spreadsheet function "QUERY" to create views.
For instance, this formula returns all the cells between A1:E from a sheet named "DataSheet" where the values in column B = 2010.
=QUERY(DataSheet!A1:E; "SELECT * WHERE B = 2010";1)
Sometimes there is a really good reason to have the data stored on two sheets. If so, use one of the vMerge functions in the script gallery to assemble a working sheet. Then create views and reports from the working sheet.
function VMerge() {
var maxw=l=0;
var minw=Number.MAX_VALUE;
var al=arguments.length ;
for( i=0 ; i<al ; i++){
if( arguments[i].constructor == Array )l =arguments[i][0].length ;
else if (arguments[i].length!=0) l = 1 ; // literal values count as array with a width of one cell, empty cells are ignored!
maxw=l>maxw?l:maxw;
minw=l<minw?l:minw;
}
if( maxw==minw) { /* when largest width equals smallest width all are equal */
var s = new Array();
for( i=0 ; i<al ; i++){
if( arguments[i].constructor == Array ) s = s.concat( arguments[i].slice() )
else if (arguments[i].length!=0) s = s.concat( [[arguments[i]]] )
}
if ( s.length == 0 ) return null ; else return s //s
}
else return "#N/A: All data ranges must be of equal width!"
}
Hope this helps.
I have datagrid with two datagridviewcombo column, one column is dynamic fill and one has fixed hardcoded values.
The problem is I can't set the value of dynamic GridViewComboBox, when I try to set it generates continues errors.
System.FormateException: DataGridViewComboBoxCell Value is not valid.
My code to load the grid is
Dim dt As DataTable
dt = GetDataTable("Select valuecol,displayCol From mytable") 'GetDataTable gives me datatable
cmbAntibiotics.DataSource = dt
cmbAntibiotics.DisplayMember = "Antibiotics"
cmbAntibiotics.ValueMember = "AntibioticsID"
Dim Index As Integer
Dim dgr As DataGridViewRow
For Each dr As DataRow In dtFromDB.Rows 'This datatable is filled with database
Index = dtFromDB.Rows.Count - 1
GRDAntimicrobials.Rows.Add()
GRDAntimicrobials.Rows(Index).Cells("cmbAntibiotics").Value = dr("AntibioticsID").ToString 'At this point it shows value (1,2,3) rather then showing its display members
GRDAntimicrobials.Rows(Index).Cells("AntibioticsStatus").Value = dr("AntibioticsStatus").ToString
Next
Pls help with me
It seems like you're trying to assign the value to whatever there is on the cell rather than instantiate the object that resides in the cell and then assign its value. I would try something like this:
Dim vComboBoxColumn As DropDownList = DirectCast(GRDAntimicrobials.Rows(index).Cells("cmbAntibiotics"))
vComboBoxColumn.Value = dr("AntibioticsStatus").ToString