Create single formula for several different lines (array) (Google Sheets) - google-sheets

In the AD column I have this sequence of values:
2
3
4
These values refer to rows in a column on another page.
In each line in AE column I use this formula:
=IF(AD1="","",IFERROR(SUM(FILTER(INDIRECT("'Registro Geral'!O2:O"&AD1)/100,REGEXMATCH(INDIRECT("'Registro Geral'!H2:H"&AD1),SUBSTITUTE(SUBSTITUTE(JOIN("|",$V$1:$V$4),"||",""),"|||",""))=TRUE))))
=IF(AD2="","",IFERROR(SUM(FILTER(INDIRECT("'Registro Geral'!O2:O"&AD2)/100,REGEXMATCH(INDIRECT("'Registro Geral'!H2:H"&AD2),SUBSTITUTE(SUBSTITUTE(JOIN("|",$V$1:$V$4),"||",""),"|||",""))=TRUE))))
=IF(AD3="","",IFERROR(SUM(FILTER(INDIRECT("'Registro Geral'!O2:O"&AD3)/100,REGEXMATCH(INDIRECT("'Registro Geral'!H2:H"&AD3),SUBSTITUTE(SUBSTITUTE(JOIN("|",$V$1:$V$4),"||",""),"|||",""))=TRUE))))
In short, this formula is getting a running Sum of values in the other sheet based on whether or not the corresponding cell in another column of the same sheet appears in a set of values.
When I try to add ARRAYFORMULA so that I don't have to have a formula on each line, leaving only in AE1, the values that return on all lines are exactly the same value.
Test Formula Fail:
=ARRAYFORMULA(IF(AD1:AD="","",IFERROR(SUM(FILTER(INDIRECT("'Registro Geral'!O2:O"&AD1:AD)/100,REGEXMATCH(INDIRECT("'Registro Geral'!H2:H"&AD1:AD),SUBSTITUTE(SUBSTITUTE(JOIN("|",$V$1:$V$4),"||",""),"|||",""))=TRUE)))))
Link to Spreadhseet example:
https://docs.google.com/spreadsheets/d/1qIv6KnLv-EwJQXRrk7ucuqY-XuJhkIHOCtih9FpAg6U/edit?usp=sharing

You're trying to do a running summation on O based on whether the corresponding value in the H column appears in the Filtered values.
We can do this with a matrix multiplication using a lower-triangular matrix and the listed values, selecting which ones to zero out based on certain conditions using IF.
=ArrayFormula(MMULT(
N(SEQUENCE(D2)>=SEQUENCE(1,D2)),
ARRAY_CONSTRAIN(
IF(
('Registro Geral'!O2:O<>"")*
IFNA(MATCH('Registro Geral'!H2:H,V:V,0)),
'Registro Geral'!O2:O
)/100,
D2,
1
)
))
Why this works
The lower-triangular matrix looks like
1 0 0 0 0 ... up to N columns
1 1 0 0 0
1 1 1 0 0
1 1 1 1 0
1 1 1 1 1
... up to N rows
The Column you want to sum looks like
Value 1
Value 2
...
Value N
So when you multiply the two, you get a new matrix of dimension N x 1:
Value 1
Value 1 + Value 2
...
Value 1 + ... + Value N
If we don't want to sum a value, then we can zero it out with a conditional so that it never gets added.

Related

Google Sheets: Query and list the last 5 values in a column if the column contains a number

I want to use Sparkline for a spreadsheet to show a trend of the last 5 soccer matches, where A and B are the goals, and C are the resulting points.
In column C, the points are only generated if values are entered for the goals and goals conceded, i.e. the columns are not empty.
A (Goals)
B (Conceded)
C (Points)
4
4
1
4
4
1
4
4
0
3
4
4
1
0
4
0
As you see, in row 3, column c is empty.
What I basically try to achieve, is to create a list where the last 5 entries which are not empty / null, are listed:
C (Points)
1
1
3
1
0
Is used this formula, but it somehow does not work
=query(J15:J114,"select * offset "&count(J15:J114)-5)
shorturl.at/gHPY9 (example result picture)
Tried to find a solution myself, but am stuck.
Best,
Feal
Use query() with a where clause, like this:
=query(
J15:J114,
"where J is not null
offset " & max(0, count(J15:J114) - 5),
0
)

