Goal Seek in multiple rows (with empty rows in the middle) - excel

I want to perform a goal seek for several rows (I don't know exactly how many - in the code I have below it stops in row 100). The thing is, for example, if I have 10 rows, row number 5 can be empty. So I want it to skip from row 4 to row 6, then continue, then "jump" another empty row if they exist.
I want to set cell M2 to 0 by changing the value of cell K2. The same for row 3, row 4, etc. and I want it to skip empty rows.
Right now I just have this... a simple case
Sub GSeek()
Dim i As Long
For i = 2 To 100
range("M" & i).GoalSeek Goal:=0, ChangingCell:=range("K" & i)
Next
End Sub

Try this
Dim i As Long
For i = 2 To 100
If range("M" & i).Value <> "" Then
range("M" & i).GoalSeek Goal:=0, ChangingCell:=range("K" & i)
End If
Next i
Each time it just makes sure that the cell isn't empty. If it is, it'll just move to next i

Related

VBA delete cell in for each loop

I'm trying to delete every cell, and a cell to the left of it, if the value of the cell is 0. (Also, to set the interior color to no fill if the value of the cell is greater than 0.)
This is my code so far
For Each cell In Range("I2:I" & LastTransaction)
If cell.Value = 0 Then
Range(cell.Offset(0, -1).Address, cell.Address).Delete Shift:=xlUp
ElseIf cell.Value > 0 Then
cell.Interior.ColorIndex = 0
End If
Next cell
The problem here is that, every time the cells are deleted, AND SHIFTED UPWARDS, the for each loop doesn't take the upward shift into account, and skips the next cell in the range.
As per #JvdV's comment, when deleting in a loop you need to do it back to front (or in this case bottom to top), using Step -1.
In this case your For loop would look something like;
For x = LastTransaction to 2 Step -1
If Range("I" & x).Value = 0 then
Range("H" & x & ":I" & x).Delete Shift:=xlUp
ElseIf Range("I" & x).Value > 0 Then
Range("I" & x).Interior.ColorIndex = 0
End If
Next
The main issue is that, when you loop through the range, each cell refers to a particular cells on the sheet.
So your loop loops through I2, I3, I4, .... If you delete a row, all the other rows are moved up, and what was in cell Ix is now in cell I(x-1), but the loop will be at cell Ix, and so a whole row will have avoided being processed.
One solution is to store all the rows that are to be deleted in a Collection, and then delete the collection afterwards.
'data
Cells.Clear
For i = 1 To 15
Cells(i, 1) = Int(Rnd * 4)
Cells(1, i + 2) = Cells(i, 1)
Next i
'code
Dim tbd As New Collection 'to be deleted collection
For Each c In [a1:a15]
If c = 1 Then tbd.Add c 'adds a row that is to be deleted
Next c
For Each c In tbd
c.Delete 'deletes all rows in tbd
Next c
The first part of the code creates some sample data to process. Then it defines a new Collection, adds the rows to it that are to be deleted (anything with a value 1 is this case), and then deletes them from the collection.

Comparing two data tables on different tabs in Excel using VBA

I am relatively new to Macros and VBA in Excel, so I need some guidance on how to solve my current issue.
The end goal of my project is to have a macro compare two sets of data organized into rows and columns (We'll say table A is the source data, and table B is based off of user input). Each row in table B should correspond to a row in table A, but they could be out of order, or there could be incorrect entries in table B.
My thought is that for the first row in each table, the macro would compare each cell left to right:
If Sheets("sheet1").Cells(2, 1) = Sheets("sheet2").Cells(2, 1) Then
If Sheets("sheet1").Cells(2, 2) = Seets("sheet2").Cells(2, 2)
Ect, ect.
My problem comes in when the Cell in table B does not match Table A.
First, I would want it to check B row 1 against the next row in A, and keep going throughout table A until it finds a "complete match" with all five columns of the row matching.
I've been trying to do this with Else if and For/Next staements
For row= 2 to 10
'if statements go here
Else If Sheets("sheet1").Cells(2, 1) <> Sheets("sheet2").Cells(2, 1)
Next row
I may be completely misunderstanding the syntax here, but I have yet to produce a situation where if the criteria is not met, it goes to the next row.
If no complete match is found, the last cell in table B row 1 that couldn't be matched should be highlighted.
Then regardless of whether a match was found or not, we would move to table B row 2, and start the whole process over.
So, I have the logic worked out (I think), where the comparison ifs would be inside a loop (or something) that would cycle through table A row by row. Then that whole process would be in another loop (or something) that would cycle through Table B.
At the end of the process, there would either be no highlighted cells showing that all entered data is correct, or cells would be highlighted showing data that do no match.
I am fairly certain that the cycling through table B is not the issue. Rather, I'm having difficulty getting the Macro to move to the next table A row if something doesn't match.
Please let me know if I need to elaborate on anything.
Thanks!
You could try:
Option Explicit
Sub test()
Dim Lastrow1 As Long, Lastrow2 As Long, i As Long, j As Long
Dim Str1 As String, Str2 As String
'Find the last row of sheet 1
Lastrow1 = Sheet1.Cells(Sheet1.Rows.Count, "A").End(xlUp).Row
'Find the last row of sheet 2
Lastrow2 = Sheet2.Cells(Sheet2.Rows.Count, "A").End(xlUp).Row
For i = 2 To Lastrow1
'Let us assume that table has 3 columns. Merge 3 columns' values and create a string for each line
Str1 = Sheet1.Cells(i, 1).Value & "_" & Sheet1.Cells(i, 2).Value & "_" & Sheet1.Cells(i, 3).Value
For j = 2 To Lastrow2
'Let us assume that table has 3 columns. Merge 3 columns' values and create a string for each line
Str2 = Sheet2.Cells(j, 1).Value & "_" & Sheet2.Cells(j, 2).Value & "_" & Sheet2.Cells(j, 3).Value
'If both strings match a message box will appear
If Str1 = Str2 Then
MsgBox "Line " & i & " in table A match with line " & j & " in table B!"
Exit For
End If
Next j
Next i
End Sub
Sheet 1 structure:
Sheet 2 structure:

Identify and duplicate unique rows

I have files of data with the following format:
In column A, identifiers occur either doubly (e.g. 302_60) or singularly (e.g.310_58). Additional information is present in column B.
What I want to do is:
tag the rows that have single identifiers in column A with
TRUE/FALSE in Column C
for any TRUE tag, insert a line BELOW
copy into the inserted row the contents of the ENTIRE tagged row (here just columns A,B)
I solved #1 using =COUNTIF(A:A, A1)=1
I then wrote a VBA script to solve #2
Sub ins_below_and_copy()
Dim c As Range
For Each c In Range("C1:C100")
If InStr(1, c, "TRUE", vbTextCompare) > 0 Then
Rows(c.Offset(1, 0).Row & ":" & c.Offset(1, 0).Row).Insert Shift:=xlDown
End If
Next c
End Sub
Achieving the desired end result (#3)
seems simple enough, right? I have been trying .Copy and .Paste commands, but keep getting type-mismatch errors, an error that does not make sense to me (since I am not a competent VBA coder). Any ideas?
You have down all the hard work, filling in the gaps is easy. Select the two columns, HOME > Editing - Find & Select, Go To Special..., Blanks, OK, =, UP and Ctrl+Enter.
You can run this after you have your empty rows created.
Dim sheet As String
Dim lastRow As Long
sheet = "SheetName"
lastRow = Sheets(sheet).Range("A" & Rows.Count).End(xlUp).Row
For r = 2 To lastRow 'Assuming you have a Header Row
If Sheets(sheet).Cells(r, 1) = "" Then
Sheets(sheet).Cells(r - 1, 3) = "FALSE"
Sheets(sheet).Cells(r, 1) = Sheets(sheet).Cells(r - 1, 1)
Sheets(sheet).Cells(r, 2) = Sheets(sheet).Cells(r - 1, 2)
Sheets(sheet).Cells(r, 3) = Sheets(sheet).Cells(r - 1, 3)
End If
Next r

Creating a macro in Excel that compares two columns, answers in third column

I haven't found an appropriate answer for this question and I'm very new to VBA, so I hope someone will help me out.
I'm trying to create a Sub in my macro that does a simple value compare between two columns, row by row. If they are an exact match it will populate a third column with "Yes", otherwise it will say "No"
All columns are within an excel Table and have the same amount of rows, an example of what the result should look like is this (don't have enough rep to post image):
I was thinking something like a For Each statement but I'm not sure how to create it the right way. Thank you ahead of time for your help!
Quick subroutine to loop through rows 1 through 20 and compare results:
for i = 1 to 20
If sheet1.range("A" & i).value = sheet1.Range("B" & i).value Then
sheet1.Range("C" & i).value = "No"
Else
sheet1.Range("C" & i).value = "Yes"
End if
Next i
Because this seems like more of a learning experiment, you can also reference cells by:
for i = 1 to 20
If sheet1.cells(i,1).value = sheet1.cells(i,2).value Then
sheet1.cells(i,3).value = "No"
Else
sheet1.cells(i,3).value = "Yes"
End if
Next i
You mention the range will vary in size. You can get the last row that is populated and then loop from 1 to that with:
Dim endRow as long
endRow = Sheet1.Range("A999999").End(xlUp).Row
for i = 1 to endRow
If sheet1.range("A" & i).value = sheet1.Range("B" & i).value Then
sheet1.Range("C" & i).value = "No"
Else
sheet1.Range("C" & i).value = "Yes"
End if
Next i
A table will automatically bring formulas to a new row when a new row is inserted. For instance, say you have the following table where the Same? column contains the formula =IF(C3=D3, "Yes", "No")
As you enter a new row in the table, the formula in the Same? column will be automatically brought to the new row. For example, this is what that cell will look like once I hit Tab to create a new row:
Now say you want to completely repopulate the table with a new set of data. That's no problem, simply copy the new data and paste it in the table like so:
Copy
Paste into first cell
The table takes care of the formulas for you, effectively making a macro unnecessary.
Thank you all for your input!
I took elements from your answers and managed to come up with a code that solves my problem. Let me know if you have any questions
Sub Compare
On Error Resume Next
Dim Sm_Row As Long
Sm_Row = Range("Table1[Same?]").Row
For Each cl In Range("Table1[Same?]")
If Range("Table1[Col1]").Cells(Sm_Row, 1).Value = Range("Table1[Col2]").Cells(Sm_Row, 1).Value Then
cl.Value = "Yes"
Else
cl.Value = "No"
End If
Sm_Row = Sm_Row + 1
Next cl
End Sub

Delete Excel record when value ends with plus sign ('+')

We have a blank workbook which I would like the user to be able to paste a list of reference numbers into column A. Some of these reference numbers will have a "+" at the end.
Sub texter1()
With Sheets("texter")
ll = .UsedRange.SpecialCells(xlCellTypeLastCell).Row
For i = 1 To ll
If InStr(1, .Range("a" & i).Value, "+", 1) Then
.Range("b" & i).Formula = "=LEFT(A" & i & ", LEN(A" & i & ")-1)"
.Range("c" & i).Value = Sheets("texter").Range("b" & i).Value
.Range("d" & i).Formula = "=VLOOKUP($c" & i & ", _
[Current_Master.xlsm]Master!$A$3:$BB$20000,14,FALSE)"
.Range("e" & i).Formula = "=VLOOKUP($c" & i & ", _
[Current_Master.xlsm]Master!$A$3:$BB$20000,15,FALSE)"
Else
Cells(i, "a").EntireRow.Delete
End If
Next i
End With
End Sub
I would like reference numbers without the "+" to have the whole row deleted. Reference numbers with a "+" work fine.
this seems to work but has to be run multiple times for it to delete all the rows without a "+" and I cannot figure out why. Please help
Thank you
You cannot delete a row inside a loop as far as affects the iterations. Imagine this: you have 4 rows; the second row meets the conditions and is deleted; in the next iteration the counter is 3 but the row number 3 is now the fourth row (when you delete a row, all the ones below go up one position); consequently, row number 3 wouldn't be analysed. Thus the solution is simple:
Cells(i, "a").EntireRow.Clear()
If you want to actually delete the whole row, you would have to do it outside the main loop. For example: store all the rows to be deleted in an array and iterate through this array right after completing the main loop.
Another alternative would be performing the iterations in the main loop in inverse order (from maximum row to minimum one), although this option is not always applicable (not sure if in your case) and might provoke further problems. The two options above are good enough, I have mentioned this last alternative just as something worthy to be known.
--- UPDATE
To delete the rows after the main loop you can use something on these lines:
'Declaration of variables
ReDim allRows(ll + 1) As Long
Dim allRowsCount As Long: allRowsCount = 0
In your main loop you store the given rows (where you have now Cells(i, "a").EntireRow.Delete):
For i = 1 To ll
'etc.
else
allRowsCount = allRowsCount + 1
allRows(allRowsCount) = i
After the loop is completed, you go through all the stored rows (in inverse order) and delete them:
If (allRowsCount > 0) Then
Dim curRow As Long: curRow = allRowsCount + 1
Do
curRow = curRow - 1
.Rows(allRows(curRow)).Delete
Loop While (curRow > 1)
End If
End With
End Sub

Resources