This is the code that I have right now.
Do While IsEmpty(objTable.cell(intNoOfRow,2).Range.Text) = False
intNoOfRows = intNoOfRows + 1
Loop
Any help would be greatly appreciated.
IsEmpty tests to see if a Variant has a type of empty. It will always return False for a String. Test against vbNullString instead:
Do While objTable.cell(intNoOfRow,2).Range.Text = vbNullString
intNoOfRows = intNoOfRows + 1
Loop
We cannot use this code:
IsEmpty(objTable.cell(intNoOfRow,2).Range.Text) will always True, so the code will run into Infinite Loop. While Len(.Cell(intNoOfRows, 2).Range.Text) <>0 always return True too. We are not sure if this is by design or bug, but the new cell always contains character. Cell(intNoOfRow, 2).Range.Text = vbNullString doesn't work too.
Find a worked way on my side(InStr( [start], string, substring, [compare] ) ):
With objTable
Dim strCellText As String
strCellText = .Cell(intNoOfRow, 2).Range.Text
Do While InStr(1, strCellText, "", vbBinaryCompare) <> 1
MsgBox "OK"
Loop
End With
Some old try:
With objTable
For Each c In .Range.Rows
intNoOfRows = intNoOfRows + 1
Next
End With
Or
You can use the .Range.Rows.Count, but the following code still need to adjust
With objTable
Do While intNoOfRows < .Range.Rows.Count
intNoOfRows = intNoOfRows + 1
'MsgBox .Range.Rows.Count
'If intNoOfRows > .Range.Rows.Count Then
' Exit Do
'End If
Loop
End With
Try something based on:
Sub Demo()
Dim r As Long, i As Long
With ActiveDocument.Tables(1)
For r = 1 To .Rows.Count
With .Rows(r)
If Len(.Range.Text) = .Cells.Count * 2 + 2 Then i = i + 1
End With
Next
End With
MsgBox "There are " & i & " empty rows in the table"
End Sub
Similarly, to add a row before a non-empty row, you might use something like:
Sub Demo()
Dim r As Long, i As Long
With ActiveDocument.Tables(1)
For r = 1 To .Rows.Count
If Len(.Rows(r).Range.Text) > .Rows(r).Cells.Count * 2 + 2 Then
.Rows.Add .Rows(r)
End If
Next
End With
End Sub
Related
Basically what I'm trying to do here is use this sub:
Sub SelectFirstBlankCell()
Dim sourceCol As Integer, rowCount As Integer, currentRow As Integer
Dim currentRowValue As String
sourceCol = ActiveCell.Column 'Uses ActiveCell.Column as reference now, but needs to fit into each Subroutine to select next available
rowCount = Cells(Rows.Count, sourceCol).End(xlUp).Row
'for every row, find the first blank cell and select it
For currentRow = 1 To rowCount
currentRowValue = Cells(currentRow, sourceCol).Value
If IsEmpty(currentRowValue) Or currentRowValue = "" Then
Cells(currentRow, sourceCol).Select
Exit For
End If
Next
End Sub
To find the next empty cell in a column to input the string from this sub into.
Set selRange = Selection
For i = 0 To ListBox1.ListCount - 1
If ListBox1.Selected(i) = True Then
If strApps = "" Then
strApps = ListBox1.List(i)
intAppCodeOffset = i
strAppCodeVal = Worksheets("TestSheet").Range("B31").Offset(i, 0).Value
Else
strApps = strApps & ", " & ListBox1.List(i)
intAppCodeOffset = i
strAppCodeVal = strAppCodeVal & ", " & Worksheets("TestSheet").Range("B31").Offset(i, 0).Value
End If
End If
Next
Set selRange = selRange.Offset(1, 0)
With selRange
selRange.Value = strAppCodeVal
End With
I've tried replacing selRage.Offset(1, 0) with SelectFirstBlankCell, but I get an object reference error every time. Any help would be greatly appreciated on this as I can't seem to find how to do it on here.
As mentioned in the comments above, try changing Sub to Function, like this:
Function SelectFirstBlankCell(sourceCol as Integer) as Range
(remove old sourceCol dim and assignment)
...
Set SelectFirstBlankCell = Cells(currentRow, sourceCol)
...
End Function
Then you can do your change:
Set selRange = SelectFirstBlankCell(ActiveCell.Column) 'Or whatever you think should be defined as the sourceCol
Your code should probably not select anything itself, unless that is the end result of your macro. Try to use the code to directly manipulate the cells instead.
How to split cells containing "hard returns" (paragraph marks)
as in the picture below?
the desired result:
Here is my code
Sub SplitCells()
'
Dim selT As String
Dim arr
Dim i As Integer
selT = selection.Range.Text
arr = Split(selT, ChrW(13))
selection.Range.Cut
selection.Cells.Split NumRows:=UBound(arr) + 1, NumColumns:=1, MergeBeforeSplit:=False
selection.MoveDown wdLine, 1
For i = UBound(arr) To 0 Step -1
selection.MoveUp wdLine, 1
selection.TypeText arr(i)
Next
End Sub
It works, but I feel this code is clumsy and hope someone can tell me a elegant way.
Try the following; it will split all affected rows in the selected table.
Sub Demo()
Application.ScreenUpdating = False
Dim Tbl As Table, RngA As Range, RngB As Range
Dim i As Long, l As Long, r As Long, c As Long, p As Long
With Selection
If .Information(wdWithInTable) = False Then
MsgBox "Please select a table/cell and try again."
Exit Sub
End If
Set Tbl = .Tables(1)
With Tbl
l = .Columns.Count
For i = .Range.Cells.Count To 1 Step -1
With .Range.Cells(i).Range
Do While .Characters.Last.Previous = vbCr
.Characters.Last.Previous = vbNullString
Loop
End With
Next
For r = .Rows.Count To 1 Step -1
With .Rows(r)
If .Range.Paragraphs.Count > l + 1 Then
For c = 1 To .Cells.Count
If .Cells(c).Range.Paragraphs.Count > p Then p = .Cells(c).Range.Paragraphs.Count
Next
If p > 1 Then .Cells.Split Numrows:=p, Numcolumns:=1, MergeBeforeSplit:=False
For c = 1 To .Cells.Count
Set RngA = .Cells(c).Range
If RngA.Paragraphs.Count > 1 Then
For p = RngA.Paragraphs.Count To 2 Step -1
Set RngB = RngA.Paragraphs(p).Range
RngB.End = RngB.End - 1
If Len(RngB.Text) > 0 Then
With Tbl.Cell(r + p - 1, c).Range
.FormattedText = RngB.FormattedText
RngB.Delete
End With
End If
RngA.Paragraphs(p - 1).Range.Characters.Last = vbNullString
Next
End If
Next
End If
End With
Next
End With
End With
Application.ScreenUpdating = True
End Sub
Compared to your approach, the above code also has the advantage of preserving any text formatting.
There's nothing wrong with it, really. In order to move up/down in a table with split/merged cells you need Selection...
Here's code that uses the object model instead of Selection as much as possible. But I'm not sure I'd term it "more elegant" or "less clumsy". Possibly, it's more self-documenting since it uses Word objects where ever possible.
One change I did make is to test whether the selection is in a table before doing anything. If the user would forget to select a cell without such a test a cryptic error messsage would display, which is always annoying...
Sub SplitCells()
'
Dim cel As Word.Cell
Dim selT As String
Dim arr
Dim i As Integer
Dim nrCells As Long
If Selection.Information(wdWithInTable) Then
Set cel = Selection.Cells(1)
selT = cel.Range.Text
arr = Split(selT, ChrW(13))
nrCells = UBound(arr)
cel.Range.Delete
cel.Split NumRows:=nrCells, NumColumns:=1 ', _
'MergeBeforeSplit:=False
cel.Select
Selection.MoveDown wdLine, nrCells - 1
For i = nrCells - 1 To 0 Step -1
Set cel = Selection.Cells(1)
cel.Range.Text = arr(i)
cel.Select
Selection.MoveUp wdLine, 1
Next
Else
MsgBox "Please select a table cell and try again."
End If
End Sub
Dear stackoverflow community
At work I have to write a macro which should be able to hide rows based on numbers in a column. Those can be multiple ones in one cell and the input should also allow to show more than one number at a time.
for example:
row 1: 20, 30, 15
row 2: 20
row 3: 13, 76
So if I enter 20, 30, it should only show rows 1 & 2)
I usually code with Java / c# and Im new to VBA, so Id really appreciate help:
My plan was to show a input box and split those numbers into an array.
Then i wanna go through each row with a for-Loop, in which i added two for each loops to check if any numbers equal. If not, hide row. If so, show and then i want to exit both for each loops and go to the next row. To exit nested loops, i tried using a do while boolean but it doesnt seem to work.
Right now it only shows the rows with all the input numbers (only row1 in example).
Sub SortingTest()
Dim numbers() As String
myNum = Application.InputBox("Enter BKPS (separate multiples by , )")
numbers = Split(myNum, ",", -1, compare)
'Userinput Vars
Dim row As Integer
row = 1
Dim saveNumber As String
'Looping Vars
Dim existingNum As String
Dim existingNumsArray() As String
Dim checkRows As Long
Dim saveElement As String
Dim done As Boolean
done = False
' Range("B3").Value = 10
' Saves the Input as Array:
For Each Element In numbers
saveNumber = Element
Cells(2, row).Value = saveNumber
row = row + 1
Next Element
Dim b As Integer
Do While done = False
For b = 1 To 100 'hardcoded, should be length of document. b == row;
existingNum = Cells(b, 3).Value
existingNumsArray = Split(existingNum, ",", -1, compare)
' loop thru input numbers
For Each Element In numbers
saveElement = Element
'loop thru given numbers
For Each inputElement In existingNumsArray
If saveElement <> inputElement Then
Rows(b).Hidden = True
ElseIf saveElement = inputElement Then
Rows(b).Hidden = False
done = True
Exit For
End If
Next
Next
Next
Loop
End Sub
Thank you very much for you answer. Yours hid all the rows, so i adjusted it to show them.
Option Explicit
Function ArrOr(a As Variant, b As Variant) As Boolean
Dim runner As Variant
ArrOr = True
If IsArray(a) Then
For Each runner In a
If ArrOr(runner, b) Then Exit Function
Next
Else
For Each runner In b
If Trim(a) = Trim(runner) Then Exit Function
Next
End If
ArrOr = False
End Function
Sub SortingBKPS()
Dim numbers As Variant, vars As Variant, i As Long, xRows As Range
numbers = Split(Application.InputBox("Enter BKPS (separate multiples by , )"), ",")
With Sheets("Sheet1")
vars = .Range("B1", .Cells(.Rows.Count, 2).End(xlUp)).Value2
For i = 2 To UBound(vars)
.Rows(i).EntireRow.Hidden = True
If ArrOr(Split(vars(i, 1), ","), numbers) Then
If xRows Is Nothing Then
Set xRows = .Rows(i)
Else
Set xRows = Union(xRows, .Rows(i))
End If
End If
Next
xRows.EntireRow.Hidden = False
End With
End Sub
By splitting it up it is very easy to do:
Option Explicit
Function ArrOr(a As Variant, b As Variant) As Boolean
Dim runner As Variant
ArrOr = True
If IsArray(a) Then
For Each runner In a
If ArrOr(runner, b) Then Exit Function
Next
Else
For Each runner In b
If Trim(a) = Trim(runner) Then Exit Function
Next
End If
ArrOr = False
End Function
Sub SortingTest()
Dim numbers As Variant, vars As Variant, i As Long, xRows As Range
numbers = Split(Application.InputBox("Enter BKPS (separate multiples by , )"), ",")
With Sheets("Sheet1")
vars = .Range("B1", .Cells(.Rows.Count, 2).End(xlUp)).Value2
For i = 1 To UBound(vars)
If ArrOr(Split(vars(i, 1), ","), numbers) Then
If xRows Is Nothing Then
Set xRows = .Rows(i)
Else
Set xRows = Union(xRows, .Rows(i))
End If
End If
Next
xRows.EntireRow.Hidden = True
End With
End Sub
by running this code line by line, it should be pretty much self explaining (also knowing you have already some knowledge in "coding")
Still, if you have any questions, just ask ;)
You can also do it the following way:
Sub SortingTest()
Dim numbers As Variant
Dim RangeCompare As Range
Dim MyRow As Integer
Dim NumFound As Boolean
numbers = Application.InputBox("Please,list the values in this format: " & _
vbCrLf & "{value, value, value, ...}", _
Default:="{#, #, #}", Type:=64)
For MyRow = 1 To Cells(Rows.Count, 1).End(xlUp).row
Set RangeCompare = Range(Cells(MyRow, 1), Cells(MyRow, Columns.Count).End(xlToLeft))
NumFound = False
For Each rCell In RangeCompare
For Each Element In numbers
If rCell = Element Then
NumFound = True
Exit For
End If
Next Element
If NumFound = True Then Exit For
Next rCell
If NumFound = False Then
Rows(MyRow).Hidden = True
End If
Next MyRow
End Sub
I think it's easy to understand but feel free to ask for explanation.
I am looking for a method to compare a list of cell values to a certain reference value. If I would only need to compare the values I'd know how to achieve that. But here is the kicker: How can I look for a partial match? e.g.: the reference value should be "good". If the value of those cells would be "good" as well it should be considered a match. If the cell value is "Mr. goodcat" it should also be considered a match. My best guess would be to reference the original value to a string variable and put in some "*" if that would be possible.
Since I am not able to post some code, I don't need you to give me the whole answer, but a point in the right direction would be very nice. Thanks in advance guys.
edit: I have put in my final code. A short explaination: It loops through values in Sheet2 and compares them to values in column J in Sheet 1. If it finds a (partial) match, it highlights the cell.
Sub CompareValues()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim ws1Lrow As Long
Dim ws2Lrow As Long
Dim i As Integer
Dim x As Integer
Dim k As Integer
Dim reference As String
Set ws1 = ThisWorkbook.Sheets("Sheet1")
Set ws2 = ThisWorkbook.Sheets("Sheet2")
ws1Lrow = Worksheets("Sheet1").Range("A" & Worksheets("Sheet1").Rows.Count).End(xlUp).Row
ws2Lrow = Worksheets("Sheet2").Range("A" & Worksheets("Sheet2").Rows.Count).End(xlUp).Row
Application.ScreenUpdating = False
For i = 1 To ws2Lrow Step 1
ws2.Select
Cells(i, 1).Select
reference = ActiveCell
ws1.Select
For x = 2 To ws1Lrow
k = InStr(1, Cells(x, 10), reference, vbTextCompare)
If k > 0 Then
Cells(x, 10).Interior.ColorIndex = 6
End If
Next x
Next i
End Sub
How about this?
Dim I As Integer
I = InStr(1, "Mr. goodcat", "good", vbTextCompare)
If I > 0 Then
' Match
Else
' No Match
End
Here's more advanced function which allows wildcards in the middle:
Function PatternMatch(ByVal SearchIn As String, ByVal Pattern As String) As Boolean
If Len(SearchIn) = 0 Or Len(Pattern) = 0 Then
PatternMatch = False
Exit Function
End If
Dim Position As Integer
Dim MatchFirst As Boolean
Dim MatchLast As Boolean
Dim Chunks() As String
MatchFirst = (Left(Pattern, 1) <> "*")
MatchLast = (Right(Pattern, 1) <> "*")
Chunks = Split(Pattern, "*")
LastChunkIndex = UBound(Chunks)
If MatchFirst Then
If Not (Left(SearchIn, Len(Chunks(0))) = Chunks(0)) Then
PatternMatch = False
Exit Function
End If
End If
If MatchLast Then
If Not (Right(SearchIn, Len(Chunks(LastChunkIndex))) = Chunks(LastChunkIndex)) Then
PatternMatch = False
Exit Function
End If
End If
Position = 1
For Each Chunk In Chunks
ChunkLength = Len(Chunk)
If ChunkLength > 0 Then
NextPosition = InStr(Position, SearchIn, Chunk, vbTextCompare)
If NextPosition > 0 And NextPosition >= Position Then
Position = NextPosition + ChunkLength
Else
PatternMatch = False
Exit Function
End If
End If
Next Chunk
PatternMatch = True
End Function
I am able to search a text in column A of my spreadsheet by using this
With WB.Sheets("MySheet")
Set FindRow = .Range("A:A").Find(What:="ProjTemp1", LookIn:=xlValues)
End With
After which I can get the row number by doing FindRow.Row
How do I then get back the row number where Column A == "ProjTemp1" && Column B == "ProjTemp2" && Column C == "ProjTemp3"
Try to use Autofilter:
Dim rng As Range
'disable autofilter in case it's already enabled'
WB.Sheets("MySheet").AutoFilterMode = False
With WB.Sheets("MySheet").Range("A1:C1")
'set autofilter'
.AutoFilter Field:=1, Criteria1:="=ProjTemp1"
.AutoFilter Field:=2, Criteria1:="=ProjTemp2"
.AutoFilter Field:=3, Criteria1:="=ProjTemp3"
End With
With WB.Sheets("MySheet")
On Error Resume Next
Set rng = .Range("A2:A" & .Rows.Count).Rows.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
End With
If Not rng Is Nothing Then
MsgBox rng.Row ' returns first visible row number
End If
WB.Sheets("MySheet").AutoFilterMode = False 'disable autofilter'
An alternative suggestion is to just loop through the table and use nested if-statements like this:
Sub ReturnRowNumber()
Dim i As Long, GetRow As Long
For i = 2 To Sheets("MySheet").Cells(Rows.Count, 1).End(xlUp).Row
'Criteria search
If Sheets("MySheet").Cells(i, 1).Value = "ProjTemp1" Then
If Sheets("MySheet").Cells(i, 2).Value = "ProjTemp2" Then
If Sheets("MySheet").Cells(i, 3).Value = "ProjTemp3" Then
'Returns row
GetRow = i
End If
End If
End If
Next i
End Sub
Just posted similar reply at MSDN and wanted to share here if anyone is still using VBA. The function for multiple match that works pretty fast.
It might help a lot if you are interested in effective code since using Application.Match() is much much faster that Find() or INDEX() method or simple looping.
The syntax is the same as COUNTIFS() but it returns the match index instead of counting.
Public Function MultiMatch(ParamArray X0() As Variant) As Variant
MultiMatch = CVErr(xlErrNA)
If UBound(X0) = -1 Then Exit Function
On Error GoTo ErrorHandler
Set Xws = X0(1).Parent
X_rFrow = X0(1)(1, 1).Row
X_rLrow = X_rFrow + X0(1).Rows.Count - 1
jLAST = UBound(X0)
l = X_rFrow
j = 0
Do While IsError(MultiMatch) And j + 1 <= jLAST And Not IsError(X1)
jCOL = X0(j + 1).Column
Set TRNG = Xws.Range(Xws.Cells(l, jCOL), Xws.Cells(X_rLrow, jCOL))
X1 = Application.Match(X0(j), TRNG, 0)
If Not IsError(X1) Then
l = TRNG(X1).Row
If X1 = 1 Then
If j + 1 = jLAST Then
MultiMatch = l - X_rFrow + 1
Else
j = j + 2
End If
Else
j = 0
End If
End If
Loop
Exit Function
ErrorHandler:
MultiMatch = CVErr(xlErrName)
End Function
This can work in such a way that X amount of values to search are Y columns to search for X values in a row, having 0 as a result of nothing and Row>= 1 the row that has the X amount of values per column in the same row.
Public Function find(sheetName As String, initCol As Integer, initRow As Integer, ParamArray values()) As Variant
Dim i As Long, GetRow As Long
On Error GoTo nextRow
For i = initRow To Sheets(sheetName).cells(Rows.Count, 1).End(xlUp).row
For ii = 0 To UBound(values)
If Sheets(sheetName).cells(i, initCol + ii).Value2 = values(ii) Then
GetRow = ii
If ii = UBound(values) Then
find = i
Exit Function
End If
GoTo nextCol
End If
If ii = 0 Then GoTo nextRow
nextCol:
Next ii
nextRow:
Next i
endFind:
find = GetRow
End Function
Use :
vRow = find("sheet", 1, 1, "test", "test1","test2")
"sheet" = sheetName, 1 = Col index start, 1 = row number start, ["test","test1","test2"] is ParamArray
"find" Function will search "test" in colunm A, "test1" in B &
"test2" in C and it will return the row number that has these values
followed in the same row