Q: Begin a row from the first non-null value in excel - excel

Problem:
I have a data table with the price of different references each year. Some references are bought since 2004, others since 2002...
However, I want something like this where all the prices begin at the same point called in my example year 1 that is the first year where the company bought a reference (so this is the first non-null value in the row), year 2 is the second year....
The main difficulty is that I can't use VBA (even if I could, I don't see how) and I have to do this for more than thousands rows.
How can I do to automatize this?
I tried to get the first value of each rows with this:
=INDEX(A2:V2;MATCH(TRUE;A2:V2<>"";0))
But I don't know how to get the values after.

I got something for you:
Sub Delete_blanks()
Selection.SpecialCells(xlCellTypeBlanks).Select
Selection.Delete Shift:=xlToLeft
End Sub
Select the data and run the macro it should work for you.
Any questions or problems let me know.

With your requirements, I think it would be easier (in terms of finding the "next" column, to find the first filled column number, instead of the first value. Then you can increment that.
=IF(INDEX(2:2,AGGREGATE(15,6,1/ISNUMBER(2:2)*COLUMN(2:2),1)-1+COLUMNS($A:A))="","",INDEX(2:2,AGGREGATE(15,6,1/ISNUMBER(2:2)*COLUMN(2:2),1)-1+COLUMNS($A:A)))
To find the first column number that contains a number (I assume the first column is text, if not, some changes may be needed):
1/ISNUMBER(2:2)*COLUMN(2:2),1)-1+COLUMNS($A:A)
will return an array of {DIV/0!, n, …} where n is a column number where the cell contains a number
The AGGREGATE function will return the smallest number in this array, ignoring errors, and that is the first filled column.
We then increment this as we fill right with the COLUMNS($A:A).
And we need the IF statement because when INDEX finds an empty cell, it will return a zero instead of a blank.

Related

calc/excel: retreive last value of a row after finding value in column

