Excel Array formula #N/A error - vba

I am using an excel array function to select a range and apply formula based on reference cells. But, everytime i add or remove a values, i am able to copy paste the references using macro but the array formula is not updating the range to reselect the new range.
Here is my main table on top and bottom table what i want sing an array formula which i already achieved but when updating using vba its not taking new entries added/deleted by updating the range in the formula.
No. Name V1 V3 V3 V4
1 Wood 10 10 10 10
2 wood 28 28 28 28
3 tree 30 45 60 68
4 plastic 50 50 50 50
5 tree 50 50 50 50
6 iron 64 75 75 80
No. Name V1 V3 V3 V4
1 Wood - A 25 25 25 25
2 Wood - A 50 50 50 50
3 tree - A 50 50 75 75
4 plastic - A 75 75 75 75
5 tree - A 75 75 75 75
6 iron - A 75 100 100 100
First formula: Name column
=concatenate(A1:A6," - A")
Ctrl+shift+enter - is giving me what i need in right table names column.
Second formula: values change
=value(if(C1:F6<25,"25",if(C1:F6<50,"50",if(C1:F6<75,"75","100"))))
This formula i used to assign actual values and the values in left table is forecast values. I can achieve this even using array "ctrl+shift+enter".
Problem:
But the problem, is everytime i update a sheet by adding new entries like A7,A8,A9 while applying the formula using vba it's not taking the new range as A1:A9(A1:A6) for first formula and C1:F9(C1:C6) for second formula but taking the old ranges in brackets. Because, of which i am getting errors like #N/A as its not taking new range, so formula couldn't understand what is in the remaining cells.

Start with:
Put this in the worksheet's code sheet.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A:F")) Is Nothing Then
On Error GoTo meh
Application.EnableEvents = False
Dim t As Range, tr As Long, v As Long
For Each t In Intersect(Target, Range("A:F"))
tr = t.Row
If Cells(tr, "B").Value2 <> vbNullString And _
Application.Count(Range(Cells(tr, "A"), Cells(tr, "F"))) = 5 Then
Cells(tr, "A").Offset(0, 7) = Cells(tr, "A").Value
Cells(tr, "B").Offset(0, 7) = Cells(tr, "B").Value & " - A"
For v = 3 To 6
Select Case Cells(tr, v).Value
Case Is < 25
Cells(tr, v).Offset(0, 7) = 25
Case Is < 50
Cells(tr, v).Offset(0, 7) = 50
Case Is < 75
Cells(tr, v).Offset(0, 7) = 75
Case Else
Cells(tr, v).Offset(0, 7) = 100
End Select
Next v
End If
Next t
End If
meh:
Application.EnableEvents = True
End Sub
Result after adding two rows.

Related

Looping through 2 columns to find start and end point to insert formula in another column

I'm relatively new to VBA and was hoping to get some advice on my problem.
I have three columns.
Column A is my start signal, Column B is my end signal, the signals are marked by the value "1".
Column C is where I want a formula to be inserted.
I'm looking for some code that loops through column A and B to find the start and end points of inserting a formula in column C, and to repeat this process until the end of column A.
Something like below.
Would really appreciate any help on this!
Thank you xx
A B C D
1
2 1 01/02/2018 01/02/2018
3 02/02/2018 01/02/2018
4 03/02/2018 01/02/2018
5 04/02/2018 01/02/2018
6 05/02/2018 01/02/2018
7 1 06/02/2018 01/02/2018
8 07/02/2018
9 08/02/2018
10 09/02/2018
11 1 10/02/2018 10/02/2018
12 11/02/2018 10/02/2018
13 12/02/2018 10/02/2018
14 13/02/2018 10/02/2018
15 1 14/02/2018 10/02/2018
16 15/02/2018
17 16/02/2018
18 17/02/2018
19 1 18/02/2018 18/02/2018
20 1 19/02/2018 18/02/2018
21 1 20/02/2018 18/02/2018
I deleted your picture and added a block with data. People here prefer not to have to click on links to understand your question.
EDIT
Normally people here will object, if you change your question. It means that previously given answers no longer match the question, and that is unfair to people who have answered in good faith. The accepted practice is to accept an answer to the first question, and ask a second one. It is OK to refer to a previous question. But since I was the only one who answered, and you are a newbie, I am being nice and altering my answer:
The following should do what you want:
Sub Button1_Click()
Dim endRow As Integer
Dim doFormula As Boolean
Dim i As Integer
Dim formulaText As String
doFormula = False
formulaText = ""
endRow = Cells(Rows.Count, "A").End(xlUp).row
For i = 1 To endRow
If Cells(i, 1) = 1 And formulaText = "" Then
formulaText = "=" + Cells(i, 3).Address(RowAbsolute:=False, ColumnAbsolute:=False)
End If
If Cells(i, 1) = 1 Or doFormula Then
Cells(i, 4).Formula = formulaText
doFormula = True
End If
If Cells(i, 1) <> 1 And Cells(i, 2) = 1 Then
doFormula = False
formulaText = ""
End If
Next
End Sub
By way of explanation, Cells(Rows.Count, "A").End(xlUp).Row gives you the last used row in column A. You might need to amend this later to use column B instead. It then loops through the rows until the last one to see if the formula needs adding. I set a Boolean value as false to begin with, so that any initial empty rows are ignored, and then the flag gets unset when it hits something in B. Cells(x, y).Address gives the A1 notation of the cells address; you can get relative or absolute addresses according to the parameters. The requirement that formulaText should be empty when resetting it, means that the 18th February value is repeated, even though the row has 1 in the a column.
Hope this helps