Count number of items in each row, multiply by their value and then sum over rows, all in one formula

I have a table of the following type:
Price
Item 1
Item 2
Item 3
Item 4
50
2
2
1
1
75
1
2
2
2
What I'd like to do in Google Sheets is to (a) sum the number of items with a certain ID in each row, (b) multiply the number with the price in each row, and then (c) sum over the results in each row. So for ID 1, the formula should give 2 x 50 + 1 x 75 = 175, and for ID 2 it should give 2 x 50 + 3 x 75 = 325.
This is straight forward if one breaks this up in two steps by adding additional columns, but I have so many item columns and item IDs that this is infeasible. I looked at ways to use COUNTIF row-wise using arrayformula, but couldn't get it to work in conjunction with the multiplication in each row.
Is there a way to do steps (a-c) in a single formula in Google sheets?
Use MMULT and SUMPRODUCT
with id in row 1 (G1 and G2)
=arrayformula(SUMPRODUCT(mmult(IF($B$2:$E=G1,1,0),transpose(column($B$2:$E)^0)),$A$2:$A))
drag to the right

Is there a way to use same field as rows and columns in google sheets to count unique occurrence between columns?

Looking to convert
Task id
John
Jan
Juliet
1
1
1
0
2
1
0
1
3
0
1
1
4
0
0
1
5
0
1
1
6
1
1
0
7
0
1
0
8
1
0
0
9
0
1
1
10
1
1
0
To
John
Jan
Juliet
John
3
1
Jan
3
3
Juliet
1
3
I have set up a new sheet ("Erik Help") in your sample spreadsheet.
In B1:
=SORT(FILTER(Sheet1!B1:1,Sheet1!B1:1<>""))
This simply fills the top row with your names list, sorted alphabetically.
In A2:
=TRANSPOSE(SORT(FILTER(Sheet1!B1:1,Sheet1!B1:1<>"")))
This fills A2 down with the same names list as above, just vertically.
In B2 is the main formula for the grid (which is then dragged over and down):
=ArrayFormula(IF( ($A2="") + (B$1="") + ($A2=B$1),, SUM(MMULT(IF((FILTER(Sheet1!$B$2:$L,Sheet1!$A$2:$A<>"")=1) * (Sheet1!$B$1:$L$1=$A2),1,0), SEQUENCE(COLUMNS(Sheet1!$B$1:$L$1),1,1,0)) * MMULT(IF((FILTER(Sheet1!$B$2:$L,Sheet1!$A$2:$A<>"")=1) * (Sheet1!$B$1:$L$1=B$1),1,0), SEQUENCE(COLUMNS(Sheet1!$B$1:$L$1),1,1,0)))))
The first ( ) + ( ) + ( ) tests three OR conditions. If any is true, the cell will be left blank. This is what allows the formula to be dragged all the way right and down without throwing errors and, in essence, "waiting" for new data from the first two formulas above that it can process.
The rest of the formula is too complex to warrant full explanation (e.g., how MMULT works in detail), this being a volunteer-run site. (Writing the formula took more time than I generally spend in a day on this or other forums.) But here's the gist.
Two grids — each formed by an MMULT (matrix multiplication) — are SUMmed. The first MMULT will produce a grid the same size as the Sheet1 grid, filled with 1 only if two conditions are met: that there was already a 1 in that slot and that the name above matches the name to the right in the "Erik Help" grid. Otherwise, the result for that slot is a zero. The second MMULT forms the same size grid based on the same conditions, only this time it gets a 1 only if there is already a 1 and the name above matches the name above the cell in "Erik Help." These two grids are multiplied, and if the product is a 1, we know that BOTH names had a 1 there. Once SUMmed, we get the count of shared projects for those two names.
As this formula is dragged, cell references not locked with a dollar sign will adjust, so that two different names will be compared by the two MMULT grids.
Because this solution requires comparing arrays with arrays with arrays, I don't currently see how a further array solution is possible, hence the need for the formulas to be dragged. That is, each of these formulas is already jam-packed with array processing.
Again, the formula is currently dragged all the way to Column Z and down to Row 200. However, it only references up to Column L (which is as far as your current names list goes). If your real world application has more names and thus carries over past Column L, the easiest way to change all of the formulas at once is this:
Go to the "Erik Help" sheet (which you can, of course, rename as you like).
Hit Ctrl-H to open the Find/Replace dialog box.
Enter $L in the FIND field and $? in the REPLACE field (where ? will be the new column to which you want the results to extend, e.g., $M or $P, etc.)
Choose "This sheet" from the "Search" drop-down.
Check the box next to "Also search within formulas."
Click the "Replace all" button.
If the data set shrinks or grows again, do the same steps, just changing the old furthest column reference for the new furthest column reference.
Here is a super-simple way of doing it which just changes the pair of columns selected in the countifs as the formula moves across and down by relative addressing:
=countifs(index($B$2:$D,0,row(A1)),1,index($B$2:$D,0,column(A1)),1)
pulled down and across.
Attempt at more general solution.
The question is tagged pivot-table. Although a pivot table approach seems useful, the data is in exactly the wrong format to achieve it. The task would be to transform the data from ones and zeroes to column numbers so
1 1 0 => 1 2
1 0 1 => 1 3
1 1 1 => 1 2, 1 3 and 2 3.
This can be achieved by generating pairs of numbers as follows and performing a lookup in the original data:
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
The formulas to generate these sequences are
=ArrayFormula(quotient(mod(sequence(90,1,0),9),3)+1)
and
=ArrayFormula(mod(sequence(90,1,0),3)+1)
(9 because there are 3X3 pairs per row of data, 90 because there are 10 rows of data).
The following generates a lookup for each row of data
=ArrayFormula(quotient(sequence(90,1,0),9)+1)
Putting all this together and wrapping it in a pivot query gives
=ArrayFormula(query({vlookup(quotient(sequence(90,1,0),9)+2,{row(B2:D),B2:D},quotient(mod(sequence(90,1,0),9),3)+2,0)*(quotient(mod(sequence(90,1,0),9),3)+1),
vlookup(quotient(sequence(90,1,0),9)+2,{row(B2:D),B2:D},mod(sequence(90,1,0),3)+2,0)*(mod(sequence(90,1,0),3)+1)},
"select count(Col1) where Col1<>0 and Col2<>0 group by Col1 pivot Col2"))
The formula can be generalised to different numbers of rows and columns.

