VBA Do Until string found, then exit step - vba

I have a file that has date values as column headers, and the last column will always be labeled "Grand Total". I want a way to look through the column headers, (which are in cells D4:I4) and input formulas to be filled down later.
For example, if we check D4, and it does not contain "Grand Totals", then I need the below formulas input in Cells L4 and L5:
Range("L4").Select
Application.CutCopyMode = False
ActiveCell.FormulaR1C1 = _
"=""Weekending ""&TEXT(RC[-8],""mm/dd/yyy"")&"" Compliant?"""
Range("L5").Select
ActiveCell.FormulaR1C1 = _
"=IF((IF(RC17=""Duplicate"",SUMIF(C2,RC2,C[-8]),RC[-8]))<27,""Compliant"",
IF((IF(RC17=""Duplicate"",SUMIF(C1,RC1,C[-8]),RC[-8]))<30,""Approaching Limit"",
""Over""))"
Then it would go to cell E4, and if it does not contain "Grand Totals", then the formulas need to be input in cells M4 and M5... Once "Grand Totals" are found [at the top of the loop], I need to exit the loop (but not the sub).
I tried to piece together bits from code I found:
Dim GrTot As String
Dim rng1 As Range
Set rng1 = Range("D4:I4")
GrTot = "Grand Total"
Range("D4").Select
Do While ActiveCell.Value <> GrTot
But I didnt know where to go. Any help is appreciated.
EDIT:
I have since tried another method based on an example found here. This is what I have currently:
Dim x As Integer
Dim y As Integer
With Worksheets("Pivot")
x = 4
Do Until .Cells(4, x).Value = "Grand Total"
For y = 12 To 16
.Cells(4, y).Formula = "=""Weekending ""&TEXT(RC[-8],""mm/dd/yyy"")&""
Compliant?"""
.Cells(5, y).Formula = "=IF((IF(RC17=""Duplicate"",SUMIF(C2,RC2,C[-8]),
RC[-8]))<27,""Compliant"",IF((IF(RC17=""Duplicate"",
SUMIF(C1,RC1,C[-8]),RC[-8]))
<30,""Approaching Limit"",""Over""))"
Next y
x = x + 1
Loop
End With
End Sub
But this one doesn't exit correctly. It will loop through fine, writing the formulas as I requested, but it doesn't exit the loop when it reaches the "Grand Total" column. What am I doing wrong here?

Loop through each cell in the range; if the cell does not have a value you can write whatever to the cell and then something to the offset. As soon as a grand total is found in your range the loop can exit and move to the next range.
For Each item In Range
If item.Value = GrTot Then
Exit For
'Perform action on cell
item.Value = formula
item.offset(0,1).Value = formula
item.offset(0,2).Value = formula
End If

Related

Looping & copying cells

Generally my macro goes through every "O" cell, checks if the row meets given requirements (not mentioned in this part of code) and copies surrounding cells on the side. I have two columns used in this part: "contract no"(M), "date"(O). The problem is that I try to use below method to go up to last the contract number and copy it as well.
I do not get any error but the contract cell value does not paste. Could you tell me what I've done wrong?
If ActiveCell.Offset(0, -2) = "" Then
'Go up find contract number copy
ActiveCell.Offset(0, -2).Select
Do Until ActiveCell.Value <> ""
ActiveCell.Offset(-1, 0).Select
Loop
ActiveSheet.Range("M" & ActiveCell.Row).Copy _
Destination:=ActiveSheet.Range("V" & ActiveCell.Row)
'Go down and return to the last active cell
Do Until ActiveCell.Value <> ""
ActiveCell.Offset(1, 0).Select
Loop
ActiveCell.Offset(0, 2).Select
End If
You didn't select the desired cell
Problem lies in this loop:
'Selecting cell from a column to the left
ActiveCell.Offset(0, -2).Select
'Condition: cell value is not empty string
Do Until ActiveCell.Value <> ""
'Selecting cell from previous row in the same column
ActiveCell.Offset(-1, 0).Select
Loop
You're leaving the loop before you can .Select a cell.
Use this loop instead:
'Selecting cell from a column to the left
ActiveCell.Offset(0, -2).Select
'Condition: cell value is not empty string
Do
'Selecting cell from previous row in the same column
ActiveCell.Offset(-1, 0).Select
Loop Until ActiveCell.Value <> ""
the issue lays in your keeping relying on ActiveCell after
ActiveCell.Offset(-1, 0).Select
statement, that changes it ...
you're actually playing with fire when using ActiveCell together with Select/Selection coding pattern!
since I cannot see what's behind the code you showed, I must keep using ActiveCell reference and amend your code as per comments:
Dim cellToCopy As Range
With ActiveCell 'reference currently "active" cell
If .Offset(0, -2) = "" Then 'if the cell two columns left of referenced (i.e. "active") cell is empty...
Set cellToCopy = .Offset(0, -2).End(xlUp) '... set cell to copy as the first not empty one above the cell two columns left of referenced (i.e. "active") cell
Else '... otherwise
Set cellToCopy = .Offset(0, -2) 'set cell to copy as the one two columns left of referenced (i.e. "active") cell
End If
cellToCopy.Copy Destination:=Range("V" & .Row) 'copy the cell set as the one to be copied and paste it column V cell same row as reference (i.e. "active") cell
End With
Try not to use ActiveCell Your code can do quite unpredictable things to your worksheet if the wrong cell was selected, and so can my "improvement" thereof below.
Sub FindAndCopy()
Dim Ws As Worksheet
Dim R As Long, C As Long
With ActiveCell
Set Ws = .Worksheet
R = .Row
C = .Column
End With
With Ws
If Len(Trim(.Cells(R, C - 2).Value)) = 0 Then
'Go up find contract number copy
Do Until Len(.Cells(R, C - 2).Value)
R = R - 1
Loop
.Cells(R, "M").Copy Destination:=.Cells(ActiveCell.Row, "V")
End If
End With
End Sub
I think the ActiveCell component in this code is still a source of great danger. However, as you see, at least the code doesn't change it which dispenses with the necessity of going back to it in the end.

How to remove a certain value from a table that will vary in size in Excel

I'm new to the community and I apologize if there is a thread elsewhere, but I could not find it!
I'm currently diving into VBA coding for the first time. I have a file that I dump into a worksheet that currently I'm manually organizing and pushing out. When put into the worksheet, it delimits itself across the cells. This dump file will have varying row and column lengths every time I get it in a given day and dump into a work sheet. For example, one day it may be twenty rows and one day it may be thirty.
A certain roadblock in my VBA code creation process has presented itself. I'm trying to create a code that will parse through the worksheet to remove any time a certain value appears (See below image - I'm referring to the (EXT)). After doing so I'm trying to concatenate the cells in the row up until there is a space (which with the rows that have (EXT), there usually isn't a space after until the (EXT) is removed).
The code I made works for now but I recognize it's not very efficient and not reliable if the names extend longer than two cells. I was hoping someone on here could provide me with guidance. So, I'm looking for two things:
For the code to scan the whole active used range of the table and remove (EXT). As it may appear in various columns.
A way to concatenate the cells in every row in the active range from A to the cell before a blank cell
Keep in mind I have no coding background, I'm learning and I'm not familiar with VBA terms and whatnot all that much just yet - so if you could please explain in laymen's terms I'd appreciate it. I hope all of this makes sense... Thanks in advance!
This is just an example of part of what the dump code looks like, so my code probably doesn't match with the example below - I just wanted to provide a visual:
http://i.imgur.com/IwDDoYd.jpg
The code I currently have:
Sub DN_ERROR_ORGANIZER()
' Removes any (EXT) in Column 3 in actual dump data file
For i = 200 To 1 Step -1
If (Cells(i, 3).value = "(EXT)") Then
Cells(i, 3).Delete Shift:=xlToLeft
End If
Next i
' Removes any (EXT) in Column 4 in actual dump data file
For j = 200 To 1 Step -1
If (Cells(j, 4).value = "(EXT)") Then
Cells(j, 4).Delete Shift:=xlToLeft
End If
Next j
' Removes any (EXT) in Column 5 in actual dump data file
For k = 200 To 1 Step -1
If (Cells(k, 5).value = "(EXT)") Then
Cells(k, 5).Delete Shift:=xlToLeft
End If
Next k
' Places a new column before A and performs a concatenate on cells B1 and C1 to
' form a name, then copies all through column A1 to repeat on each row
Columns("A:A").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A1").Select
ActiveCell.FormulaR1C1 = "=PROPER(CONCATENATE(RC[1],"", "", RC[2]))"
Range("A1").Select
Selection.AutoFill Destination:=Range("A1:A51")
Range("A1:A51").Select
End Sub
edited: to keep the comma after the first "name" only
this should do:
Sub main()
Dim cell As Range
With Worksheets("names")
With Intersect(.UsedRange, .Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).EntireRow)
For Each cell In .Rows
cell.Cells(1, 2).Value = Replace(Replace(Replace(Join(Application.Transpose(Application.Transpose(cell.Value)), " "), " ", " "), " (EXT)", ""), " ", ", ", , 1)
Next cell
.Columns(1).FormulaR1C1 = "=PROPER(RC[1])"
.Columns(1).Value = .Columns(1).Value
.Offset(, 1).Resize(, .Columns.Count - 1).ClearContents
End With
End With
End Sub
just remember to change "names" to you actual worksheet name
edited 2:
code for stopping cells to be processed at every line at the last one before the first blank one
Sub main()
Dim cell As Range, dataRng As Range
With Worksheets("names") '<--| change "names" to you actual worksheet name
Set dataRng = Intersect(.UsedRange, .Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).EntireRow)
For Each cell In dataRng.Columns(1).Cells
cell.Offset(, 1).Value = Replace(Replace(Replace(Join(Application.Transpose(Application.Transpose(.Range(cell, cell.End(xlToRight)).Value)), " "), " ", " "), " (EXT)", ""), " ", ", ", , 1)
Next cell
With dataRng
.Columns(1).FormulaR1C1 = "=PROPER(RC[1])"
.Columns(1).Value = .Columns(1).Value
.Offset(, 1).Resize(, .Columns.Count - 1).ClearContents
End With
End With
End Sub
I believe you are quite close to achieve what you are asking for and, based on your request, I will not give you a solution but some guidance to complete it by yourself.
First 3 loops: You could simplify by having a single set of nested loops: An outer loop running from 3 to 5, an inner loop running from 200 to 1; the outer loop will run over index, say "p", the inner over index, say "q", and your reference to cells would become Cells(q,p). If you need to run this over more than 3 rows, just start the outer loop from, say, 3 and till, say 10000 (being 10000 the maximal number of rows your data may display) and add a condition that if the first cell of the row is empty, you exit the outer loop.
The second part (this is what I understood) is to take the 2-3 first cells and concatenate them into a new cell (i.e. the column you add at the left). Once again, you can just loop over all your rows (much the same as in the outer loop mentioned above), except that now you will be looking at the cells in columns 2-4 (because you added a column at the left). The same exit condition as above can be used.
I'm not sure if this is what you were looking for, but this is what I understood you were looking for.
After reading user3598756's answer, I realized that I missed the boat with my original answer.
Sub DN_ERROR_ORGANIZER()
Dim Target As Range
Set Target = Worksheets("Sheet1").UsedRange
Target.Replace "(EXT)", ""
With Target.Offset(0, Target.Columns.Count).Resize(, 1)
.FormulaR1C1 = "=PROPER(C1&"", ""&TEXTJOIN("" "",TRUE,RC[-" & (Target.Columns.Count - 1) & "]:RC[-1]))"
.Value = .Value
End With
Target.Delete
End Sub
UPDATE
If you are running an older version of Excel that doesn't support TEXTJOIN then use this:
Sub DN_ERROR_ORGANIZER()
Dim Data
Dim x As Long, y As Long
Dim Target As Range
Dim Text As String
Set Target = Worksheets("Sheet1").UsedRange
Target.Replace "(EXT)", ""
Data = Target.Value
For x = 1 To Target.Rows.Count
Data(x, 1) = Data(x, 1)
For y = 2 To Target.Columns.Count
If Data(x, y) <> vbNullString Then Text = Text & " " & Data(x, y)
Next
If Len(Text) Then Data(x, 1) = Data(x, 1) & "," & Text
Text = vbNullString
Next
Target.ClearContents
Target.Columns(1).Value = Data
End Sub

Get the lowest values greater than zero

I have the above code, that finds the five max values from a column.
I need to do the same but for the minimum values higher then zero.
I need this to be in VBA because the user can change the values after in the worksheet.
I had already changed max for min, but get errors.
Sub best()
Dim maxvalue As Long
Dim copyrow As Long
copyrow = 30
Dim prevval As Long
Dim prevrow As Long
Dim i As Long
Dim fndrow As Long
prevval = 0
prevrow = 0
For i = 1 To 5
maxvalue = WorksheetFunction.Large(Sheets("Resumo").Range("J11:J47"), i)
If maxvalue <> prevval Then
fndrow = Sheets("Resumo").Range("J11:J47").Find(What:=maxvalue, LookIn:=xlValues, lookat:=xlWhole).Row
Else
fndrow = Sheets("Resumo").Range("J" & prevrow & ":J47").Find(What:=maxvalue, LookIn:=xlValues, lookat:=xlWhole).Row
End If
Dim vendor As String
vendor = Sheets("Resumo").Range("G" & CStr(fndrow))
Sheets("os melhores").Range("F" & CStr(copyrow)) = maxvalue
If InStr(vendor, " ") <> 0 Then
Sheets("os melhores").Range("G" & CStr(copyrow)) = Left(vendor, InStr(vendor, " "))
Sheets("os melhores").Range("H" & CStr(copyrow)) = Right(vendor, InStr(vendor, " "))
Else
Sheets("os melhores").Range("G" & CStr(copyrow)) = Sheets("Resumo").Range("G" & CStr(fndrow))
End If
prevval = maxvalue
prevrow = fndrow
copyrow = copyrow + 1
Next i
End Sub
File
Thanks in advance
You don't need to use a macro to solve this question or your original question about the top 5. You can use an array formula.
Please see this screen shot for reference:
Setup:
A1:A7 has data, you will need to update with your range
C2 has the formula in C3
C3:C7 have the top 5 largest values
D2 has the formula in D3
D3:D7 have the top 5 small values that are greater than 0
Taking the Max as an example, put the formula in cell C3 and press enter. You will then get the largest number. From there highlight cell C3 and press shift down 4 times so you highlight the next 4 rows. Then go into the formula bar and click the formula like you want to edit it. From there press ctrl+shift+enter (PC) command+enter (apple, I think) and it will fill in the remaining cells. They will update as you change the values in the referenced range.
Here is a screen shot of what it should look like to do the array formula:
As you can see, the cell with the formula is the main cell with focus while the next 4 cells are highlighted. The cursor is in the formula box and then press the ctrl+shift+enter.
Minor Update:
You don't even need an array formula to solve the largest 5. You could just set each excel to LARGE(A1:A7, 1) then the next cell as LARGE(A1:A7, 2) then the next cell as LARGE(A1:A7, 3), etc..
You could try this:
Option Explicit
Sub best()
Dim copyrow As Long
Dim helpRng As Range
copyrow = 30
With Worksheets("Resumo")
With .Range("J11:J47")
Set helpRng = .Offset(, .Parent.UsedRange.Columns.Count)
helpRng.Value = .Value
helpRng.Offset(, 1).Value = .Offset(, -7).Value
Set helpRng = helpRng.Resize(.Rows.Count + 1, 2).Offset(-1)
End With
End With
With helpRng
.Cells(1, 1).Resize(, 2) = "header"
.Sort key1:=helpRng, order1:=xlAscending, Header:=xlYes
.AutoFilter field:=1, Criteria1:=">0"
If Application.WorksheetFunction.Subtotal(103, .Resize(, 1)) > 1 Then
Worksheets("os melhores").Cells(copyrow, "F").Resize(5, 2).Value = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Resize(5).Value
Worksheets("os melhores").Cells(copyrow, "G").Resize(5).TextToColumns DataType:=xlDelimited, ConsecutiveDelimiter:=True, Space:=True
End If
.Parent.AutoFilterMode = False
.ClearContents
End With
End Sub
and change order1:=xlAscending to order1:=xlDescending to have the top five highest values report in "os melhores" worksheet
Based on the code, I think the error is caused by that if you change WorksheetFunction.Large to WorksheetFunction.Small (which you did, right?) then maxvalue can be 0, so 0 is not a dedicated 'invalid value' anymore. You can do one of the following:
declare maxvalue as variant and use a different value to mean invalid, e.g. "n/a", examine if maxvalue is this one in the first If.
(and this also helps to filter out 0's and negative values) The first parameter of WorksheetFunction.Large should be another Worksheet function that excludes, negative (or non-positive values), like you would do it in a formula:
=IF( 0 < a, a, bignumber )
Write an algorithm to store the values and the line numbers into a fixed size array.

Change a cell's format to boldface if the value is over 500

I am using Excel 2010 and trying to add a bunch of rows placing the sum of columns A and B in column C. If the sum is over 500 I would then like to boldface the number in column C. My code below works works mathematically but will not do the bold formatting. Can someone tell me what I am doing wrong? Thank you.
Public Sub addMyRows()
Dim row As Integer 'creates a variable called 'row'
row = 2 'sets row to 2 b/c first row is a title
Do
Cells(row, 3).Formula = "=A" & row & "+B" & row 'the 3 stands for column C.
If ActiveCell.Value > 500 Then Selection.Font.Bold = True
row = row + 1
'loops until it encounters an empty row
Loop Until Len(Cells(row, 1)) = 0
End Sub
Pure VBA approach:
Public Sub AddMyRows()
Dim LRow As Long
Dim Rng As Range, Cell As Range
LRow = Range("A" & Rows.Count).End(xlUp).Row
Set Rng = Range("C2:C" & LRow)
Rng.Formula = "=A2+B2"
For Each Cell In Rng
Cell.Font.Bold = (Cell.Value > 500)
Next Cell
End Sub
Screenshot:
An alternative is conditional formatting.
Hope this helps.
Note: The formula in the block has been edited to reflect #simoco's comment regarding a re-run of the code. This makes the code safer for the times when you need to re-run it. :)

VBA Excel find and replace WITHOUT replacing items already replaced

I am looking to make an excel script that can find and replace data, but for the love of everything I cannot figure out how to write it.
Situation:
A-----------B-----------C
Cat-------Dog------Banana
Dog------Fish------Apple
Fish------Cat-------Orange
So the macro would look at the data in a cell in column B, then look at the adjacent cell in column C, and replace all instances of that data in column A with what if found in C. So the results would be:
A---------------B-----------C
Orange------Dog------Banana
Banana------Fish------Apple
Apple--------Cat-------Orange
But that's not all, I would like it to not change cells in A that already have been changed once! (I'm trying this with changing the background colour)
Any help? I am at a complete loss.
EDIT:
Okay I found out how to do the easy part (replacing), but I cannot find out how to not change cells that already have been changed once. Here is my code:
Sub multiFindNReplace()
Dim myList, myRange
Set myList = Sheets("sheet1").Range("A2:B3") 'two column range where find/replace pairs are
Set myRange = Sheets("sheet1").Range("D2:D5") 'range to be searched
For Each cel In myList.Columns(1).Cells
myRange.Replace what:=cel.Value, replacement:=cel.Offset(0, 1).Value, ReplaceFormat:=True
Next cel
End Sub
As far as I can tell, ReplaceFormat:=True
doesn't do anything ;/ so items that already have been replaced once still are being replaced! Is there a way to somehow make this work?
Here's the answer using your recommendation with color as a one-time limiter:
Sub Replace_Once()
'Find last row using last cell in Column B
LastRow = Range("B" & Rows.Count).End(xlUp).Row
'Clear colors in Column A
Range("A1:A" & LastRow).Interior.ColorIndex = xlNone
'Look at each cell in Column B one at a time (Cel is a variable)
For Each Cel In Range("B1:B" & LastRow)
'Compare the cell in Column B with the Value in Column A one at a time (C is a variable)
For Each C In Range("A1:A" & LastRow)
'Check if the Cell in Column A matches the Cell in Column B and sees if the color has changed.
If C.Value = Cel.Value And C.Interior.Color <> RGB(200, 200, 200) Then
'Colors the cell
C.Interior.Color = RGB(200, 200, 200)
'Updates the value in Column A with the cell to the right of the Cell in Column B
C.Value = Cel.Offset(0, 1).Value
End If
Next
Next
'Uncomment the line below to remove color again
'Range("A1:A" & LastRow).Interior.ColorIndex = xlNone
End Sub