Can you help me with loop that will go through cells A3:A50 and replace entire cell with a new value.
Reference below:
[ita-IT] to IT
[jpn] to JA
[por-BR] to PTBR
[spa-ES] to ES
etc.
Thanks for tips!
Try:
Sub ReplaceValues()
Dim r As Range
Dim v() As Variant
Dim i As Integer
v = [{"ita-IT","IT";"jpn","JA";"por-BR","PTBR";"spa-ES","ES"}]
Set r = ActiveSheet.Range("A3:A50")
For i = LBound(v) To UBound(v)
r.Replace what:=v(i, 1), replacement:=v(i, 2), lookat:=xlWhole, MatchCase:=False
Next i
End Sub
Edit:
There's no problem having 50 (or more) replacement pairs, but this would be much easier to manage by storing them in a table in the workbook, rather than listing them in a VBA array:
You can replace cell contents which have text before / after your lookup value by using wildcards. So combining those changes, your code now becomes:
Sub ReplaceValues2()
Dim r As Range
Dim v() As Variant
Dim i As Integer
v = Sheet1.ListObjects("tbReplacement").DataBodyRange
Set r = ActiveSheet.Range("A3:A50")
For i = LBound(v) To UBound(v)
r.Replace What:="*" & v(i, 1) & "*", Replacement:=v(i, 2), LookAt:=xlWhole, MatchCase:=False
Next i
End Sub
Here is an approach. If you get a lot of codes to replace it could look like spaghetti, and if there is a logic to the replacement, it would be nice to build in the logic, but it does what you asked, and is, I hope, readable.
Sub ReplaceStrings()
Dim result As String
For Each myCell In Range("A3:A50")
Select Case myCell.Value
Case "[ita-IT]"
result = "IT"
Case "[jpn]"
result = "JA"
Case "[por-BR]"
result = "PTBR"
Case "[spa-ES]"
result = "ES"
Case Else
result = myCell.Value
End Select
myCell.Value = result
Next myCell
End Sub
EDIT - To go with the original spirit but to meet the requirement of matching within the string, I replaced Case with an If Elseif series and used Like and wildcards to match. Note To match square brackets (assuming that was what you meant), I had to enclose them in square brackets. I also amended to reference the ActiveSheet to be safe, drawing on the other answer (which I am not criticizing, just showing another way to think about it).
Sub ReplaceStrings()
Dim result, s As String
For Each myCell In ActiveSheet.Range("A3:A50")
s = myCell.Value
If s Like ("*[[]ita-IT[]]*") Then
result = "IT"
ElseIf s Like "*[[]jpn[]]*" Then
result = "JA"
ElseIf s Like "*[[]por-BR[]]*" Then
result = "PTBR"
ElseIf s Like "*[[]spa-ES[]]*" Then
result = "ES"
Else
result = s
End If
myCell.Value = result
Next myCell
End Sub
Related
I am trying to use a formula with INDEX and MATCH to return a cell reference to enter TEXT into with VBA.
I have a list of vendors in column A and to find the cell to the right of it I can use the following
=CELL("address";INDEX(A29:C42;MATCH("***";A29:A42;0);2))
However I am struggling with how to get this in my VBA code. (note the value *** is changing as I need to run the sub several times for different vendors.
Can i use Function sub for this? I have tried as below with no luck:
Sub CellRef()
'
' CellRef
'
'
Function.Range(="CELL("ADDRESS";INDEX(A29:C42;MATCH("Accenture";A29:A42;0);2))")
End Sub
If I understand your question correctly you want to match the value/vendor in column A and return the address of the cell to its right?
You can do like so:
Cell2WorkWith = Cells(Application.Match(Searchvalue, Searchrange, 0),2).Address
If you don't want the "$", then replace them like so:
Cell2WorkWith = Replace(Cells(Application.Match(Searchvalue, Searchrange, 0),2).Address,"$","")
Or, even better, like:
Cell2WorkWith = Cells(Application.Match(Searchvalue, Searchrange, 0),2).Address(0,0)
If I misunderstood and it is the value from that cell to the right then below will do:
Value2WorkWith = Cells(Application.Match(Searchvalue, Searchrange, 0),2).Value
Remember to only use match when the value CAN be found within the range, else you'll have to catch an error.
One alternative would be to look for the cell with "Find":
Option Explicit
Sub CellRef()
Dim SearchString As String
Dim ra, cell, VendorsRange As Range
Dim k As Integer
Set VendorsRange = Range("E1:E10")
k = 1
For Each cell In VendorsRange
SearchString = cell.Value
Set ra = Range("A29:A42").Find(What:=SearchString, LookIn:=xlValues, LookAt _
:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False)
If ra Is Nothing Then
MsgBox "String not available"
Else
Range("D" & k).Value = ra.Offset(0, 1).Address 'Change "D1" to whereever you want to put your result in
End If
k = k + 1
Next cell
End Sub
The code would check for every vendor (in my code range "E1:E10"), where is the cell in your range A29:A42 and returns the address of the cell next to it.
Some people prefer to find all occurrences of a searched item, and then change the value or the formula, or do else. Here is some code allowing great flexibility using an array.
'**************************************************************************************************************************************************************
'To return an array of information (value, formula, address, row, and column) for all the cells from a specified Range that have the searched item as value
'Returns an empty array if there is an error or no data
'**************************************************************************************************************************************************************
Public Function makeArrayFoundCellInfoInRange(ByVal itemSearched As Variant, ByVal aRange As Variant) As Variant
Dim cell As Range, tmpArr As Variant, x As Long
tmpArr = Array()
If TypeName(aRange) = "Range" Then
x = 0
For Each cell In aRange
If itemSearched = cell.Value Then
If x = 0 Then
ReDim tmpArr(0 To 0, 0 To 4)
Else
tmpArr = reDimPreserve(tmpArr, UBound(tmpArr, 1) + 1, UBound(tmpArr, 2))
End If
tmpArr(x, 0) = cell.Value
tmpArr(x, 1) = cell.Formula
tmpArr(x, 2) = cell.Address(0, 0) 'Without the dollar signs
tmpArr(x, 3) = cell.Row
tmpArr(x, 4) = cell.Column
x = x + 1
End If
Next cell
End If
makeArrayFoundCellInfoInRange = tmpArr
Erase tmpArr
End Function
I need to be able to copy cells from one column to another that contain specific characters. In this example they would be ^ and * the characters can be in any order in the cell.
Here is an example :
It looks like I might be able to use the InStr function in VBA to accomplish this if I am not mistaken.
Run a loop for each item in the list and check it with something like the following:
IF InStr(1,Range("A" & i), "^") <> 0 AND InStr(1, Range("A" & i), "*") <> 0 THEN
'copy cell to another place
End If
or might there be a more elegant solution?
I can't see your image form where I am, but Like is generally easier and faster than Instr(). You could try something like this:
If Range("A" & i) Like "*[*^]*[*^]*" Then
meaning you look for some text, then * or a ^, more text, then * or *, more text
For detailed syntax, look here.
Option for no loops - use Arrays and Filter
Option Explicit
Sub MatchCharacters()
Dim src As Variant, tmp As Variant
Dim Character As String, Character2 As String
Character = "*"
Character2 = "^"
' Replace with your sheetname
With Sheet1
src = Application.Transpose(Range(.Cells(2, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, 1)))
tmp = Filter(Filter(src, Character), Character2)
.Range(.Cells(2, 3), .Cells(.Cells(1, 3).End(xlDown).Row, 3)).ClearContents
If UBound(tmp) > -1 Then
With .Cells(2, 3)
Range(.Offset(0, 0), .Offset(UBound(tmp), 0)).Value2 = Application.Transpose(tmp)
End With
End If
End With
End Sub
Or use as a function with unlimited character searching
Public Function MatchCharacters(arr As Variant, ParamArray Characters() As Variant) As Variant
Dim i As Long
For i = LBound(Characters) To UBound(Characters)
arr = Filter(arr, Characters(i))
Next i
MatchCharacters = arr
End Function
Sub test()
Dim tmp As Variant
With Sheet1
tmp = Application.Transpose(Range(.Cells(2, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, 1)))
tmp = MatchCharacters(tmp, "*", "^")
If UBound(tmp) > -1 Then
With .Cells(2, 3)
Range(.Offset(0, 0), .Offset(UBound(tmp), 0)).Value2 = Application.Transpose(tmp)
End With
End If
End With
End Sub
Edit
Looking at this again and being inspired by Tom's answer about filtering, it got be thinking... the AdvancedFilter can do exactly what you're looking to do. It's designed into the spreadsheet side of Excel, but you can use it from VBA.
If you only want to work out of VBA, or if your filter won't be changing often, then this probably is not your best choice... but if you want something that's more visible and flexible from the workbook side of things, this would be a good choice.
To manually run Advanced Filter...
Example code and dynamic filter scenario...
(Notice you can use equations with it)
Sub RunCopyFilter()
Dim CriteriaCorner As Integer
CriteriaCorner = Application.WorksheetFunction.Max( _
Range("B11").End(xlUp).Row, _
Range("C11").End(xlUp).Row, _
Range("D11").End(xlUp).Row)
[A4:A10].AdvancedFilter xlFilterCopy, Range("B4:D" & CriteriaCorner), [E4:E10], True
End Sub
Named Ranges
AdvancedFitler automatically creates NamedRanges for it's criteria and output. That can be handy because you can reference the NamedRange as Extract and it will dynamically update.
Original Post
Here's some code for a "tolerant" InStr() function from a similar post I made... it isn't tailored exactly to your example, but it gets at the basic point of character-by-character analysis.
Function InStrTolerant(InputString As String, MatchString As String, Optional CaseInsensitiveChoice = False, Optional Tolerance As Integer = 0) As Integer
'Similar to InStr, but allows for a tolerance in matching
Dim ApxStr As String 'Approximate String to Construct
Dim j As Integer 'Match string index
j = 1
Dim Strikes As Integer
Dim FoundIdx As Integer
For i = 1 To Len(InputString)
'We can exit early if a match has been found
If StringsMatch(ApxStr, MatchString, CaseInsensitiveChoice) Then
InStrTolerant = FoundIdx
Exit Function
End If
If StringsMatch(Mid(InputString, i, 1), Mid(MatchString, j, 1), CaseInsensitiveChoice) Then
'This character matches, continue constructing
ApxStr = ApxStr + Mid(InputString, i, 1)
j = j + 1
FoundIdx = i
Else
'This character doesn't match
'Substitute with matching value and continue constructing
ApxStr = ApxStr + Mid(MatchString, j, 1)
j = j + 1
'Since it didn't match, take a strike
Strikes = Strikes + 1
End If
If Strikes > Tolerance Then
'Strikes exceed tolerance, reset contruction
ApxStr = ""
j = 1
Strikes = 0
i = i - Tolerance
End If
Next
If StringsMatch(ApxStr, MatchString, CaseInsensitiveChoice) Then
InStrTolerant = FoundIdx
Else
InStrTolerant = 0
End If
End Function
Also, I always feel obliged to mention Regex in these cases. Although it isn't the easiest to use, especially with VBA, it is designed exactly for powerful complex matching.
I would like to have an VBA to extract an alphanumeric value from a column G which is a sentence.
This sentence is generally a comment. So it includes characters and numbers.
The value always starts with AI0 and ends with 0. This can be 11 to 13 digits long. Sometimes the number is mentioned in the comment as AI038537500, also sometimes as AI038593790000.
I have researched through almost all the websites, but have not found any case like this. I know about the formulas, left, right, mid but in my case, it doesn't apply.
Any lead would be appreciable.
You may try something like this...
Place the following User Defined Function on a Standard Module and then use it on the sheet like
=GetAlphaNumericCode(A1)
UDF:
Function GetAlphaNumericCode(rng As Range)
Dim Num As Long
Dim RE As Object, Matches As Object
Set RE = CreateObject("VBScript.RegExp")
With RE
.Global = False
.Pattern = "AI\d{9,}0"
End With
If RE.Test(rng.Value) Then
Set Matches = RE.Execute(rng.Value)
GetAlphaNumericCode = Matches(0)
Else
GetAlphaNumericCode = "-"
End If
End Function
Why not give something like the following a try?
Sub findMatches()
Dim strLength As Integer
Dim i As Long
For i = 1 To Rows.Count
Dim AllWords As Variant
AllWords = Split(Cells(i, 7).Value, " ")
For Each Item In AllWords
strLength = Len(Item)
If strLength > 0 And strLength <= 13 And Item Like "A10*?#" Then
Cells(i, 8) = Item
End If
Next
Next i
End Sub
Test Cases:
I am apple and my batch number is: A10545440 so incase you needed to know
Result: A10545440
Some random comment… A20548650
Result: NO RESULT
A101234567891 is an awesome alphanumeric combo
Result: A101234567891
Another random comment… A10555
Result: A10555
Notice: The above example covers cases where the alphanumeric combo, starting with A10 is either:
In the middle of a sentence, or
Beginning of a sentence, or
At the end of a sentence
Also note: right now it is set to go through ALL the rows... so if you want to limit that, change the Rows.Count in the For statement to whatever your set limit is.
EDIT:
In the above code, I am explicitly asking it to look in column G
can you give this a try? I think it should do the job, also you should ammend the code with the column values, I tested it with the comments being in column C, while the code will be written in column D.
Option Explicit
Sub FindValue()
Dim i As Long
Dim lastrow As Long
Dim lFirstChr As Long
Dim lLastChr As Long
Dim CodeName As String
lastrow = activesheet.Range("c" & Rows.Count).End(xlUp).Row
' gets the last row with data in it
For i = 1 To lastrow
' shuffles through all cell in data
lFirstChr = InStr(1, Cells(i, 3), "A10") ' gets the coordinate of the first instance of "A10"
If lFirstChr = 0 Then GoTo NextIteration
lLastChr = InStr(lFirstChr, Cells(i, 3), " ") ' gets the coordinate of the first instansce of space after "A10"
If lLastChr = 0 Then 'if there is no space after A10 then sets lastchr to the lenght of the string
lLastChr = Len(Cells(i, 3))
End If
CodeName = Mid(Cells(i, 3).Value, lFirstChr, lLastChr - lFirstChr) ' extracts the codename from the string value
Range("d" & i).Value = CodeName
Goto NextTteration
NextIteration:
Next i
End Sub
I need some help to create a macro which adds all the values on the column E between the rows with the "avg" word. the result should be displayed on the cells where the "Sum here" label is displayed. Both texts "avg" and "sum here" is just for illustrate the example, "avg" could be replaced by any other word and "sum here" should actually be the aggregation of the values above it.
The real challenge is that the number of ranges on column E is variable, so i would like to find a macro which is able to deal with "n" number of ranges on column E.
Finally, the values on column D are only the example of the expected value on the "sum here" cells.
This is what I have tried to far:
Sub Macro1()
'
' Macro1 Macro
'
Dim sumhere As Range
Dim startingpoint As Range
Dim endingpoint As Range
'
Range("C17").Select
Selection.End(xlDown).Select
If ActiveCell = "avg" Then
ActiveCell.Offset(rowoffset:=0, columnoffset:=2).Select
Set sumhere = ActiveCell
Set startingpoint = ActiveCell.Offset(rowoffset:=-1, columnoffset:=0)
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=sum(range(startingpoint:endingpoint)"
Else
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=Sum(Range(startingpoint.adress:endingpoint.adress))"
Else: End If
End If
End If
End Sub
Additionally, as you can see, I do not know, how to define a range using variables. My original idea was to combine this code with some kind of "do while" or/and "for i= 1 to x" and "next i". But I can't see how to combine it.
Using formula only, and providing that column A only has avg (or any text) on each subtotal row.
I've given two versions of the formula - the volatile version (updates everytime you change anything on the spreadsheet), and the non-volatile version (only updates if it needs to).
The formula should be entered on row 6 - change the $E6 to which ever row you need.
(volatile)
=SUM(OFFSET($E6,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)-ROW()+1,,ROW()-1-IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)))
(non volatile):
=SUM(INDEX($E:$E,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)+1):INDEX($E:$E,ROW()-1))
or if you don't mind using a helper column:
In cell B6:
=IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)
In E6: (volatile)
=SUM(OFFSET($E6,$B6-ROW()+1,,ROW()-1-$B6))
or (non volatile):
=SUM(INDEX($E:$E,$B6):INDEX($E:$E,ROW()-1))
Edit:
Thought I'd add a UDF to calculate it to if you're after VBA.
Use the function =AddSubTotal() in the rows you want the sub total to be shown in, or use =AddSubTotal("pop",6) to sum everything in column F (col 6) using "pop" rather than "avg".
Public Function AddSubTotal(Optional Delim As String = "avg", Optional ColNumber = 5) As Double
Dim rCaller As Range
Dim rPrevious As Range
Dim rSumRange As Range
Set rCaller = Application.Caller
With rCaller.Parent
Set rPrevious = .Range(.Cells(1, 1), .Cells(rCaller.Row - 1, 1)).Find(Delim, , , , , xlPrevious)
If Not rPrevious Is Nothing Then
Set rSumRange = rPrevious.Offset(1, ColNumber - 1).Resize(rCaller.Row - rPrevious.Row - 1)
Else
Set rSumRange = .Range(.Cells(1, ColNumber), .Cells(rCaller.Row - 1, ColNumber))
End If
End With
AddSubTotal = WorksheetFunction.Sum(rSumRange)
End Function
The following VBA routine assumes that
your data is in Columns C:E
Nothing else relevant (nothing numeric) in that range
Your "key word" where you want to show the sum is avg
avg (the key word) is hard-coded in the macro
You could easily modify this routine to also perform an average of those values, and put those results, for example, in Column D
Any of the above are easily modified
Option Explicit
Sub TotalSubRanges()
Dim vSrc As Variant, rSrc As Range
Dim dAdd As Double
Dim I As Long
Const sKey As String = "avg"
Set rSrc = Range(Cells(1, "C"), Cells(Rows.Count, "C").End(xlUp)).Resize(columnsize:=3)
vSrc = rSrc
'Do the "work" in a VBA array, as this will
' execute much faster than working directly
' on the worksheet
For I = 1 To UBound(vSrc, 1)
If vSrc(I, 1) = sKey Then
vSrc(I, 3) = dAdd
dAdd = 0
Else
If IsNumeric(vSrc(I, 3)) Then dAdd = dAdd + vSrc(I, 3)
End If
Next I
'write the results back to the worksheet
' and conditionally format the "sum" cells
With rSrc
.EntireColumn.Clear
.Value = vSrc
.Columns(3).AutoFit
.EntireColumn.ColumnWidth = .Columns(3).ColumnWidth
.FormatConditions.Delete
.FormatConditions.Add _
Type:=xlExpression, _
Formula1:="=" & .Item(1, 1).Address(False, True) & "=""" & sKey & """"
With .FormatConditions(1)
.Interior.ColorIndex = 6
End With
End With
End Sub
Surely you just need something like:
Sub sums()
Dim i As Integer, j As Integer, k As Integer
j = Range("C1048576").End(xlUp).Row
k = 1
For i = 1 To j
If Range("C" & i).Value <> "" Then
Range("E" & i).Value = "=Sum(E" & k & ":E" & i - 1 & ")"
k = i + 1
End If
Next i
End Sub
Change:
Dim startingpoint As Range
Dim endingpoint As Range
To:
Dim startingpoint As Variant
Dim endingpoint As Variant
As the startingpoint and endingpoint is used in a formula, you cant define them as a Range.
I have the following Excel table:
I want to pass the first column as a string, determine the address of the cells called 'LNA' and 'LCAMP', and sum the adjacent cells 'between' those two addresses. My failed code:
Function LNAtoLCAMP(ComponentList) As Single
Dim i As Integer
Dim LBoundAddress As Variant, UBoundAddress As Variant
For i = LBound(ComponentList) To UBound(ComponentList)
If ComponentList(i, 1).Value = "LNA" Then
LBoundAddress = ComponentList(i, 1).Address.Offset(0, 1)
End If
If ComponentList(i, 1).Value = "LCAMP" Then
UBoundAddress = ComponentList(i, 1).Address.Offset(0, 1)
End If
Next
LNAtoLCAMP = Application.WorksheetFunction.Sum(LBoundAddress, ":", UBoundAddress)
End Function
Maybe there's a better way?
Try this:
Function LNAtoLCAMP() As Single
Dim LNA As Range, LCAMP As Range
With Sheets("Sheet1")
Set LNA = .Range("B:B").Find("LNA").Offset(0, 1)
Set LCAMP = .Range("B:B").Find("LCAMP").Offset(0, 1)
If Not LNA Is Nothing And Not LCAMP Is Nothing Then _
LNAtoLCAMP = Evaluate("SUM(" & .Range(LNA, LCAMP).Address & ")")
End With
End Function
Edit2: For your dynamic needs.
Function CONSUM(rng As Range, str1 As String, str2 As String, _
Optional idx As Long = 1) As Variant
Application.Volatile '<~~ autoupdate on cell change, remove otherwise
Dim r1 As Range, r2 As Range
Set r1 = rng.Find(str1, rng(1), , xlWhole)
Set r2 = rng.Find(str2, rng(1), , xlWhole, , xlPrevious)
If Not r1 Is Nothing And Not r2 Is Nothing Then _
CONSUM = Application.Sum(rng.Parent.Range(r1.Offset(0, idx), _
r2.Offset(0, idx))) Else CONSUM = CVErr(xlErrValue)
End Function
In the second function, you can select the range you search and also specify the string you want to search. It returns #VALUE! error if the strings you specify are not found. HTH.
For Edit2 offset is dynamic as well (default at 1). Also this will sum the first instance of the 1st string up to the last instance of the second which was raised by chrisneilsen.
Result:
According to your comment you are calling the function as
=LNAtoLCAMP(B16:B61)
This is not passing an array, it is passing a range (that's a good thing)
Your function, modified:
Function LNAtoLCAMP(ComponentList As Range) As Variant
Dim i As Long
Dim dat As Variant
Dim Sum As Double
Dim LBoundAddress As Long, UBoundAddress As Long
dat = ComponentList.Value
For i = LBound(dat, 1) To UBound(dat, 1)
Select Case dat(i, 1)
Case "LNA", "LCAMP"
If LBoundAddress = 0 Then
LBoundAddress = i
End If
If i > UBoundAddress Then
UBoundAddress = i
End If
End Select
Next
For i = LBoundAddress To UBoundAddress
Sum = Sum + dat(i, 2)
Next
LNAtoLCAMP = Sum
End Function
Call it with both columns in the range
=LNAtoLCAMP(B16:C61)
Note:
I have assumed you want to include hidden rows in the sum, and "Between" includes the rows LNA and LCAMP are on. Both these assumptions are easily modified if required.
I have also assumed you want to sum from the first instance of either string to the last instance of either string. Also easily modified if required.
You could also pass in the search strings to make it more flexable.
You should add error handling, eg if one of the search string is not in the list
If you insist on using an ApplicationFunction then you need quotes.
Also I believe it should be .offset().address (Needs to be flipped)
TESTED WORKING:
Function LNAtoLCAMP(ByVal ComponentList As Range) As Single
Dim LBoundAddress As Range, UBoundAddress As Range
Dim cel As Range
For Each cel In ComponentList
If cel.Value = "LNA" Then
Set LBoundAddress = cel.Offset(0, 1)
End If
If cel.Value = "LCAMP" Then
Set UBoundAddress = cel.Offset(0, 1)
End If
Next cel
LNAtoLCAMP = Application.WorksheetFunction.Sum(Range(LBoundAddress, UBoundAddress))
End Function