Delete rows based on value of 3 cells

I am fairly new to VBA so what I am trying to do feels almost impossible.
I have an excel sheet with three columns showing the percentage of toner left.
Black Cyan Magenta Yellow
6 45 67 100
93 93 5 19
20 40 65 57
I want to know if it's possible to create a macro to look at all three columns per row and if all the values are higher than 10, to delete the row and if any of the 3 values is below 10 to keep the row and move on to the next one.
In this case, row 2 and 3 would be kept because there's values below 10 but row 4 would be deleted.
For i = Sheet.Cells(Rows.Count, "A").End(xlUp).Row to 2 Step -1
If Cells(i, Column1).Value2 > 10 And Cells(i, Column2).Value2 > 10 And Cells(i, Column3).Value2 > 10 Then
Rows(i).Delete
End If
Next i

Grouping Value using VBA

I got the following data on a table, I want to make a macro that will group all the values with #N/A right at the bottom. Please assist.
A B C D E
1 Line Item Quantity Part Number Description
2 Line 1 Fan Motor 0 0 0
3 Line 2 Fan #N/A #N/A #N/A
4 Line 3 Fan guard 0 0 0
5 Line 4 Pump 0 0 0
6 Line 5 Access door 0 0 0
7 Line 6 Nozzle grommet 0 0 0
8 Line 7 Nozzle 0 0 0
9 Line 20 SST tube 0 0 0
10 Line 21 Flanges 2 205024M2P HDGFLG
11 Line 22 Part 11 #N/A #N/A #N/A
12 Line 23 Part 12 #N/A #N/A #N/A
16 Line 29 Terminal box 1 31123800P TERMINAL BOX
14 Line 25 Check Valve #N/A #N/A #N/A
15 Line 26 Buttefly Valve #N/A #N/A #N/A
17 Line 30 Solenoid valve 1 31601700P SOLENOID VALVE 1 PER8
18 Line 31 Coil 1 31602000P COIL FOR SOL. VALVE
I used the code below initially to delete rows with value #N/A
Sub GroupRows()
Dim rownum As Long
For rownum = 1 To 1000
If Cells(rownum, 3).Text = "#N/A" Then
Rows(rownum).Delete
Next rownum
Cells(rownum, 3).Activate
End Sub
but a
"Compile error: Next without For"
error message pops up, thing is I am still new to VBA and there's some things I do not quite understand as of yet, so I do not know whether I am coding right or not.
This is how your code would look like if you use the last used row instead of a fixed max row number.
Sub GroupRows()
Dim iRow As Long, lastRow As Long
Dim ws As Worksheet
Set ws = Worksheets("MySheetName") 'qualify your sheet
lastRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row 'find last used row
For iRow = lastRow To 1 Step -1 'run from last used row backwards to row 1
If ws.Cells(iRow, 3).Text = "#N/A" Or _
ws.Cells(iRow, 4).Text = "#N/A" Then
ws.Rows(iRow).Delete
End If
Next iRow
ws.Cells(iRow, 3).Activate
End Sub
Your code was missing a End if and you should always qualify a Cells or Range with a worksheet like ws.Cells or ws.Range.

