I'm trying to do a macro to produce a list based on a lookup list. For some reason the outer loop doesnt work, it only iterates once.
Sub Macro5()
Dim LookupRng As Range
Dim Store As String
Dim jrow As Integer
Dim irow As Integer
Dim i As Integer
Dim j As Integer
Set LookupRng = Sheet1.Range("B2") ' The Lookup range
jrow = Sheet2.Range("T" & Rows.Count).End(xlUp).Row ' last row of list of values to be searched
irow = Sheet1.Range("B" & Rows.Count).End(xlUp).Row ' last row of lookup range
Sheet3.Range("A2:A" & Rows.Count).Clear
For j = 2 To jrow
Store = Sheet2.Cells(j, 20).Value ' the value to be searched in the lookup range
For i = 1 To irow
If LookupRng.Value = Store Then
Sheet3.Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Value = LookupRng.Offset(0, -1).Value
End If
Set LookupRng = LookupRng.Offset(1, 0)
Next i
Next j
The i loop works, it searches all values that match "Store", but for some reason the j loop doesnt seem to be working, it doesnt jump to the next value of the "Store" list.
I'm new to this so would prefer a simple solution, but any help will be very appreciated
Loops with Offset
The issue was that you are not resetting the Lookup Range to the initial position after each inner loop finishes. So the following loops were trying to compare the values below the Lookup Range which were empty.
You should abandon the idea of 'offsetting' and use Cells or Range and increase the rows or define the range and use a For Each loop. But the best would be to use only one loop and use Application.Match to find a match.
A Quick Fix
Option Explicit
Sub lookupLoop()
Dim ilCell As Range ' Initial Lookup Cell Range
Dim lCell As Range ' Lookup Cell Range
Dim dCell As Range ' Destination Cell Range
Dim Store As Variant ' Current Value in Search Range
Dim iRow As Long ' Last Row of Lookup Range
Dim jRow As Long ' Last Row of Search Range
Dim i As Long ' Lookup Range Rows Counter
Dim j As Long ' Search Range Rows Counter
Set ilCell = Sheet1.Range("B2")
iRow = Sheet1.Range("B" & Sheet1.Rows.Count).End(xlUp).Row
jRow = Sheet2.Range("T" & Sheet2.Rows.Count).End(xlUp).Row
Sheet3.Range("A2:A" & Sheet3.Rows.Count).Clear
Set dCell = Sheet3.Range("A2")
For j = 2 To jRow
Store = Sheet2.Cells(j, "T").Value
Set lCell = ilCell '***
For i = 2 To iRow
If lCell.Value = Store Then
dCell.Value = lCell.Offset(0, -1).Value
Set dCell = dCell.Offset(1)
Exit For
End If
Set lCell = lCell.Offset(1)
Next i
Next j
End Sub
An Application.Match solution might look like this:
Sub lookupAM()
Dim lrg As Range ' Lookup Range (Read)
Dim vrg As Range ' Values Range (Write)
Dim srg As Range ' Search Range
Dim sCell As Range ' Current Cell in Search Range
Dim dCell As Range ' Current Cell in Destination Range
Dim cMatch As Variant ' Current Match
Dim lRow As Long ' Last Row of Lookup Range
Dim sRow As Long ' Last Row of Search Range
lRow = Sheet1.Range("B" & Sheet1.Rows.Count).End(xlUp).Row
Set lrg = Sheet1.Range("B2:B" & lRow)
Set vrg = lrg.Offset(, -1)
sRow = Sheet2.Range("T" & Sheet2.Rows.Count).End(xlUp).Row
Set srg = Sheet2.Range("T2:T" & sRow)
Sheet3.Range("A2:A" & Sheet3.Rows.Count).Clear
Set dCell = Sheet3.Range("A2")
For Each sCell In srg.Cells
cMatch = Application.Match(sCell.Value, lrg, 0)
If IsNumeric(cMatch) Then
dCell.Value = vrg.Cells(cMatch).Value
End If
Set dCell = dCell.Offset(1)
Next sCell
End Sub
Related
I'm trying to take the first column from my file (all rows except header) and delete text to the left of a colon character but I get a 400 error from VBA. I don't know what's wrong with this code.
As an example A2 (and subsequent cells in the A column) look like this:
Sub cleanLoginTime()
Dim cell As Range
Dim MyRange As Range
Dim tmp As String
LastRow = Cells(Rows.Count, 1).End(xlUp)
Set MyRange = ActiveSheet.Range("A2:A" & LastRow) 'this is your range of data
For Each cell In MyRange.Cells
tmp = cell.Value
'output n - 1 characters from the right
cell.Value = Right(tmp, Len(tmp) - 21)
Next
End Sub
Remove Left From Strings in Column
Sub CleanLoginTime()
Const FindString As String = ":"
Dim FindStringLength As Long: FindStringLength = Len(FindString)
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range
Set rg = ws.Range("A2", ws.Cells(ws.Rows.Count, "A").End(xlUp))
Dim cell As Range
Dim FindStringPosition As Long
Dim CellString As String
For Each cell In rg.Cells
CellString = CStr(cell.Value)
FindStringPosition = InStr(CellString, FindString)
If FindStringPosition > 0 Then ' string found
cell.Value = Right(CellString, Len(CellString) _
- FindStringPosition - FindStringLength + 1)
'Else ' string not found; do nothing
End If
Next cell
End Sub
To make it more efficient (faster), you could introduce an array (Data), to access the worksheet minimally.
Sub CleanLoginTimeArray()
Const FindString As String = ":"
Dim FindStringLength As Long: FindStringLength = Len(FindString)
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
Dim rg As Range
Set rg = ws.Range("A2", ws.Cells(ws.Rows.Count, "A").End(xlUp))
Dim Data() As Variant: Data = rg.Value ' works only if more than one cell!
Dim r As Long
Dim FindStringPosition As Long
Dim CellString As String
For r = 1 To UBound(Data, 1)
CellString = CStr(Data(r, 1))
FindStringPosition = InStr(CellString, FindString)
If FindStringPosition > 0 Then ' string found
Data(r, 1) = Right(CellString, Len(CellString) _
- FindStringPosition - FindStringLength + 1)
'Else ' string not found; do nothing
End If
Next r
rg.Value = Data
End Sub
I get a run-time error '13' because you need .row in
lastrow = Cells(Rows.Count, 1).End(xlUp).row
Do any of your cells have a length < 21?
I'm trying to make a SUMIF using VBA to sum the values until the end of the column, always changing the criteria of the sum using the previus cell of the previous column as parameter.
Sub SOMA()
Dim r As Range
For Each r In Range("E1")
r = ("E1" + 1)
LastRow = Range("E" & Rows.Count).End(xlUp).Row
Range("F1:F" & LastRow) = WorksheetFunction.SumIfs(Range("A:A"), Range("B:B"), Range(r))
Next r
End Sub
the r should always change to E1,E2,E3,etc. until end of the column. Because the E is always a new criteria.
Edit 1:
Sorry for my bad explaning, what i expected was make a SUMIF(A:A;E1;B:B) row by row placing the values on column F always changing the criteria to E2,E3,E4,etc. until the end of the column F.
VBA SumIfs
Option Explicit
Sub SOMA()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Source
Dim sws As Worksheet: Set sws = wb.Worksheets("Sheet1")
Dim slRow As Long: slRow = sws.Range("B" & sws.Rows.Count).End(xlUp).Row
Dim scrg As Range: Set scrg = sws.Range("B2:B" & slRow) ' Criteria Range
Dim ssrg As Range: Set ssrg = sws.Range("A2:A" & slRow) ' Sum Range
' Destination
Dim dws As Worksheet: Set dws = wb.Worksheets("Sheet1")
Dim dlRow As Long: dlRow = dws.Range("E" & dws.Rows.Count).End(xlUp).Row
Dim dcrg As Range: Set dcrg = dws.Range("E2:E" & dlRow) ' Criteria Range
Dim dsrg As Range: Set dsrg = dws.Range("F2:F" & dlRow) ' Sum Range
Dim dcOffset As Long: dcOffset = dsrg.Column - dcrg.Column
Application.ScreenUpdating = False
Dim dcCell As Range ' Criteria Cell
' Loop.
For Each dcCell In dcrg.Cells
dcCell.Offset(, dcOffset).Value = Application.SumIfs(ssrg, scrg, dcCell)
Next dcCell
Application.ScreenUpdating = True
MsgBox "Soma is done.", vbInformation
End Sub
I think you want multiple SUMIFS in column F where column E has the criteria. If so you don't need a loop, you can just enter the formula in F1 and use FILLDOWN to complete the rest
Sub SOMA()
Dim LastRow as Long
LastRow = Range("E" & Rows.Count).End(xlUp).Row
Range("F1") = WorksheetFunction.SumIfs(Range("A:A"), Range("B:B"), Range("E1"))
Range("F1:F"&LastRow).FillDown
End Sub
From what’s in your code I’m assuming:
column E is being SUMIF’d
column A is the range being assessed for the SUMIF
column B is providing the criteria for the SUMIF
the SUMIF value is being output to column F
This is how I’d do it…
Sub Soma()
Dim wf As WorksheetFunction
Dim r_A As Range, r_B As Range, r_E As Range, r_F As Range
Set wf = Application.WorksheetFunction
For i = 0 To ActiveSheet.Range("E1048576").End(xlUp).Row - 1
Set r_A = Range(Range("A1"), Range("A1").Offset(i, 0))
Set r_B = Range("B1").Offset(i, 0)
Set r_E = Range(Range("E1"), Range("E1").Offset(i, 0))
Set r_F = Range("F1").Offset(i, 0)
r_F = wf.SumIf(r_A, r_B, r_E)
Next
End Sub
Sub add_number()
Dim a As Currency
Dim i As Integer
lastrow = ActiveSheet.Cells(Rows.Count, 7).End(xlUp).Row
a = 0
For i = lastrow To Range("g4").Offset(1, 0) Step by - 1
a = a + ActiveSheet.Cells(i, 7).Value
Next
ActiveSheet.Cells(h1).Value = a
End Sub
Sum Up Filtered Column
In Excel, you could use the SUBTOTAL function.
In VBA, you could avoid a loop and just evaluate a formula or write a formula to the cell as illustrated in the following code.
Option Explicit
Sub SumFilteredColumn()
Const dRow As Long = 1 ' worksheet row
Const sCol As Long = 7 ' n-th column of the range (table)
Dim ws As Worksheet: Set ws = ActiveSheet ' improve!
' Restrict to continue only if the auto filter is turned on.
If Not ws.AutoFilterMode Then Exit Sub
' Reference the data column range.
Dim scrg As Range
With ws.AutoFilter.Range.Columns(sCol)
Set scrg = .Resize(.Rows.Count - 1).Offset(1)
End With
Dim dCol As Long: dCol = scrg.Column ' n-th column of the worksheet
' Write the sum (subtotal).
With ws.Cells(dRow, dCol)
' Write the value.
.Value = ws.Evaluate("SUBTOTAL(109," & scrg.Address & ")")
' Maybe writing a formula makes more sense.
'.Formula = "=SUBTOTAL(109," & scrg.Address(, 0) & ")"
End With
End Sub
I am unable to find the Object Error is appearing when i run the function. I do not why this is happening. It should work fine but no it does not. I hope to get some help and any help will be appreciated.
Sub SumIF()
Dim LastRow As Long
Dim sh As Worksheet
Set sh = Sheets("SumIF")
LastRow = sh.Cells(Rows.Count, 1).End(xlUp).Row
Table1 = sh.Range("A2:A" & LastRow) 'Need to Match this with Table3
Table2 = sh.Range("B2:B" & LastRow) 'Need to Sum this in K2:K
Table3 = sh.Range("J2:J" & LastRow)
sh.Range("K2:K" & LastRow) = Application.WorksheetFunction.SumIF(Table1, Table3, Table2)
End Sub
VBA SumIf Using .Formula
Option Explicit
Sub VBASumIfFormula()
' Workbook, Worksheet
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets("SumIf")
' Source Column Ranges
Dim slRow As Long: slRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
If slRow < 2 Then Exit Sub ' no data in column range
Dim slrg As Range: Set slrg = ws.Range("A2:A" & slRow) ' lookup column
Dim svrg As Range: Set svrg = slrg.Offset(0, 1) ' values column
' Destination Column Ranges
Dim dlRow As Long: dlRow = ws.Cells(ws.Rows.Count, "J").End(xlUp).Row
If dlRow < 2 Then Exit Sub ' no data in column range
Dim dlrg As Range: Set dlrg = ws.Range("J2:J" & dlRow) ' lookup column
Dim dvrg As Range: Set dvrg = dlrg.Offset(0, 1) ' values column (empty)
' Construct formula string.
Dim FormulaString As String
FormulaString = "=IFERROR(SUMIF(" & slrg.Address & "," _
& dlrg.Cells(1).Address(0, 0) & "," & svrg.Address & "),"""")"
'Debug.Print FormulaString
' Write formulas.
dvrg.Formula = FormulaString
' Convert formulas to values.
dvrg.Value = dvrg.Value
End Sub
If you insist on doing it your way, which is less efficient since looping is necessary, you could do...
' Either...
Dim cCell As Range
For Each cCell In Table3.Cells
cCell.Offset(0, 1).Value = Application.WorksheetFunction _
.SumIf(Table1, cCell.Value, Table2)
Next cCell
' ... or:
Dim cCell As Range
For Each cCell In sh.Range("K2:K" & LastRow).Cells ' or e.g. 'Table4'
cCell.Value = Application.WorksheetFunction _
.SumIf(Table1, cCell.Offset(0, -1).Value, Table2)
Next cCell
because the second argument, criteria, is not supposed to be a range:
... criteria in the form of a number, expression, a cell reference, text, or a function...
I am trying to change the value of each cell in column 7 that meets criteria. So far I managed to change the value with one criteria but I would like to add up to 14 criteria. Thanks for your help
Sub ChangeValue()
Dim i As Integer
Dim WK As Worksheet
Dim rg1 As range
Dim rg2 As range
Set WK = Sheet4
Set rg1 = range("AB2")
Set rg2 = range("AB3")
For i = Cells(Rows.Count, 7).End(xlUp).Row To 1 Step -1
If Cells(i, 7).Value = rg1 Then
Cells(i, 7).Value = rg2.Value
End If
Next i
End Sub
I would like to have more conditions something like if = AB3 change to AB4 if= AB4 Change to AB5 and so on...
To create a variable list of value/replace value pairs I would suggest using a dictionary:
Option Explicit
Sub ChangeValue()
Dim d
Set d = CreateObject("Scripting.Dictionary")
Dim r_test_value As Range
Dim r_anchor As Range
Set r_anchor = Range("AB2")
'need at least 2 values
If Not IsEmpty(r_anchor) And Not IsEmpty(r_anchor.Offset(1, 0)) Then
Set r_test_value = Range(r_anchor, _
Cells(Rows.Count, r_anchor.Column).End(xlUp).Offset(-1, 0))
Debug.Print r_test_value.Address
Dim i As Long
i = 0
Dim r As Range
For Each r In r_test_value
d.Add r.Value, r.Offset(i+1, 0).Value
i = i + 1
Next r
For i = Cells(Rows.Count, 7).End(xlUp).Row To 1 Step -1
If d.exists(Cells(i, 7).Value) Then
Cells(i, 7).Value = d.Item(Cells(i, 7).Value)
End If
Next i
End If
End Sub
Search and Replace Cell Values
EDIT
This is a more appropriate solution.
Adjust the starting rows i.e. For i = ? and For k = ?
Second Answer
Sub replaceValues()
' Determine Source Last Row.
Dim sLastRow As Long
sLastRow = Sheet4.Cells(Sheet4.Rows.Count, "AB").End(xlUp).Row
' Determine Destination Last Row.
Dim dLastRow As Long
dLastRow = Sheet4.Cells(Sheet4.Rows.Count, "G").End(xlUp).Row
Dim i As Long ' Destination Range Rows Counter
Dim k As Long ' Source Rows Counter
' Loop through rows of Destination Range.
For i = 2 To dLastRow
' Loop through rows of Source Range.
For k = 1 To sLastRow - 1
' When a value is found...
If Sheet4.Cells(i, "G").Value = Sheet4.Cells(k, "AB").Value Then
' ... replace it with the value below.
Sheet4.Cells(i, "G").Value = Sheet4.Cells(k + 1, "AB").Value
Exit For ' Value has been found and replaced. Stop searching.
' Otherwise you'll end up with the last replace value.
End If
Next k
Next i
' Inform.
MsgBox "Values replaced.", vbInformation, "Success"
End Sub
The First Answer (misunderstood)
The first solution is for using worksheet code names. It can be used for two worksheets. It is one in your case (Sheet4).
The second solution shows how to use it in two worksheets using worksheet names.
The code will loop through a column range of values and replace each value found in a row range of 'search values' with an associated 'replace value' in another same sized row range (in this case the ranges are adjacent, one below the other).
The Code
Option Explicit
Sub replaceValuesWorksheetCodeNames()
' Source
' Make sure the following two are of the same size.
Const srcAddress As String = "AB2:AO2"
Const rplAddress As String = "AB3:AO3"
' Destination
Const dFirstCell As String = "G2"
' Write Source Row Ranges to Source Arrays (Search and Replace).
With Sheet4
Dim srcData As Variant: srcData = .Range(srcAddress).Value
Dim rplData As Variant: rplData = .Range(rplAddress).Value
End With
' Define Destination Column Range.
Dim rg As Range
Dim RowOffset As Long
With Sheet4.Range(dFirstCell)
RowOffset = .Row - 1
Set rg = .Resize(.Worksheet.Rows.Count - RowOffset) _
.Find("*", , xlFormulas, , , xlPrevious)
If rg Is Nothing Then Exit Sub
Set rg = .Resize(rg.Row - RowOffset)
End With
' Write values from Destination Column Range to Data Array.
Dim rCount As Long: rCount = rg.Rows.Count
Dim Data As Variant
If rCount > 1 Then
Data = rg.Value
Else
ReDim Data(1 To 1, 1 To 1): Data = rg.Value
End If
' Search and replace values in Data Array.
Dim cValue As Variant
Dim cIndex As Variant
Dim i As Long
For i = 1 To rCount
cValue = Data(i, 1)
If Not IsError(cValue) Then
If Len(cValue) > 0 Then
cIndex = Application.Match(cValue, srcData, 0)
If IsNumeric(cIndex) Then
' When the replace data is in a row range.
Data(i, 1) = rplData(1, cIndex)
' When the replace data is in a column range.
'Data(i, 1) = rplData(cIndex, 1)
End If
End If
End If
Next i
' Write possibly modified values from Data Array back
' to Destination Column Range.
rg.Value = Data
' Inform.
MsgBox "Values replaced.", vbInformation, "Success"
End Sub
Sub replaceValuesWorksheetNames()
' Source
Const sName As String = "Sheet1"
' Make sure the following two are of the same size.
Const srcAddress As String = "AB2:AO2"
Const rplAddress As String = "AB3:AO3"
' Destination
Const dName As String = "Sheet2"
Const dFirstCell As String = "G2"
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook
' Write Source Row Ranges to Source Arrays (Search and Replace).
With wb.Worksheets(sName)
Dim srcData As Variant: srcData = .Range(srcAddress).Value
Dim rplData As Variant: rplData = .Range(rplAddress).Value
End With
' Define Destination Column Range.
Dim rg As Range
Dim RowOffset As Long
With wb.Worksheets(dName).Range(dFirstCell)
RowOffset = .Row - 1
Set rg = .Resize(.Worksheet.Rows.Count - RowOffset) _
.Find("*", , xlFormulas, , , xlPrevious)
If rg Is Nothing Then Exit Sub
Set rg = .Resize(rg.Row - RowOffset)
End With
' Write values from Destination Column Range to Data Array.
Dim rCount As Long: rCount = rg.Rows.Count
Dim Data As Variant
If rCount > 1 Then
Data = rg.Value
Else
ReDim Data(1 To 1, 1 To 1): Data = rg.Value
End If
' Search and replace values in Data Array.
Dim cValue As Variant
Dim cIndex As Variant
Dim i As Long
For i = 1 To rCount
cValue = Data(i, 1)
If Not IsError(cValue) Then
If Len(cValue) > 0 Then
cIndex = Application.Match(cValue, srcData, 0)
If IsNumeric(cIndex) Then
' When the replace data is in a row range.
Data(i, 1) = rplData(1, cIndex)
' When the replace data is in a column range.
'Data(i, 1) = rplData(cIndex, 1)
End If
End If
End If
Next i
' Write possibly modified values from Data Array back
' to Destination Column Range.
rg.Value = Data
' Inform.
MsgBox "Values replaced.", vbInformation, "Success"
End Sub