Looping through a set of google sheet values

I have 2 sets of data. One is tank names
Tank Name
A1
A2
B1
B2
and the next is ON/OFF Data
ON/OFF
0
1
1
1
1
1
0
0
1
1
1
1
1
1
1
0
0
1
1
1
0
1
1
Now the result I am looking is, when the ON/OFF is 1 then the first tank is to be mentioned: when it's 0, no tank to be mentioned. Once all the tanks are mentioned,then it should again start from the first tank ie A1.But if 0 comes in between then it should start again from A1 .. like this
Result expected
0
1 A1
1 A2
1 B1
1 B2
1 A1
0
0
1 A1
1 A2
1 B1
1 B2
1 A1
1 A2
1 B1
0
0
1 A1
1 A2
1 B1
0
1 A1
1 A2
You can check the google sheet here : Scenario 2 https://docs.google.com/spreadsheets/d/1SP2SfA-bzzhHgfrvpyUIkeQfUykata0oHxyD-x69yxE/edit?usp=sharing
Hope to get some help to get this solved.
Thanks
Edit
Single formula solution:
=FILTER(IF(B1:B=0,"",VLOOKUP(IFERROR(VLOOKUP(SUMIF(ROW(B1:B),"<="&ROW(B1:B),B1:B),{ROW(INDIRECT("a1:a"&COUNTIF(B:B,1))) , TRANSPOSE(SPLIT(TEXTJOIN("",1,TRANSPOSE(TEXT(ROW(INDIRECT("a1:a"&MAX(LEN(SPLIT(TEXTJOIN("",1,B1:B),"0")))))*(LEN(SPLIT(TEXTJOIN("",1,B1:B),"0"))>=ROW(INDIRECT("a1:a"&MAX(LEN(SPLIT(TEXTJOIN("",1,B1:B),"0")))))),"0"","";;"))),",")) },2,),0),{row(INDIRECT("a1:a"&COUNTIF(B1:B,1))) , VLOOKUP(MOD(ROW( INDIRECT("a1:a"&COUNTIF(B1:B,1)) )-1,COUNTA(A1:A))+1,{ROW(A1:A)-row(A1)+1,A1:A},2,)},2,)),B1:B<>"")
Two formulas will do it.
#1 in C1. For cumulative sum with a loop:
=Filter(IFERROR(VLOOKUP(SUMIF(ROW(B1:B),"<="&ROW(B1:B),B1:B),{ROW(INDIRECT("a1:a"&COUNTIF(B:B,1))) , TRANSPOSE(SPLIT(TEXTJOIN("",1,TRANSPOSE(TEXT(ROW(INDIRECT("a1:a"&MAX(LEN(SPLIT(TEXTJOIN("",1,B1:B),"0")))))*(LEN(SPLIT(TEXTJOIN("",1,B1:B),"0"))>=ROW(INDIRECT("a1:a"&MAX(LEN(SPLIT(TEXTJOIN("",1,B1:B),"0")))))), "0"","";;"))),",")) },2,),0),B1:B<>"")
#2 in D1. For the result:
=FILTER(IF(B1:B=0,"",VLOOKUP(C1:C,{row(INDIRECT("a1:a"&COUNTIF(B1:B,1))) , VLOOKUP(MOD(ROW( INDIRECT("a1:a"&COUNTIF(B1:B,1)) )-1,COUNTA(A1:A))+1,{ROW(A1:A)-row(A1)+1,A1:A},2,)},2,)),B1:B<>"")
References:
Counters Lab
Array Formulas Lab
You can do it most easily with a helper column
=if(B4=1,if(B3=0,0,mod(E3+1,4)),"")
starting in E4 then
=if(E4<>"",index(A$4:A$7,E4+1),"")
starting in F4.
Here's one way of doing it with a single non-array formula:
=IF(B4=1,INDEX(A$4:A$7,MOD(SUM(B4:INDEX(B:B,MAX(INDEX(IF(B$4:B4=0,ROW(B$4:B4)),0)),0))-1,4)+1),"")
in (say) G4 and pulled down.
NB both of these assume that the sequence in column B starts with a zero and would need adjustment if this is not the case.
An array formula is also possible:
=ArrayFormula(if(B4:B=1,vlookup(mod(sumif(row(B4:B),"<="&row(B4:B),B4:B)-sumif(row(B4:B),"<="&vlookup(row(B4:B),if(B4:B=0,row(B4:B)),1),B4:B)-1,4)+4,{row(4:7),A4:A7},2,false),""))
EDIT
Probably the easiest way to get it to work when the first row of on/off data contains a 1 is to include the header row in column B and use n() to treat it as zero so the helper column formula is
=if(B4=1,if(n(B3)=0,0,mod(E3+1,4)),"")
and the non-array formula is
=IF(B4=1,INDEX(A$4:A$7,MOD(SUM(B4:INDEX(B:B,MAX(INDEX(IF(n(B$3:B4)=0,ROW(B$3:B4)),0)),0))-1,4)+1),"")
The array formula works without any change. This is for the fortuitous reason that, while the second Vlookup will fail with #N/A if there are no zeroes in column B before the current row, when this is passed to Sumif you get a zero result which is what you need.