VBA macro to find if a value in a cell is within the range of the values of two cells

I have 2 sheets. Sheet1 has 3 columns A, B & C with values of range_start_number (A), range_end_number (B) and range_name (C).
Sheet2 has 1 column A with the list of numbers.
How to find which numbers in sheet2 belong to which ranges in sheet1? In addition, also, the number's position within a corrosponding range. The output should be:
'range name'-'position in range'
see below for example data inputs (sheet1 & sheet2) and expected output (in sheet2).
Sheet1 (input)
A B C
10 17 rangeA
17 15 rangeB
30 12 rangeC
8 9 rangeD
11 9 rangeE
36 50 rangeF
40 45 rangeG
31 32 rangeH
5 25 rangeI
25 26 rangeJ
Sheet2 (input)
A
10
11
12
13
14
15
16
17
18
19
22
23
24
25
Sheet2 (output)
A B C D E
10 rangeA-1 rangeI-6 rangeE-2
11 rangeA-2 rangeI-7 rangeE-1
12 rangeA-3 rangeC-19 rangeI-8
13 rangeA-4 rangeC-18 rangeI-9
14 rangeA-5 rangeC-17 rangeI-10
15 rangeA-6 rangeB-3 rangeC-16 rangeI-11
16 rangeA-7 rangeB-2 rangeC-15 rangeI-12
17 rangeA-8 rangeB-1 rangeC-14 rangeI-13
18 rangeC-13 rangeI-14
19 rangeC-12 rangeI-15
22 rangeC-9 rangeI-18
23 rangeC-8 rangeI-19
24 rangeC-7 rangeI-20
25 rangeC-6 rangeI-21 rangeJ-1
The number's position within the range is calculated subtracting the lesser of the ranges' numbers from the input value
Sub RangeSub()
Row = 1
Do While True
Column = 6
If Not IsEmpty(Cells(Row, 5)) Then
rRow = 1
Do While True
If Not IsEmpty(Cells(rRow, 1)) And Not IsEmpty(Cells(rRow, 2)) And Not IsEmpty(Cells(rRow, 3)) Then
If (Cells(rRow, 1).Value <= Cells(Row, 5).Value And Cells(Row, 5).Value <= Cells(rRow, 2).Value) Or (Cells(rRow, 1).Value >= Cells(Row, 5).Value And Cells(Row, 5).Value >= Cells(rRow, 2).Value) Then
Position = Abs(Cells(Row, 5) - Cells(rRow, 1)) + 1
Cells(Row, Column).Value = Cells(rRow, 3).Value + "-" + Str(Position)
Column = Column + 1
End If
Else
Exit Do
End If
rRow = rRow + 1
Loop
Row = Row + 1
Else
Exit Do
End If
Loop
End Sub
The code works with the ranges and input in the same sheet, you could change it to work in different sheets.
A B C D E
10 17 rangeA 10
17 15 rangeB 11
30 12 rangeC 12
8 9 rangeD 13
11 9 rangeE 14
36 50 rangeF 15
40 45 rangeG 16
31 32 rangeH 17
5 25 rangeI 18
25 26 rangeJ 19
22
23
24
25
D is blank.

Sort rows in decreasing order until a blank row then continue to next blank row

