Migrating to new Google spreadsheets. I have a custom formula that combines a few arrays into one array
=TRANSPOSE(SPLIT(ARRAYFORMULA(CONCATENATE('Monthly link'!A10:A&CHAR(13) , 'Monthly link'!R10:R&CHAR(13) , 'Monthly link'!AG10:AG&CHAR(13) , 'Monthly link'!AU10:AU&CHAR(13) )), CHAR(13)))
this formula works perfectly fine in the old Google spreadsheet, but in the new one, it gave me a "Error: Text result of CONCATENATE is longer than the limit of 50000 characters."
Is there a way around this? I've tried the Array_Literal formula but can't seem to get it to work, that seems like the a possible solution. But it seems the function combines arrays across and not down.
I've tried:
=array_literal('Monthly link'!A10:A,'Monthly link'!R10:R,'Monthly link'!AG10:AG,'Monthly link'!AU10:AU)
Don't know if this is working, but might be worth a shot. Someone posted this on Google Docs help forum.
/.../ If you want more than 50,000 characters in a single cell, you can use QUERY's header clause.
Example:
=ArrayFormula(query(row(A1:A70000),,100000))
This creates a cell with 408,893 characters. You can verify by using the LEN function.
Ok I fixed the above line like this, I think it works:
=ArrayFormula(query(A1:A100000,,100000))
This is provided that you have data in column A, from row 1 to row 100000. It will concatenate all of it. I guess max length is 100000 characters?
Looking back to here, you can probably nest concatenate:
=TRANSPOSE(SPLIT(ARRAYFORMULA(CONCAT(CONCATENATE('Monthly link'!A10:A&CHAR(13) ,
'Monthly link'!R10:R&CHAR(13)), CONCATENATE('Monthly link'!AG10:AG&CHAR(13) ,
'Monthly link'!AU10:AU&CHAR(13)) )), CHAR(13)))
I simply added in one more CONCATENATE to combine the strings with only one value, then use CONCAT to combine those.
EDIT
This isn't exactly a fix, but reading google documentation states that you can create an old spreadsheet by going here.
EDIT 2
Try this code:
=TRANSPOSE(SPLIT(CONCAT(ARRAYFORMULA(CONCATENATE('Monthly link'!A10:A&CHAR(13),
'Monthly link'!R10:R&CHAR(13))), ARRAYFORMULA(CONCATENATE(
'Monthly link'!AG10:AG&CHAR(13), 'Monthly link'!AU10:AU&CHAR(13)))), CHAR(13)))
It seems the error is coming from ARRAYFORMULA, has it has a limit of 50000. Not CONCATENATE or CONCAT. So, I use CONCAT to combine two different ARRAYFORMULAs that both house half of the original data. You can continue to divide these until there are even 4 ARRAYFORMULAs that all only have one dataset if need be.
EDIT 3
Currently, I am working on implementing a function in javascript found here.
You can test it currently by Tools->Script editor->Paste, then to run it go Tools->Script Manager->organizeData->Run.
I'll continue to work on it... it is currently not working, but I am close;)
EDIT 4
I finished it! You can see it here. You need to create a new script using the above instructions (Tools->Script editor->Paste), save it, then you can run it from the Script Editor window or from the spreadsheet by doing Tools->Script Manager->organizeData->Run.
What the script does is gets the data from the forms, puts it in the data to be copied, then it has a strange restriction where it requires a letter in the column to be able to copy it, so it adds a letter in so the script will fill the rows with "undefined". From there, all of the rows have "undefined" in them, so data can be copied to all of them.
If you want to know how to implement the script directly into a cell, you can just put:
=organizeData()
it will call the custom function! See here for more details.
My answer is related to another similar Q, marked as duplicete:
Text result of JOIN is longer than the limit of 50000 characters
My solution is to use the formula:
=query(joinSplit(A2:A, ";"), "select Col1, count(Col1) group by Col1", 0)
where joinSplit(A2:A, ";") is a custom formula.
The code to paste into script editor is:
function joinSplit(column, delim)
{
var result = [];
var row = [];
for (var i = 0, l = column.length; i < l; i++)
{
row = column[i].join(delim).split(delim);
row.forEach( function(elt) { result.push([elt]); } );
}
return result;
}
It will return the column of unique items.
If data is:
A;B;C;D
D;D
E;F;A;A
G;A;B;C
The result is column:
A
B
C
D
D
D
E
F
A
A
G
A
B
C
The joinSplit script SAVED MY LIFE.
I tweaked it to trim out whitespace in case your data has a ", " in it.
function joinSplit(column, delim)
{
var result = [];
var row = [];
for (var i = 0, l = column.length; i < l; i++)
{
row = column[i].join(delim).split(delim);
row.forEach( function(elt) { result.push([elt.trim()]); } );
}
return result;
}
Related
I have a sheet with a column of data where the entry is one of two strings, for simplicity's sake we'll say "A" and "B". I want another column of calculated data which is the differential between the number of "A"s and the number of "B"s up to that point, so just a countif()-countif() with the range increasing by one row. I can do this using =countif(A$2:A2, "A") - countif(A$2:A2, "B"), but it means I have to keep filling that equation down to cover any newly entered data, so I figured an array formula would be the best option to not have to do that. However when I try to use =arrayformula(countif(A$2:A, "A") - countif(A$2:A, "B")), it only populates a single cell with the difference counting the entire column. Is there a way to use an array formula so that it increases the range by one row for every row it populates?
=ARRAYFORMULA("A2:A"&ROW(A2:A))
This will give a dynamic string for the right range, which in theory, you should be able to wrap with INDIRECT, to plug into COUNTIF.
But COUNTIF only works in some circumstances with ARRAY FORMULA
Unfortunately, AFAIK, only if you use COUNTIF like this:
=ARRAYFORMULA(COUNTIF(A1:A100,A1:A100))
will it work as an array formula. Note how both the ranges in the arguments are the same.
MMULT
There may be a way along these lines with MMULT:
=ARRAYFORMULA(
MMULT(
(A2:A100 = TRANSPOSE(A2:A100)) * (ROW(A2:A100) >= TRANSPOSE(ROW(A2:A100))),
SIGN(ROW(A2:A100))
)
)
But I couldn't get that to work because I am not 100% sure on how to use MMULT in this way, but I have seen that type of solution elsewhere.
An Apps Script Workaround
As you have seen, what you want to achieve with sheet functions quickly gets very complicated. If you wanted to use more letters or different rules, it would get tough to maintain. Apps Script can make these things much simpler.
So here is a custom Apps Script function:
function myFunction() {
// Initializing
let file = SpreadsheetApp.getActive();
let sheet = file.getSheetByName("Sheet1");
var lastRow = sheet.getLastRow();
// This is the range of the As and Bs
let range = sheet.getRange(2,1, parseInt(lastRow) - 1,1)
let rows = range.getValues();
// Creating an object to keep track of the count
let tracker = {}
// This will be the output column
let newCol = []
rows.forEach(row => {
tracker[row[0]] += 1
// Adding a row to the output
newCol.push([tracker.A - tracker.B])
})
return newCol
}
Paste that into the script editor and you can use it like this:
Demo:
You can change the name of the function in the script editor according to what you need it for, just remember to call it with that name from the sheet.
References
Main Page
Sheets Guide
Tutorials
Sheets Reference
INDIRECT
MMULT
try:
=ARRAYFORMULA(
COUNTIFS(A2:A, A2:A, A2:A, "A", ROW(A2:A), "<="&ROW(A2:A))-
COUNTIFS(A2:A, A2:A, A2:A, "B", ROW(A2:A), "<="&ROW(A2:A)))
Alternatively, you may this IF statement:
=ARRAYFORMULA(IF(K2:K<>"",K2:K*B294,""))
Results (Above formula is applied on L2 row onwards):
Source: https://blog.sheetgo.com/google-sheets-formulas/arrayformula-google-sheets/
I have a row which look like this:
|Data A-1|Data A-2|Data A-3|Data A-4|Data A-5|Data A-6|Data B-1|Data B-2|Data B-3|Data B-4|Data B-5|Data B-6|Data C-1|Data C-2|Data C-3|Data C-4|Data C-5|Data C-6|
There are 5 columns that are related to each Data. I need to convert all the data in a row into fix rows and columns which look like this:
|Data A-1|Data A-2|Data A-3|Data A-4|Data A-5|Data A-6|
|Data B-1|Data B-2|Data B-3|Data B-4|Data B-5|Data B-6|
|Data C-1|Data C-2|Data C-3|Data C-4|Data C-5|Data C-6|
How can I achieve this in Google Sheets?
Example Sheet
In your case, I thought that this thread might be able to be used.
Sample formula 1:
For the goal in your question, how about the following sample formula?
=ARRAYFORMULA(TRIM(SPLIT(TRANSPOSE(SPLIT(REGEXREPLACE(TEXTJOIN(",",TRUE,A1:1),"(([\w\s\S]+?,){6})","$1#"),"#")),",")))
In this case, one row is used. So A1:1 is used as the range. But when you have several rows, please modify the range. And, the row is splitted by 6 column. So (([\w\s\S]+?,){6}) is used as the regex.
The flow of this formula is as follows.
Join all cell values by ignoring the empty cells using TEXTJOIN.
Put # to the joined text value for 6 columns using REGEXREPLACE.
Split the text value with # using SPLIT.
Transpose the splitted values using TRANSPOSE.
Split the each row with , using SPLIT.
Result:
Sample formula 2:
For your shared Spreadsheet, how about the following sample formula?
=ARRAYFORMULA(TRIM(SPLIT(TRANSPOSE(SPLIT(REGEXREPLACE(TEXTJOIN(",",TRUE,A8:8),"(([\w\s\S]+?,){8})","$1#"),"#")),",")))
In this case, one row is used. So A8:8 is used as the range. But when you have several rows, please modify the range. And, the row is splitted by 8 column. So (([\w\s\S]+?,){8}) is used as the regex.
Result:
Note:
In this case, because of REGEXREPLACE(TEXTJOIN(",",TRUE,A8:8),"(([\w\s\S]+?,){8})","$1#"), when the characters are over 50,000, an error occurs. In that case, I would like to propose to use the Google Apps Script as the custom function. The sample script is as follows. Please copy and paste the following script to the script editor of Spreadsheet, and put =SAMPLE(A8:8, 8) to a cell when your shared Spreadsheet is used. In this case, the arguments of A8:8 and 8 are the range and the splitted number, respectively. By this, your goal can be achieved.
const SAMPLE = (values, split) => values.flatMap(r => {
const temp = [];
while (r.length > 0) temp.push(r.splice(0, split));
return temp
});
References:
TEXTJOIN
REGEXREPLACE
SPLIT
TRANSPOSE
Custom Functions in Google Sheets
Added:
About your additional question by your comment as follows,
Is there any way to make the first column to be sorted?
how about the following sample formula?
Sample formula:
=SORT(ARRAYFORMULA(TRIM(SPLIT(TRANSPOSE(SPLIT(REGEXREPLACE(TEXTJOIN(",",TRUE,A8:8),"(([\w\s\S]+?,){8})","$1#"),"#")),","))),1,TRUE)
In this case, the rows are sorted by the 1st column as the ascending order.
When you use the custom formula created by Google Apps Script, you can also use SORT as follows.
=SORT(SAMPLE(A8:8, 8),1,TRUE)
Or, you can also use the following script. When you use this, please put =SAMPLE2(A8:8, 8) to a cell.
const SAMPLE2 = (values, split) => values.flatMap(r => {
const temp = [];
while (r.length > 0) temp.push(r.splice(0, split));
return temp.sort((a, b) => a[0] - b[0]);
});
or try:
=QUERY(
{FLATTEN(FILTER(A8:15, MOD(COLUMN(A1:1)-1, 8)=0)),
FLATTEN(FILTER(A8:15, MOD(COLUMN(A1:1)-2, 8)=0)),
FLATTEN(FILTER(A8:15, MOD(COLUMN(A1:1)-3, 8)=0)),
FLATTEN(FILTER(A8:15, MOD(COLUMN(A1:1)-4, 8)=0)),
FLATTEN(FILTER(A8:15, MOD(COLUMN(A1:1)-5, 8)=0)),
FLATTEN(FILTER(A8:15, MOD(COLUMN(A1:1)-6, 8)=0)),
FLATTEN(FILTER(A8:15, MOD(COLUMN(A1:1)-7, 8)=0)),
FLATTEN(FILTER(A8:15, MOD(COLUMN(A1:1)-8, 8)=0))},
"where Col1 is not null", 0)
Migrating to new Google spreadsheets. I have a custom formula that combines a few arrays into one array
=TRANSPOSE(SPLIT(ARRAYFORMULA(CONCATENATE('Monthly link'!A10:A&CHAR(13) , 'Monthly link'!R10:R&CHAR(13) , 'Monthly link'!AG10:AG&CHAR(13) , 'Monthly link'!AU10:AU&CHAR(13) )), CHAR(13)))
this formula works perfectly fine in the old Google spreadsheet, but in the new one, it gave me a "Error: Text result of CONCATENATE is longer than the limit of 50000 characters."
Is there a way around this? I've tried the Array_Literal formula but can't seem to get it to work, that seems like the a possible solution. But it seems the function combines arrays across and not down.
I've tried:
=array_literal('Monthly link'!A10:A,'Monthly link'!R10:R,'Monthly link'!AG10:AG,'Monthly link'!AU10:AU)
Don't know if this is working, but might be worth a shot. Someone posted this on Google Docs help forum.
/.../ If you want more than 50,000 characters in a single cell, you can use QUERY's header clause.
Example:
=ArrayFormula(query(row(A1:A70000),,100000))
This creates a cell with 408,893 characters. You can verify by using the LEN function.
Ok I fixed the above line like this, I think it works:
=ArrayFormula(query(A1:A100000,,100000))
This is provided that you have data in column A, from row 1 to row 100000. It will concatenate all of it. I guess max length is 100000 characters?
Looking back to here, you can probably nest concatenate:
=TRANSPOSE(SPLIT(ARRAYFORMULA(CONCAT(CONCATENATE('Monthly link'!A10:A&CHAR(13) ,
'Monthly link'!R10:R&CHAR(13)), CONCATENATE('Monthly link'!AG10:AG&CHAR(13) ,
'Monthly link'!AU10:AU&CHAR(13)) )), CHAR(13)))
I simply added in one more CONCATENATE to combine the strings with only one value, then use CONCAT to combine those.
EDIT
This isn't exactly a fix, but reading google documentation states that you can create an old spreadsheet by going here.
EDIT 2
Try this code:
=TRANSPOSE(SPLIT(CONCAT(ARRAYFORMULA(CONCATENATE('Monthly link'!A10:A&CHAR(13),
'Monthly link'!R10:R&CHAR(13))), ARRAYFORMULA(CONCATENATE(
'Monthly link'!AG10:AG&CHAR(13), 'Monthly link'!AU10:AU&CHAR(13)))), CHAR(13)))
It seems the error is coming from ARRAYFORMULA, has it has a limit of 50000. Not CONCATENATE or CONCAT. So, I use CONCAT to combine two different ARRAYFORMULAs that both house half of the original data. You can continue to divide these until there are even 4 ARRAYFORMULAs that all only have one dataset if need be.
EDIT 3
Currently, I am working on implementing a function in javascript found here.
You can test it currently by Tools->Script editor->Paste, then to run it go Tools->Script Manager->organizeData->Run.
I'll continue to work on it... it is currently not working, but I am close;)
EDIT 4
I finished it! You can see it here. You need to create a new script using the above instructions (Tools->Script editor->Paste), save it, then you can run it from the Script Editor window or from the spreadsheet by doing Tools->Script Manager->organizeData->Run.
What the script does is gets the data from the forms, puts it in the data to be copied, then it has a strange restriction where it requires a letter in the column to be able to copy it, so it adds a letter in so the script will fill the rows with "undefined". From there, all of the rows have "undefined" in them, so data can be copied to all of them.
If you want to know how to implement the script directly into a cell, you can just put:
=organizeData()
it will call the custom function! See here for more details.
My answer is related to another similar Q, marked as duplicete:
Text result of JOIN is longer than the limit of 50000 characters
My solution is to use the formula:
=query(joinSplit(A2:A, ";"), "select Col1, count(Col1) group by Col1", 0)
where joinSplit(A2:A, ";") is a custom formula.
The code to paste into script editor is:
function joinSplit(column, delim)
{
var result = [];
var row = [];
for (var i = 0, l = column.length; i < l; i++)
{
row = column[i].join(delim).split(delim);
row.forEach( function(elt) { result.push([elt]); } );
}
return result;
}
It will return the column of unique items.
If data is:
A;B;C;D
D;D
E;F;A;A
G;A;B;C
The result is column:
A
B
C
D
D
D
E
F
A
A
G
A
B
C
The joinSplit script SAVED MY LIFE.
I tweaked it to trim out whitespace in case your data has a ", " in it.
function joinSplit(column, delim)
{
var result = [];
var row = [];
for (var i = 0, l = column.length; i < l; i++)
{
row = column[i].join(delim).split(delim);
row.forEach( function(elt) { result.push([elt.trim()]); } );
}
return result;
}
How do you arrayformula() a sum() such as:
=sum(A1:H1)
I need to go down 1000 rows.
Another option:
=ArrayFormula(SUMIF(IF(COLUMN(A1:H1),ROW(A1:A1000)),ROW(A1:A1000),A1:H1000))
Of the two answers that work, Jacob Jan Tuinstra and AdamL, Jacob gives a better answer. Jacob's runs faster and is easier to remember.
However, why use crazy formulas when it is much easier to use Google Sheets to the fullest?
=ARRAYFORMULA(A2:A+B2:B+C2:C+D2:D+E2:E+F2:F+G2:G+H2:H)
In the foregoing formula, you can use named ranges instead of the ranges by reference.
=ARRAYFORMULA(range1+range2+range3+range4+range5+range6+range7+range8)
As well, you can sum across rows that span sheets rather than being stuck working with columns within the same sheet.
To fix the formula to block returning zeros for blank rows, use this:
=arrayFormula(if(isNumber(A2:A),A2:A+B2:B+C2:C+D2:D+E2:E+F2:F,G2:G,H2:H))
See: See Ahab's answer on Google Forums
For a cool sum accumulation formula, see Otávio Alves Ribeiro's answer on Google Forums
This is what you are looking for:
=MMULT(A1:H1000,TRANSPOSE(ARRAYFORMULA(COLUMN(A1:H1000)^0)))
See this answer on Web Application I gave: https://webapps.stackexchange.com/a/53419/29140
Note: tried it on the new Google Spreadsheet, without succes.
with new functions in google sheets (since 20 Sep, 2022) all you need is:
=BYROW(A:H; LAMBDA(x; SUM(x)))
Summing A-H horizontal and running down for 523 lines:
=ARRAYFORMULA(iferror(mmult(A1:H523;TRANSPOSE(column(A1:H1))^0)))
if I look at this formula I really think the following might be simpler. Add this to Tools > Script Editor:
function row_sum(range_to_sum_per_row) {
var result_column = [];
for (var r = 0; r < range_to_sum_per_row.length; r++) {
var row_sum = parseFloat(0);
for (var c = 0; c < range_to_sum_per_row[r].length; c++) {
row_sum += range_to_sum_per_row[r][c] || 0;
}
result_column.push([row_sum]);
}
return result_column;
}
use this like so for performance reasons, where C:H is the range you want to sum up and A:A is a column that does not contain an empty string:
=row_sum(filter(C2:H, len(A2:A)>0))
If you want to be able to add rows and sum to the last row for all values in A1:H, you can use:
=ArrayFormula(SUMIF(IF(COLUMN(A1:H1),ROW(A1:A)),ROW(A1:A),A1:H))
Alternatively, if you want be be able to add rows and columns to the spreadsheet and sum to the last of both this can also be done. Paste the following code into any cell and it will create a column of summed values for all cells in each row below and to the right of pasted cell:
=arrayformula(SUMIF(IF(COLUMN(indirect(concatenate(REGEXREPLACE(address(row(),column()+1),"[^[:alpha:]]", ""),VALUE(REGEXREPLACE(address(row(),column()),"[^[:digit:]]", "")),":",VALUE(REGEXREPLACE(address(row(),column()),"[^[:digit:]]", ""))))),ROW(indirect(concatenate(REGEXREPLACE(address(row(),column()+1),"[^[:alpha:]]", ""),VALUE(REGEXREPLACE(address(row(),column()),"[^[:digit:]]", "")),":",REGEXREPLACE(address(row(),column()+1),"[^[:alpha:]]", ""))))),ROW(indirect(concatenate(REGEXREPLACE(address(row(),column()+1),"[^[:alpha:]]", ""),VALUE(REGEXREPLACE(address(row(),column()),"[^[:digit:]]", "")),":",REGEXREPLACE(address(row(),column()+1),"[^[:alpha:]]", "")))),indirect(concatenate(concatenate(REGEXREPLACE(address(row(),column()+1),"[^[:alpha:]]", ""),VALUE(REGEXREPLACE(address(row(),column()),"[^[:digit:]]", "")),":"),address(rows($A:$A),columns($1:$1))))))
Using Query
=INDEX(TRANSPOSE(
QUERY(TRANSPOSE(FILTER(A2:H,A2:A<>"")),
"select sum(Col"&JOIN("), sum(Col",SEQUENCE(COUNTA(A2:A)))&")",0)
),,2)
notes:
generating query string on the fly
Using DSUM:
=ARRAYFORMULA(DSUM(
TRANSPOSE(FILTER({A2:A,A2:H},A2:A<>"")),
SEQUENCE(COUNTA(A2:A)),{IFERROR(1/0);IFERROR(1/0)}))
notes:
{IFERROR(1/0);IFERROR(1/0)} is to make zero creteria for DSUM.
{A2:A,A2:H} -- added fake column for DSUM to mimic header column.
may be able to cahnge the formula into DMAX or DAVERAGE
Answer similar to #adamL suggestion, but removing the internal if.
=ArrayFormula(
sumif(ROW(A1:A10)*COLUMN(A1:H1)^0,ROW(A1:A10),A1:A10)
)
In this case I use ROW(A1:A10) * COLUMN(A1:H1)^0 to generate the row number matriz. To understand how it works, you can test just this part in your Google sheets:
= ArrayFormula(ROW(A1:A10) * COLUMN(A1:H1)^0)
So, with your row matrix, sumif can operate for each line, is the line has the matched row number, it will be summed up.
Let us not complicate this.
Simply put parenthesis in each of the array in the range.
=arrayformula( Sum( (A1:A):(H1:H) )
This spans not only upto 1000 rows but upto infiinity.
If you really want to limit then go
=arrayformula( Sum( (A1:A1000):(H1:H1000) )
I use the following function
=DAYS360(A2, A35)
to calculate the difference between two dates in my column. However, the column is ever expanding and I currently have to manually change 'A35' as I update my spreadsheet.
Is there a way (in Google Sheets) to find the last non-empty cell in this column and then dynamically set that parameter in the above function?
There may be a more eloquent way, but this is the way I came up with:
The function to find the last populated cell in a column is:
=INDEX( FILTER( A:A ; NOT( ISBLANK( A:A ) ) ) ; ROWS( FILTER( A:A ; NOT( ISBLANK( A:A ) ) ) ) )
So if you combine it with your current function it would look like this:
=DAYS360(A2,INDEX( FILTER( A:A ; NOT( ISBLANK( A:A ) ) ) ; ROWS( FILTER( A:A ; NOT( ISBLANK( A:A ) ) ) ) ))
To find the last non-empty cell you can use INDEX and MATCH functions like this:
=DAYS360(A2; INDEX(A:A; MATCH(99^99;A:A; 1)))
I think this is a little bit faster and easier.
If A2:A contains dates contiguously then INDEX(A2:A,COUNT(A2:A)) will return the last date. The final formula is
=DAYS360(A2,INDEX(A2:A,COUNT(A2:A)))
Although the question is already answered, there is an eloquent way to do it.
Use just the column name to denote last non-empty row of that column.
For example:
If your data is in A1:A100 and you want to be able to add some more data to column A, say it can be A1:A105 or even A1:A1234 later, you can use this range:
A1:A
So to get last non-empty value in a range, we will use 2 functions:
COUNTA
INDEX
The answer is =INDEX(B3:B,COUNTA(B3:B)).
Here is the explanation:
COUNTA(range): Returns number of values in a range, we can use this to get the count of rows.
INDEX(range, row, col): Returns the content of a cell, specified by row and column offset. If the column is omitted then the whole row is returned.
Examples:
INDEX(A1:C5,1,1) = A1
INDEX(A1:C5,1) = A1,B1,C1 # Whole row since the column is not specified
INDEX(A1:C5,1,2) = B1
INDEX(A1:C5,1,3) = C1
INDEX(A1:C5,2,1) = A2
INDEX(A1:C5,2,2) = B2
INDEX(A1:C5,2,3) = C2
INDEX(A1:C5,3,1) = A3
INDEX(A1:C5,3,2) = B3
INDEX(A1:C5,3,3) = C3
For the picture above, our range will be B3:B. So we will count how many values are there in range B3:B by COUNTA(B3:B) first. In the left side, it will produce 8 since there are 8 values while it will produce 9 in the right side. We also know that the last value is in the 1st column of the range B3:B so the col parameter of INDEX must be 1 and the row parameter should be COUNTA(B3:B).
PS: please upvote #bloodymurderlive's answer since he wrote it first, I'm just explaining it here.
My favorite is:
=INDEX(A2:A,COUNTA(A2:A),1)
So, for the OP's need:
=DAYS360(A2,INDEX(A2:A,COUNTA(A2:A),1))
If the column expanded only by contiguously added dates
as in my case - I used just MAX function to get last date.
The final formula will be:
=DAYS360(A2; MAX(A2:A))
Here's another one:
=indirect("A"&max(arrayformula(if(A:A<>"",row(A:A),""))))
With the final equation being this:
=DAYS360(A2,indirect("A"&max(arrayformula(if(A:A<>"",row(A:A),"")))))
The other equations on here work, but I like this one because it makes getting the row number easy, which I find I need to do more often. Just the row number would be like this:
=max(arrayformula(if(A:A<>"",row(A:A),"")))
I originally tried to find just this to solve a spreadsheet issue, but couldn't find anything useful that just gave the row number of the last entry, so hopefully this is helpful for someone.
Also, this has the added advantage that it works for any type of data in any order, and you can have blank rows in between rows with content, and it doesn't count cells with formulas that evaluate to "". It can also handle repeated values. All in all it's very similar to the equation that uses max((G:G<>"")*row(G:G)) on here, but makes pulling out the row number a little easier if that's what you're after.
Alternatively, if you want to put a script on your sheet you can make it easy on yourself if you plan on doing this a lot. Here's that scirpt:
function lastRow(sheet,column) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
if (column == null) {
if (sheet != null) {
var sheet = ss.getSheetByName(sheet);
} else {
var sheet = ss.getActiveSheet();
}
return sheet.getLastRow();
} else {
var sheet = ss.getSheetByName(sheet);
var lastRow = sheet.getLastRow();
var array = sheet.getRange(column + 1 + ':' + column + lastRow).getValues();
for (i=0;i<array.length;i++) {
if (array[i] != '') {
var final = i + 1;
}
}
if (final != null) {
return final;
} else {
return 0;
}
}
}
Here you can just type in the following if you want the last row on the same of the sheet that you're currently editing:
=LASTROW()
or if you want the last row of a particular column from that sheet, or of a particular column from another sheet you can do the following:
=LASTROW("Sheet1","A")
And for the last row of a particular sheet in general:
=LASTROW("Sheet1")
Then to get the actual data you can either use indirect:
=INDIRECT("A"&LASTROW())
or you can modify the above script at the last two return lines (the last two since you would have to put both the sheet and the column to get the actual value from an actual column), and replace the variable with the following:
return sheet.getRange(column + final).getValue();
and
return sheet.getRange(column + lastRow).getValue();
One benefit of this script is that you can choose if you want to include equations that evaluate to "". If no arguments are added equations evaluating to "" will be counted, but if you specify a sheet and column they will now be counted. Also, there's a lot of flexibility if you're willing to use variations of the script.
Probably overkill, but all possible.
This works for me. Get last value of the column A in Google sheet:
=index(A:A,max(row(A:A)*(A:A<>"")))
(It also skips blank rows in between if any)
This seems like the simplest solution that I've found to retrieve the last value in an ever-expanding column:
=INDEX(A:A,COUNTA(A:A),1)
For strictly finding the last non-empty cell in a column, this should work...
=LOOKUP(2^99, A2:A)
What about this formula for getting the last value:
=index(G:G;max((G:G<>"")*row(G:G)))
And this would be a final formula for your original task:
=DAYS360(G10;index(G:G;max((G:G<>"")*row(G:G))))
Suppose that your initial date is in G10.
I went a different route. Since I know I'll be adding something into a row/column one by one, I find out the last row by first counting the fields that have data. I'll demonstrate this with a column:
=COUNT(A5:A34)
So, let's say that returned 21. A5 is 4 rows down, so I need to get the 21st position from the 4th row down. I can do this using inderect, like so:
=INDIRECT("A"&COUNT(A5:A34)+4)
It's finding the amount of rows with data, and returning me a number I'm using as an index modifier.
for a row:
=ARRAYFORMULA(INDIRECT("A"&MAX(IF(A:A<>"", ROW(A:A), ))))
for a column:
=ARRAYFORMULA(INDIRECT(ADDRESS(1, MAX(IF(1:1<>"", COLUMN(1:1), )), 4)))
This will give the contents of the last cell:
=indirect("A"&max(ARRAYFORMULA(row(a:a)*--(a:a<>""))))
This will give the address of the last cell:
="A"&max(ARRAYFORMULA(row(a:a)*--(a:a<>"")))
This will give the row of the last cell:
=max(ARRAYFORMULA(row(a:a)*--(a:a<>"")))
Maybe you'd prefer a script. This script is way shorter than the huge one posted above by someone else:
Go to script editor and save this script:
function getLastRow(range){
while(range.length>0 && range[range.length-1][0]=='') range.pop();
return range.length;
}
One this is done you just need to enter this in a cell:
=getLastRow(A:A)
Calculate the difference between latest date in column A with the date in cell A2.
=MAX(A2:A)-A2
To find last nonempty row number (allowing blanks between them) I used below to search column A.
=ArrayFormula(IFNA(match(2,1/(A:A<>""))))
The way an amateur does it is "=CONCATENATE("A",COUNTUNIQUE(A1:A9999))", where A1 is the first cell in the column, and A9999 is farther down that column than I ever expect to have any entries. This resultant A# can be used with the INDIRECT function as needed.
Ben Collins is a Google sheets guru, he has many tips on his site for free and also offers courses. He has a free article on dynamic range names and I have used this as the basis for many of my projects.
https://www.benlcollins.com/formula-examples/dynamic-named-ranges/
Disclaimer, I have nothing to gain by referring Ben's site.
Here is a screenshot of one of my projects using dynamic ranges:
Cell D3 has this formula which was shown above except this is as an array formula:
=ArrayFormula(MAX(IF(L2s!A2:A1009<>"",ROW(2:1011))))
Cell D4 has this formula:
="L2s!A2:E"&D3
This may work:
=DAYS360(A2,INDEX(A2:A,COUNTA(A2:A)))
To pick the last in a column of arbitrary, non-empty values ignoring the header cell (A1):
=INDEX(A2:A,COUNT(A2:A))
With the introduction of LAMBDA and REDUCE functions we can now compute the row number in a single pass through the cells (Several of the solutions above filter the range twice.) and without relying on magic text or numeric values.
=lambda(rng,
REDUCE(0, rng, lambda(maxrow, cell, if(isblank(cell),maxrow,row(cell)) ) )
)(A:A)
which can be nicely packaged into a Named Function for usage like
=LAST_ROWNUM(A:A)
It works on columns with interspersed blanks, and multi-column ranges (because REDUCE iterates over the range in row-first), and partial columns (like A20:A), still returning the actual row number (not the offset within the range).
This can then be combined with Index to return the value
=DAYS360(A2, Index(A1, LAST_ROWNUM(A:A)))
(In truth, though, I suspect that the OPs date values are monotonic (even if with blanks in between), and that he could get away with
=DAYS360(A2, MAX(A2:A))
This solution is identified above as relying on the dates being "contiguous" - whether that means "no blanks" or "no missing dates" I'm not certain - but either stipulation is not necessary.)