Negative References or reversing order of column for DATEDIF

I have a ascending sorted list of irregular dates in Column A:A:
A B C D (A:A,A2:A) E (A:A,A3:A)
2017-11-09 10 10 NA NA
2017-11-10 11 21 1 NA
2017-11-14 15 36 4 5
2017-11-15 22 58 1 5
Column C:C is a rolling sum of B:B. I'm trying to get arrayformula in D:D/E:E to find the datedif between current row (starting date) and X rows above (end date):
=ArrayFormula(DATEDIF(B:B-(X Rows),B:B,"D"))
The goal is to find range of change in D:D over X amount of days:
D:D - D:D-rowX / datedif (A:A-rowX, A:A)
i.e for 2 days on row C4:
(C4-C2) / datedif(C4-2,C4,"D")
(58-21) / datedif(C2,C4,"D")
37 / 5 = 7.4
for 5 days on row C10:
(C10-C5) / datedif(C10-5,C10,"D")
for 15 days on row C20:
(C20-C5) / datedif(C20-15,C20,"D")
I'm trying to calculate X for 1,2,3,4,7,28 rows up which means the array has to start that 1,2,3,4,7,28 rows down.
Right now, the array bugs out to bad reference because the first starting date is DATEDIF(B-X,B1,"D") where B-X is a invalid negative reference. Arrayformulas with bad values instead of bad references seems to just skip past errors and starts working once input are valid. But I can't figure out how to skip bad references. I've tried forcing start date with INDIRECT but can't get it to recognize value as a date. I also tried DATEDIF(B:B, B:B+X,"D"), which spits out the correct numbers but results are offset by X rows. I've tried reverse sorting A:A, =ArrayFormula(if(len(A:A),DATEDIF(SORT(A2:A,1,0),SORT(A:A,1,0),"D"),"")) it produces a reverse orders list of correct answers that I can't figure out how to flip back.
Seems like I'm missing something obvious?
EDIT: tried to clarify original post
Is there a easy way to displace an entire column?
Alternative Solution?
The formula roughly works but is not aligned to the correct row:
C D E
1 2 3
1 2 3
1 2 3
1 2
1
I just need it to display
C D E
1
1 2
1 2 3
1 2 3
1 2 3
To get things aligned, I can put in cell on row2 of Column F:
=array_constrain(ARRAYFORMULA(D:D),COUNT(A:A)-2,1)
Or cell in row3 of Column G:
=array_constrain(ARRAYFORMULA(E:E),COUNT(A:A)-3,1)
But if I try trigger teh formula from row1 via:
=arrayformula(if(row(A:A)>=2,array_constrain(D:D,COUNT(A:A)-2,1)))
It label everythign >=2 row false and still render D:D without displacing the cells the proper number of rows:
C D
1 false
1 2
1 2
1 2
1
EDIT: I'm closing the request, ended up just using vlookup(B:B-X) which provided an approximate enough result to work for my needs.
Short answer
Add the following formula to D1
=ArrayFormula({"N/A";ARRAY_CONSTRAIN(DATEDIF(A:A,A2:A,"D"),COUNT(A:A)-1,1)})
And the following formula to E1
=ArrayFormula({"N/A";"N/A";ARRAY_CONSTRAIN(DATEDIF(A:A,A3:A,"D"),COUNT(A:A)-2,1)})
Explanation
The solution use ARRAY_CONSTRAIN to return just the required result values and use a the array notation to add the required N/A values for the rows that as it don't have a pair to calculate the date difference.
REMARK:
Please note that the DATEDIF functions use the column A for the references as this column is the one that holds the date values.

Resources