I want to sort rows in a decreasing order "until hitting every blank row."
I have the following code:
For j = 1 To 15
For i = 1 To 15
Do Until mySheet.Cells(i, 1).Value = 0
If mySheet.Cells(j, 2).Value > mySheet.Cells(i, 2).Value Then
For c = 2 To 5
temp1 = mySheet.Cells(i, c).Value
mySheet.Cells(i, c).Value = mySheet.Cells(j, c).Value
mySheet.Cells(j, c).Value = temp1
Next c
End If
i = i + 1
Loop
Next i
Next j
The If statement swaps the rows in decreasing order by comparing the 2nd number of the row.
What went wrong is the Do Until Loop. I'd like to keep checking/swapping the rows until it hits a blank row, but continue to check/swapping for the rows after a blank row. Check, swap, stop when hitting a blank row, then check the next rows again, swap again, so on and so forth.
EDIT
Here is a what's what I am trying to do:
BEFORE:
Row B C D E
1 63743 734 1848 246
2 86208 900 900 974
3 --------**Empty Row**----------
4 40934 730 5643 5565
5 97734 454 54656 3345
6 73885 347 3728 9934
7 --------**Empty Row**----------
8 34355 998 3884 3299
9 98438 383 43483 4399
10 19874 454 53439 3499
11 --------**Empty Row**----------
AFTER:
Row B C D E
1 86208 900 900 974
2 63743 734 1848 246
3 --------**Empty Row**----------
4 97734 454 54656 3345
5 73885 347 3728 9934
6 40934 730 5643 5565
7 --------**Empty Row**----------
8 98438 383 43483 4399
9 34355 998 3884 3299
10 19874 454 53439 3499
11 --------**Empty Row**----------
My If compares the values in Column B, and sorts rows in a decreasing order. I could not figure out how to make a while loop so to stop sorting when hitting a blank row, but then continue comparing/sorting for the next couple rows after a blank row. Note I would not know how many rows there are before a blank row.
EDIT 2
BEFORE:
Row A B C D E
1 A 63743 734 1848 246
2 B 86208 900 900 974
3 -------------**Empty Row**----------
4 C 40934 730 5643 5565
5 D 97734 454 54656 3345
6 E 73885 347 3728 9934
7 -------------**Empty Row**----------
8 F 34355 998 3884 3299
9 G 98438 383 43483 4399
10 H 19874 454 53439 3499
11 -------------**Empty Row**----------
AFTER:
Row A B C D E
1 B 86208 900 900 974
2 A 63743 734 1848 246
3 -------------**Empty Row**----------
4 D 97734 454 54656 3345
5 E 73885 347 3728 9934
6 C 40934 730 5643 5565
7 -------------**Empty Row**----------
8 G 98438 383 43483 4399
9 F 34355 998 3884 3299
10 H 19874 454 53439 3499
11 -------------**Empty Row**----------
The code as it stands can never terminate, because the variable you are checking,
Do Until mySheet.Cells(i, 1).Value = 0
if not changed in any of what follows:
If mySheet.Cells(j, 2).Value > mySheet.Cells(i, 2).Value Then
For c = 2 To 5
temp1 = mySheet.Cells(i, c).Value
mySheet.Cells(i, c).Value = mySheet.Cells(j, c).Value
mySheet.Cells(j, c).Value = temp1
Next c
End If
You loop your c from 2 to 5, so Cells(i,1) is never touched.
This is so fundamental that it's a little bit hard to understand what you were really trying to do, but I'll have a shot at it.
It seems that you want each of columns 2 to 5 (maybe 1 to 5) to be sorted with a bubble sort - check two adjacent cells, move the smaller one to the top, keep going to the bottom of the column. You don't state whether each column has the same length, so I am going to assume it doesn't.
We should be able to sort one column at a time as follows (this is not the most efficient algorithm but it's true to your intention, I think):
Sub sortMyColumns()
Dim colNum As Integer
Dim numRows As Integer
Dim i, j As Integer
Dim lastCell As Range
For colNum = 1 To 5
Set lastCell = Cells(1, colNum).End(xlDown)
numRows = lastCell.Row
For i = 2 To numRows
For j = numRows To i Step -1
If Cells(j, colNum) < Cells(j - 1, colNum) Then
temp = Cells(j - 1, colNum).Value
Cells(j - 1, colNum).Value = Cells(j, colNum).Value
Cells(j, colNum).Value = temp
End If
Next j
Next i
Next colNum
End Sub
For each column, this finds the number of rows; it then starts at the bottom, and pushes the smaller number all the way to the top. It returns to the bottom, but this time only pushes up to one from the top. It continues until it gets to the last two cells - everything should now be sorted.
You may need to add some error trapping in case cells do not contain numerical values etc, but in principle this should work.
EDIT
Based on your comment, this was not what you were looking for. I have created a second Sub which sorts columns B through E based just on the value in B - this mirrors your code example a little better, and may be what you had in mind. I am using the length of column B to find out how many rows to sort - I still don't understand clearly what your column A is doing and how testing it helps you.
If this is still not what you want, I suggest you edit your question with a simple example (screen shot) of the type "this is what my sheet starts out with", and "this is what it has to look like". Just four or five lines of the spreadsheet, and columns A through E, should be sufficient.
Sub sortByColumnB()
' sort cells in columns B through E
' based on the value found in B
Dim colNum As Integer
Dim numRows As Integer
Dim i, j As Integer
Dim lastCell As Range
' find the last cell in column B:
Set lastCell = Cells(1, 2).End(xlDown)
numRows = lastCell.Row
For i = 2 To numRows
For j = numRows To i Step -1
If Cells(j, 2) < Cells(j - 1, 2) Then
' swap around each of the cells in this row with the one above
For colNum = 2 To 5
temp = Cells(j - 1, colNum).Value
Cells(j - 1, colNum).Value = Cells(j, colNum).Value
Cells(j, colNum).Value = temp
Next colNum
End If
Next j
Next i
End Sub
I ran this code on the following dummy spreadsheet:
And it resulted in the following output:
As you can see, column A was untouched, and each row in columns B through E is sorted according to the key in column B. You know of course that there is a built in sort function in Excel, but I assume you had reasons for not wanting to use it...
I hope this is what you needed! If it isn't, then please update your question with and example of "I want THIS to turn into THAT".
EDIT 3
Your latest update to the question plus the comment to my solution finally makes it clear what you intend to do. Since we can't really know we have reached the last block until we "fall off the edge", I have modified the code so it has an infinite loop with an error trap (that is generated when you try to go beyond the bottom of the spreadsheet). I tested this with blank row all the way (including blank in column A - note the code no longer uses column A at all):
Sub keepSorting()
Dim colNum As Integer
Dim firstRow, lastRow As Integer
Dim i, j As Integer
' loop around the algorithm we had earlier, but only for the 'non-empty blocks of rows'
firstRow = 1
lastRow = [B1].End(xlDown).Row
On Error GoTo allDone
While True ' break out of the loop when we throw error
' sort from firstRow to lastRow:
For i = firstRow + 1 To lastRow
For j = lastRow To i Step -1
If Cells(j, 2) > Cells(j - 1, 2) Then
' swap around each of the cells in this row with the one above
For colNum = 1 To 5
temp = Cells(j - 1, colNum).Value
Cells(j - 1, colNum).Value = Cells(j, colNum).Value
Cells(j, colNum).Value = temp
Next colNum
End If
Next j
Next i
firstRow = Cells(lastRow + 1, 2).End(xlDown).Row
lastRow = Cells(firstRow, 2).End(xlDown).Row
Wend
allDone:
On Error GoTo 0
End Sub
It turns THIS:
into THIS:
Note - the On Error Resume Next is there because finding the lastRow when firstRow is at the bottom of the sheet generates an error; but since we're done by that time, we just need to exit the while loop...
There are a lot of ways to do this, but if you want to keep your nested For approach the first thing you need to do is find out how many rows there are before you hit a blank.
Dim lngTotalRows As Long
lngTotalRows = 0
While Cells(lngTotalRows + 1, 1) <> ""
lngTotalRows = lngTotalRows + 1
Wend
Now that you have that, you can just basically use your existing code replacing your 15 with lngTotalRows. You do have a couple small problems with your loops though...
Your outer loop should be:
For j = 1 to lngTotalRows - 1
Your inner loop should be:
For i = j + 1 to lngTotalRows
If you look at a couple specific example you will probably see why. You are comparing your outer loop cell to each cell after it, so the first time through the loop our first cell will have j = 1 (so the cell in Row 1) and we are comparing it to every row after that, so we start at j + 1 (which is Row 2), then we look at row 3, row 4 and so on. When the outer loop is looking at Row 10 the inner loop will just look at the values between Row 11 and the end of the sort range.
The outer loop ends at lngTotalRows - 1 because the inner loop will be looking at the cell after that one which will be the last cell in the range.
See if you can implement that in your existing code. If things aren't behaving as you expect use breakpoints and inspect the values while you step through the code. It can be very enlightening. Also you can use Debug.Print statements in your code to output values to the immediate window to help track down problems.