I have a calc spreedsheet, which is mostly similar to excel regarding formulas. In one sheet I have the details of some boughts I've done (comprasDet) and in another the items I can buy (productos).
The first entry in comprasDet looks like this:
I need to retreive from productos the last registered cost from "Shampo y Mascarilla Chocolate....", put it in ctUnit (that stands for unitary cost) and then calculate the entry cost in ctTot.
The last registered costs in the productos sheet are:
In this case, I need the value in the F12 cell - $6.00. Now, I know that with:
MATCH(D2,$productos.D:D,0) (written in comprasDet)
I can get the row number of "Shampo y Mascarilla Chocolate..." in productos (12), and with (formula taken from this this post):
LOOKUP(2,1/($productos.12:12<>""),$productos.12:12)
I get actually the last value in the 12th row ($6.00) - the thing is, I need to be able to look for the last price for any given value in comprasDet, for example, if instead of "Shampo y Mascarilla Chocolate..." I have "Keratina Brasileña (1 lt)" I'll would like to get the F4 value in productos ($7.00).
The sheet productos will be getting more columns as the prices vary, for example, in the previos print, there were only two price columns (ct20210918 and ct20211001), here is an example with another price modification:
And, in this scenario, the price I need to get from "Shampo y Mascarilla Chocolate..." with a formula inside the comprasDet sheet will be the value in productos in cell G12, $8.00.
Note: I know that in excel the notation to reference cell/s in another sheet is Sheet_name!First_cell:Last_cell, in libreoffice calc changes a little, Sheet_name.First_cell:Last_cell, but the formulas are pretty much the same.
Thanks in advance.
Edit: With the new requirements, you need to use COLUMN and MAX to find the last used column. Because you are looking for the last used column of a particular row, you need to use INDIRECT to reference only that row. Here is how you do it in excel. You will have to modify it for your program
=INDEX(productos!A:AD,MATCH(D2,productos!D:D,0),MAX(COLUMN(INDIRECT("R"&MATCH(D2,productos!D:D,0)&"C1:"&"R"&MATCH(D2,productos!D:D,0)&"C30"))))
Explanation:
MATCH(D2,productos!D:D,0) gets the correct row in the productos table. I'll call it MyRow.
INDIRECT("R"&MyRow&"C1:"&"R"&MyRow&"C30") creates a reference to the first 30 columns of MyRow. I'll call it MyRange. Note, "30" is an arbitrary number. Make it large enough that you always get the data.
COLUMN(MyRange) creates an array of column indexes for all filled cells in MyRange.
MAX(COLUMN(MyRange)) gets the largest column index, which is the last filled cell. I'll call this MyCol.
`=INDEX(productos!A:AD,MyRow,MyCol)' returns the last used column of the row you are interested in.
Original Answer:
If this was excel you would use XLOOKUP.
=XLOOKUP(D2,'productos'!D:D,'productos'!F:F,"-",0,-1)
If your program has access to the index function, this should work:
=INDEX($productos.F:F, MATCH(D2,$productos.D:D,0))

IF + AND + Date range formula

I have a problem to create a formula so I want to ask you for your help.
Excel sheet has 150 000 rows and with this formula I want to safe a time.
I have a Date, Name and Status and I need to see in other cell which Name was 4x or more time in consecutive GOOD or OK
Example of input:
https://imgur.com/aRALd9S
I think IF + AND + DATE Range it’s enough, but I don’t know how to put it together.
Thanks a lot for your suggestions !
Here is what i have so far: https://imgur.com/Y5WAov5
=COUNTIFS($D$2:$D$15;D2;$C$2:$C$15;"OK";$D$2:$D$15;D2;$E$2:$E$15;">="&E2;$E$2:$E$15;"<="&E2+7)+(COUNTIFS($D$2:$D$15;D2;$C$2:$C$15;"GOOD"))
With this i'm able to count how many times i have a Name, which is OK or GOOD and is in one week range, but i still don't know, what i have to change, that i will stop to count when false is there
Well here's something you could try. You could do it in one formula with an array formula, but with 150K rows it seems much better to try and avoid array formulas and use helper columns where necessary.
The first helper column just contains the person's ID if the row contains FALSE:
=IF(H2=FALSE,I2,"")
The second helper column contains the offset from the current row to the next FALSE for the same person:
=IFERROR(MATCH(I2,K2:K$15,0)-1,16-ROW())
So now you can use basically your same COUNTIFS formula but replacing each range with an INDEX which specifies how many rows you should count:
=IF(H2=FALSE,0,COUNTIFS(I2:INDEX(I2:I$15,L2),I2,H2:INDEX(H2:H$15,L2),"GOOD",J2:INDEX(J2:J$15,L2),">="&J2,J2:INDEX(J2:J$15,L2),"<="&J2+7))+
IF(H2=FALSE,0,COUNTIFS(I2:INDEX(I2:I$15,L2),I2,H2:INDEX(H2:H$15,L2),"OK",J2:INDEX(J2:J$15,L2),">="&J2,J2:INDEX(J2:J$15,L2),"<="&J2+7))
Note 1
The 16 in the second equation is to allow for the case where there are no more rows labelled FALSE after the current row, so the MATCH fails. This makes the Countifs count everything from the current rows to the end of the data.
Note 2 - expanding to a larger range of data
You should be able replace the figure 16 with countA(I:I), the size of the data plus headers.
There shouldn't be a problem with using a larger range for the Index e.g.
=IF(H2=FALSE,0,COUNTIFS(I2:INDEX(I2:I$150000,L2),I2,H2:INDEX(H2:H$150000,L2),"GOOD",J2:INDEX(J2:J$150000,L2),">="&J2,J2:INDEX(J2:J$150000,L2),"<="&J2+7))+
IF(H2=FALSE,0,COUNTIFS(I2:INDEX(I2:I$150000,L2),I2,H2:INDEX(H2:H$150000,L2),"OK",J2:INDEX(J2:J$150000,L2),">="&J2,J2:INDEX(J2:J$150000,L2),"<="&J2+7))
but increasing the search range in the MATCH to 150K rows in the second formula does seriously affect performance when repeated 150K times. The only solution I can think of at the moment is to see if a maximum can be placed on the distance from any occurrence of a name to the next occurrence of the name with FALSE next to it.

Google Spreadsheet, SUM or MINUS of above cell in the column using ARRAYFORMULA in the first row

I'm learning to use array formulas and have been successful doing simple things like adding 2 columns together in a third column. For example, I can put =arrayformula(B:B+C:C) in D1 and it adds B and C for each row.
But now I have a situation where I want to subtract two numbers in the same column. I want to take the value of that column in the current row and subtract the previous row's value from it. Without array formulas this is simple: in O7 I put =N7-N6 and cop that down so O8 gets =N8-N7, etc. But that requires copying down every time - can I do the same thing with an array formula?
Basically, can I do something like =arrayformula(B:B+(B-1):(B-1)) ?
Context: column N is a monthly account balance. I would like to calculate how much that balanced changed each month. So for row 7, =N7-N6 gives me that difference. But I'm changing the entire spreadsheet to array formulas so I can stop pasting all of the formulas and I'm stuck on this one since it's comparing the same column.
I'm trying to get everything into Row 1 so my values and calculations can start in Row 2. For example, here's one of my formulas in Row 1:
arrayformula(if(row(A:A)=1,"Total gross income",if(LEN(B:B),B:B+C:C,"")))
Unfortunately, in Column O (the one I asked about originally) if I do this:
=arrayformula(if(row(A:A)=1,"Amount saved this month",if(row(A:A)>1,if(LEN(N:N),N2:N-N:N,""))))
Or this:
=arrayformula(if(row(A:A)=1,"Amount saved this month",if(row(A:A)>1,if(LEN(N:N),offset(N:N,1,0)-N:N,""))))
Every row is off by 1 - the result that should go in Row 3 goes in Row 2, etc. And if I do this:
=arrayformula(if(row(A:A)=1,"Amount saved this month",if(row(A:A)>1,if(LEN(N:N),N:N-offset(N:N,-1,0),""))))
Then it gives me an error because the offset function is trying to evaluate something out of range (possibly it starts with N1 and tries to grab a value 1 row above N1?)
Any advice on how to handle that out-of-range error?
I think the error is because of offset range N:N which starts from N1 and you are trying to shift it -1 or one cell up, which brings the formula out of sheet.
Try this formula instead:
=arrayformula(
{"Amount saved this month";
if(LEN(N2:N),N2:N-offset(N2:N,-1,0),"")})
It uses {} to make an array. See more info:
https://support.google.com/docs/answer/6208276?hl=en
Bonus. There is no reason to check row number now.

Excel formula to lookup the last value in a column and return the value of the adjacent cell

I have the following formula to return the value of the last value in a column:
=LOOKUP(2,1/(D:D<>""),D:D)
What I need now is to return the value of the cell adjacent to it as well. (It will not necessarily be the last value in that column and the info in Column D could have duplicates.
If your data looks like this:
A 1
A 2
A 3
B 4
B 5
B 6
C 7
To get last value this will do the trick:
=INDIRECT("B"&COUNTA(A:A))
And to get last where value is A:
=INDIRECT("B"&MATCH("A",A1:A7,0)+COUNTIF(A1:A7,"A")-1)
Just use next column:
=LOOKUP(2,1/(D:D<>""),E:E)
Ok, So I have found an answer by playing around with array formulas.
The problem was that this is a stock control sheet where there are changes made at multiple times, each recorded in the next available row. There is always a date (Column E) but not necessarily a Supplier, as it might be stock moving out. When a Supplier delivers, the Supplier name is recorded in Column D. In D1 the last supplier is then shown with the following formula.
=LOOKUP(2,1/(D:D<>""),D:D)
I want to then see what date it was last received. The formula I found that works is as follows (Array Formula):
=INDEX(E:E,MAX(IF(D:D=D1,ROW(D:D)-ROW(INDEX(D:D,1,1))+1)))
This is generally how I do it:
=XMATCH(FALSE,ISBLANK(A:A),0,-1)
This is what each part does:
Parameter
Explanation
FALSE
Instructs Excel to find the first instance of FALSE that it finds
ISBLANK(A:A)
Takes in the column A:A and notionally assigns a value to every item in the column
0
Means we want an exact match. Probably not necessary to put in, but I think it's good practice anyway
-1
Instructs Excel to start the search at the bottom/right of the range and work up/left. If you change this to 1 (the default), Excel will begin the search at the top/left and work down/right
So, taken together, this will search from the bottom of the column A:A, until Excel finds the first cell that is not blank, and return that cell.
Also, yes, this equation can be changed to a row format (e.g. 1:1), and can take a smaller range (e.g. A1:A20), but it cannot take a 2-dimensional range (e.g. A1:B20).
As a practical matter, this approach is much faster than other approaches (and much faster than you'd think, given it's evaluating against every row/column in the range), and won't get fooled by columns that have empty spaces in them (like with a COUNTA style approach).

Random excel function with no duplicates and unfixed data

I need to translate an old program working on AS/400 that was picking random students to work for my city. I can use any program, as long as it works. To make it simple and fast, i chose excel.
However, i come over a small problem. I need to have no duplicates, because the same student can't do 2 jobs over one summer. Also, i need this to be flexible, since every year, new students will be added and some will be deleted.
This function works almost as much as i would want it:
=INDEX($A:$A,RANDBETWEEN(1,COUNTA($A:$A)),1)
The index $A:$A gets all the lines in the column A. So even if i add 20 names, it will take them into consideration. Then it choose randomly a value (the name) between the line 1 and the number of total lines (COUNTA) in the column $A. The problem with this method is that it allows duplicates.
Another function i found was to create a colum full of =ALEA() and then rank these by the numbers. This is not very pretty, but at least, there is no duplicates. The problem comes from my formula, that is static, and that i can't make flexible:
=INDEX($A$2:$A$74,RANK(B2,$B$2:$B$74))
My names are in the colum $A and my random values in colum $B. What i say is, rank the value in B2 (then B3, then B4, etc.) that is found in the column B.
What i would like is to integrate the COUNTA into the second function and (IF POSSIBLE) take the RANDBETWEEN instead of the rank function so that i don't have ugly numbers.
I am opened to use the first function with some kind of duplicate check. As long as the secretary doesn't have to do a lot of manipulation, it should be fine.
Thanks a lot for your help xox
I've created something in VBA that does what I think you want. Now keep in mind I'm very new with VBA, so it probably isn't the prettiest thing. To be clear, I had 10 names in column A from rows 1 to 10 and then simply ran this subroutine and it generated a list of unique names in column F. Here's my code:
Sub getRandom()
Do While Application.WorksheetFunction.CountA(Range("A:A")) > 0
Dim count As Integer
count = Application.WorksheetFunction.CountA(Range("A:A"))
Dim name As String
name = Application.WorksheetFunction.Index(Range("A:A"), Application.WorksheetFunction.RandBetween(1, count))
Dim row As Integer
row = Application.WorksheetFunction.Match(name, Range("A:A"), 0)
Range("F11").Select
Selection = name
Rows(row).EntireRow.Delete
Loop
End Sub
If you wanted to get the names one at a time, just remove the loop. How it works is it does exactly what you did with the INDEX and RANDBETWEEN functions, grabs the name in column A with that generated number, then deletes that row entirely, and thus no unique name is generated.
I chose column F arbitrarily and cell F11 specifically since that cell will not be affected when the rows are deleted.
I hope this helps, and if it's not what you were looking for I'll see if I can enhance it a bit.
There are many examples available for doing this in VBA (google Excel Random Unique) One of the best is from CPearson.Com which includes (among others):
Random Elements From A Range Of Worksheet Cells
You can also create a function that returns a number of elements in random order and without duplicates from a range of worksheet cells. The function below does just this. You can call as an array formula with a formula like
=RandsFromRange(A1:A10,5)
where A1:A10 is the list of elements from which to pull the values and 5 is the number of values to return. Enter the formula in a range with as many cells as you specify to return, and press CTRL SHIFT ENTER rather than just ENTER.
If you don't want to use VBA then then it can be done with a few simple steps:
Insert a column next to your student names, with the formula =RAND()
Sort the list of names and random numbers on the number
Pick as many students as you want fromm the top of the list
Each time you sort you get a new random ordering

Resources