I am trying to get 2 For loops to increment at the same time but am only able to get it to where one loop increments and after that loop has gone through its complete loop then the 2nd loop increments. I would like for the code to go down the list of both loops at the same time where it goes:
set criteria1 (1) and criteria2 (1) to the rngstart and rngend
then runs the For i = (rngStart.Row + 2) To (rngEnd.Row - 3) section and outputs to a text file
then set criteria1 (2) and criteria2 (2) to the rngstart and rngend
then runs the For i = (rngStart.Row + 2) To (rngEnd.Row - 3) section and outputs to a text file
etc.
Any guidance on what I am doing wrong and how to resolve the issue would be greatly appreciated.
Below is the code I am trying to resolve the issue with:
Sub ExportStuffToText()
Dim rngFind As Range, rngStart As Range, rngEnd As Range, rngPrint As Range, cell As Range
Dim Criteria1, Criteria2
Dim sTextPath
Dim strCriteria1() As String
Dim strCriteria2() As String
Dim a As Integer, b As Integer, i As Integer, j As Integer
Dim intCriteria1Max As Integer
Dim intCriteria2Max As Integer
Dim FileNum As Integer
Dim str_text As String
Dim sLine As String
Dim sType As String
Set rngFind = Columns("B")
intCriteria1Max = 5
ReDim strCriteria1(1 To intCriteria1Max)
strCriteria1(1) = "Entry1"
strCriteria1(2) = "Entry2"
strCriteria1(3) = "Entry3"
strCriteria1(4) = "Entry4"
strCriteria1(5) = "Entry5"
intCriteria2Max = 5
ReDim strCriteria2(1 To intCriteria2Max)
strCriteria2(1) = "Entry2"
strCriteria2(2) = "Entry3"
strCriteria2(3) = "Entry4"
strCriteria2(4) = "Entry5"
strCriteria2(5) = "Entry6"
For a = 1 To intCriteria1Max
For b = 1 To intCriteria2Max
Criteria1 = strCriteria1(a)
Set rngStart = rngFind.Find(What:=Criteria1, LookIn:=xlValues)
sTextPath = rngStart
Criteria2 = strCriteria2(b)
Set rngEnd = rngFind.Find(What:=Criteria2, LookIn:=xlValues)
If rngStart Is Nothing Then
MsgBox "Criteria1 not found"
Exit Sub
ElseIf rngEnd Is Nothing Then
MsgBox "Criteria2 not found"
Exit Sub
End If
FileNum = FreeFile
str_text = ""
For i = (rngStart.Row + 2) To (rngEnd.Row - 3)
sLine = ""
sType = Sheets![Sheetnamegoeshere].Cells(i, 8).Text
If sType = "somestring" Or sType = "adifferentstring" Then
For j = 1 To 2
If j > 1 Then
sLine = sLine & vbTab
End If
sLine = sLine & Sheets![Sheetnamegoeshere].Cells(i, j).Text
Next j
If Not Len(Trim(Replace(sLine, vbTab, ""))) = 0 Then
If i > 4 Then
str_text = str_text & IIf(str_text = "", "", vbNewLine) & sLine
End If
End If
End If
Next
Open sTextPath For Append As #FileNum
Print #FileNum, str_text
Close #FileNum
str_text = ""
Next b
Next a
End Sub
Ok I made some modifications in the code. I should be working but I did not test it. Give it a try.
Note that I split the original procedure into three smaller ones. Usually if you have a huge amount of variables on the top, it's a sign that the procedure is too large.
Option Explicit
Sub ExportStuffToText()
Dim shToWork As Worksheet
Dim arrCriteria(4, 1) As String
Dim strText As String
Dim rngFind As Range
Dim rngStart As Range
Dim rngEnd As Range
' Add the criterias pairs
arrCriteria(0, 0) = "Entry1"
arrCriteria(0, 1) = "Entry2"
arrCriteria(1, 0) = "Entry2"
arrCriteria(1, 1) = "Entry3"
arrCriteria(2, 0) = "Entry3"
arrCriteria(2, 1) = "Entry4"
arrCriteria(3, 0) = "Entry4"
arrCriteria(3, 1) = "Entry5"
arrCriteria(3, 0) = "Entry5"
arrCriteria(3, 1) = "Entry6"
' Put the name of the sheet here "Sheetnamegoeshere"
Set shToWork = Sheets("Sheetnamegoeshere")
Set rngFind = shToWork.Columns("B")
Dim t As Long
' Loop through my criteria pairs.
For t = LBound(arrCriteria, 1) To UBound(arrCriteria, 1)
'Try to find the values pair.
Set rngStart = rngFind.Find(what:=arrCriteria(t, 0), LookIn:=xlValues)
Set rngEnd = rngFind.Find(what:=arrCriteria(t, 1), LookIn:=xlValues)
If Not rngStart Is Nothing And Not rngEnd Is Nothing Then
' Create the text to append.
strText = GetStringToAppend(rngStart, rngEnd)
'Write to the file
WriteToFile rngStart.Value, strText
Else
' If one or more of the ranges is nothing then
' show a message.
If rngStart Is Nothing Then
MsgBox "Criteria1 not found"
Exit Sub
ElseIf rngEnd Is Nothing Then
MsgBox "Criteria2 not found"
Exit Sub
End If
End If
Next t
End Sub
'Creates a string that will be append to the file.
Function GetStringToAppend(ByRef rStart As Range, _
ByRef rEnd As Range) As String
Dim sh As Worksheet
Dim sLine As String
Dim sType As String
Dim ret As String
Dim i As Long, j As Long
'Grab the sheet from one of the ranges.
Set sh = rStart.Parent
For i = (rStart.Row + 2) To (rEnd.Row - 3)
sType = sh.Cells(i, 8).Text
If sType = "somestring" Or sType = "adifferentstring" Then
For j = 1 To 2
If j > 1 Then
sLine = sLine & vbTab
End If
sLine = sLine & sh.Cells(i, j).Text
Next j
If Not Len(Trim$(Replace(sLine, vbTab, vbNullString))) = 0 Then
If i > 4 Then
ret = ret & IIf(ret = vbNullString, vbNullString, vbNewLine) & sLine
End If
End If
End If
Next
'Return the value
GetStringToAppend = ret
End Function
'Procedure to write to the file.
Sub WriteToFile(ByVal strFilePath As String, _
ByVal strContent As String)
Dim FileNum As Integer
FileNum = FreeFile
Open strFilePath For Append As #FileNum
Print #FileNum, strContent
Close #FileNum
End Sub
I hope this helps :)
Related
I have a code that works with the selected running text but not working with the selected table cells.
Dim i As Integer
Dim oWords As Words
Dim oWord As Range
Set oWords = Selection.Range.Words
For i = 1 To oWords.Count Step 1
Set oWord = oWords(i)
''Make sure the word range doesn't include a space
Do While oWord.Characters.Last.text = " "
Call oWord.MoveEnd(WdUnits.wdCharacter, -1)
Loop
Debug.Print "'" & oWord.text & "'"
oWord.text = StrReverse(oWord.text)
Next i
I also have the code to extract each cell value but how to modify this to run on selected table cells.
First Code:
Sub Demo()
Dim x As String
Dim i As Integer
Dim j As Integer
Dim Tbl As Table
Set Tbl = ActiveDocument.Tables(1)
For i = 1 To Tbl.Rows.Count
For j = 1 To Tbl.Columns.Count
x = Tbl.Cell(i, j).Range.Text
Next j
Next i
End Sub
Second Code:
Sub testTable()
Dim arr As Variant
Dim intcols As Integer
Dim lngRows As Long
Dim lngCounter As Long
lngRows = ActiveDocument.Tables(1).Rows.Count
intcols = ActiveDocument.Tables(1).Columns.Count
arr = Split(Replace(ActiveDocument.Tables(1).Range.Text, Chr(7), ""), Chr(13))
For rw = 1 To lngRows
For col = 1 To intcols
Debug.Print "Table 1, Row " & rw & ", column " & col; " data is " & arr(lngCounter)
lngCounter = lngCounter + 1
Next
lngCounter = lngCounter + 1
Next
End Sub
Here is code that you should be able to adapt to your purpose.
Sub FindWordsInTableCells()
Dim doc As Word.Document, rng As Word.Range
Dim tbl As Word.Table, rw As Word.Row, cl As Word.Cell
Dim i As Integer, iRng As Word.Range
Set doc = ActiveDocument
For Each tbl In doc.Tables
For Each rw In tbl.rows
For Each cl In rw.Cells
Set rng = cl.Range
rng.MoveEnd Word.WdUnits.wdCharacter, Count:=-1
For i = 1 To rng.words.Count
Set iRng = rng.words(i)
Debug.Print iRng.Text
Next i
Next cl
Next rw
Next tbl
End Sub
If you want to only use cells that are currently selected then use this adaptation of the above routine.
Sub FindWordsInSelectedTableCells()
Dim rng As Word.Range
Dim cl As Word.Cell
Dim i As Integer, iRng As Word.Range
If Selection.Information(wdWithInTable) = True Then
For Each cl In Selection.Cells
Set rng = cl.Range
rng.MoveEnd Word.WdUnits.wdCharacter, Count:=-1
For i = 1 To rng.words.Count
Set iRng = rng.words(i)
rng.Select
'insert your word manipulation code here
Debug.Print Selection.Text
Next i
Next cl
End If
End Sub
I found this code, which add's one extra column to the chart each time it runs.
Meaning first time it runs it shows week 1-7, secound time 1-8, next 1-9 and I would like it to show 2-7, 3-8, 4-9 ect.
Sub ChartRangeAdd()
On Error Resume Next
Dim oCht As Chart, aFormulaOld As Variant, aFormulaNew As Variant
Dim i As Long, s As Long
Dim oRng As Range, sTmp As String, sBase As String
Set oCht = ActiveSheet.ChartObjects(1).Chart
oCht.Select
For s = 1 To oCht.SeriesCollection.count
sTmp = oCht.SeriesCollection(s).Formula
sBase = Split(sTmp, "(")(0) & "(<FORMULA>)" ' "=SERIES(" & "<FORMULA>)"
sTmp = Split(sTmp, "(")(1) ' "..., ..., ...)"
aFormulaOld = Split(Left(sTmp, Len(sTmp) - 1), ",") ' "..., ..., ..."
aFormulaNew = Array()
ReDim aFormulaNew(UBound(aFormulaOld))
' Process all series in the formula
For i = 0 To UBound(aFormulaOld)
Set oRng = Range(aFormulaOld(i))
' Attempt to put the value into Range, keep the same if it's not valid Range
If Err.Number = 0 Then
Set oRng = oRng.Worksheet.Range(oRng, oRng.Offset(0, 1))
aFormulaNew(i) = oRng.Worksheet.Name & "!" & oRng.Address
Else
aFormulaNew(i) = aFormulaOld(i)
Err.Clear
End If
Next i
sTmp = Replace(sBase, "<FORMULA>", Join(aFormulaNew, ","))
Debug.Print "Series(" & s & ") from """ & oCht.SeriesCollection(s).Formula & """ to """ & sTmp & """"
oCht.SeriesCollection(s).Formula = sTmp
sTmp = ""
Next s
Set oCht = Nothing
End Sub
I want to do the opposite of this code, so instead of adding a column one column should be substracted. How can the code be modifued to do this?
(LINK: VBA: Modify chart data range)
Thank you!
Try changing the line
Set oRng = oRng.Worksheet.Range(oRng, oRng.Offset(0, 1))
to
Set oRng = oRng.Worksheet.Range(oRng.Offset(0, 1), oRng.Offset(0, 1))
Public Function FindCodes(keywords As Range, text As String)
'FindCodes = "TEST"
Dim codeRows As Collection
Dim codeString As String
Set codeRows = New Collection
'Find Codes
For Each Item In keywords
Dim keywordArr() As String
Dim i As Integer
i = 0
If Item.Row <> 1 Then 'Ignore first row
keywordArr() = Split(Item, ",")
'On Error Resume Next
On Error GoTo ErrHandler
For Each s In keywordArr()
If InStr(LCase(text), LCase(s)) <> 0 Then
codeRows.Add Item.Row, CStr(Item.Row)
End If
Next s
End If
Next Item
'Build Codes String
If codeRows.Count > 0 Then
Dim codeArr() As String
'Set codeArr = New Collection
'Dim i As Integer
'i = 0
ReDim codeArr(codeRows.Count)
For Each s In codeRows
'codeArr.Add s, CStr(Worksheets("Codes").Range("A" & s).Value)
codeArr(i) = Worksheets("Codes").Range("A" & s).Value
'Set i = Worksheets("Codes").Range("B" + s).Value
i = i + 1
Next s
End If
'FindCodes = Join(codeArr, ",")
If UBound(codeArr) > 1 Then
FindCodes = Join(codeArr, ",")
ElseIf UBound(codeArr) = 1 Then
FindCodes = codeArr(0)
Else
FindCodes = ""
End If
ErrHandler:
If Err.Number = 457 Or Err.Number = 0 Or Err.Number = 20 Then
'foo = someDefaultValue
Resume Next
Else
'Err.Raise Err.Number
FindCodes = CVErr(xlErrValue)
End If
End Function
Sub temp()
Dim r As Range
Set r = Worksheets("Codes").Range("B:B")
MsgBox FindCodes(r, ".")
End Sub
Your code seems over-complex, but maybe I'm misunderstanding what it's supposed to do.
Try this:
Public Function FindCodes(keywords As Range, text As String)
Dim c As Range, keywordArr, s, rv
'only look at used cells
Set keywords = Application.Intersect(keywords, keywords.Worksheet.UsedRange)
For Each c In keywords.Cells
If c.Row > 1 And Len(c.Value) > 0 Then 'Ignore first row and empty cells
keywordArr = Split(c.Value, ",")
For Each s In keywordArr
If LCase(Trim(s)) = LCase(Trim(text)) Then
'grab value from ColA and go to next cell
rv = rv & IIf(Len(rv) = 0, "", ",") & c.EntireRow.Cells(1).Value
Exit For
End If
Next s
End If
Next c
FindCodes = rv
End Function
I'm working with some legacy code I'd like to build on and I can't seem to figure out the following: Why does the function AantalZichtbareRows return 1? Where It says For Each row In rng.Rows the row count is 1500 something (and so is the actual excel I'm working with).
I'm specifically puzzeled by n = r.Areas.Count. This is where the 1 originates.
Sub motivatieFormOpmaken()
Public iLaatsteKolom As Integer
Public iLaatsteRij As Integer
Public iKolomnrCorpID As Integer
Public iKolomnrNaam As Integer
Public iKolomnrHuidigeFunctie As Integer
Const StBestand = "Stambestand.xlsm"
Const motivatie = "Template motivatieformulier opstapregeling.xlsx"
Dim wbMotivTemp As Workbook
Dim wsMotiv As Worksheet
Dim PathOnly, mot, FileOnly As String
Dim StrPadSourcenaam As String
Set wbMotivTemp = ThisWorkbook
Set wsMotiv = ActiveSheet
StrHoofdDocument = ActiveWorkbook.Name
StrPadHoofdDocument = ActiveWorkbook.Path
StrPadSourcenaam = StrPadHoofdDocument & "\" & c_SourceDump
If Not FileThere(StrPadSourcenaam) Then
MsgBox "Document " & StrPadSourcenaam & " is niet gevonden."
Exit Sub
End If
Application.ScreenUpdating = False
Workbooks.Open FileName:=StrPadSourcenaam
Application.Run "Stambestand.xlsm!unhiderowsandcolumns"
Worksheets("stambestand").Activate
iLaatsteKolom = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).Column
iLaatsteRij = Worksheets("stambestand").Cells.SpecialCells(xlLastCell).row
VulKolomNr
If KolomControle = False Then Exit Sub
Aantalregels = AantalZichtbareRows
Dim rng As Range
Dim row As Range
Dim StrFileName As String
'If Aantalregels > 1 Then
Set rng = Selection.SpecialCells(xlCellTypeVisible)
For Each row In rng.Rows
iRijnummer = row.row
If iRijnummer > 1 Then
wsMotiv.Range("motiv_cid") = Cells(iRijnummer, iKolomnrCorpID).Text
wsMotiv.Range("motiv_naam") = Cells(iRijnummer, iKolomnrNaam).Text
wsMotiv.Range("motiv_ldg") = Cells(iRijnummer, iKolomnrHuidigeLeidingGevende).Text
n = naamOpmaken
wbMotivTemp.Activate
ActiveWorkbook.SaveAs FileName:=StrPadHoofdDocument & "\Docs\" & n & ".xlsm", FileFormat:=xlOpenXMLWorkbookMacroEnabled, CreateBackup:=False
End If
Next row
End Sub
Function naamOpmaken() As String
Dim rng As Range
Dim row As Range
Set rng = Selection.SpecialCells(xlCellTypeVisible)
iRijnummer = rng.row
If iRijnummer > 1 Then
naam = Cells(iRijnummer, iKolomnrNaam).Text
ldg = Cells(iRijnummer, iKolomnrHuidigeLeidingGevende).Text
cid = Cells(iRijnummer, iKolomnrCorpID).Text
Dim Position As Long, Length As Long
Dim n As String
Position = InStrRev(naam, " ")
Length = Len(naam)
n = Right(naam, Length - Position)
End If
naamOpmaken = n + "-" + ldg + "-" + cid
End Function
Public Function AantalZichtbareRows() As Integer
Dim rwCt As Long
Dim r As Range
Dim n As Long
Dim I As Long
Set r = Selection.SpecialCells(xlCellTypeVisible)
n = r.Areas.Count
For I = 1 To n
rwCt = rwCt + r.Areas(I).Rows.Count
Next I
AantalZichtbareRows = rwCt
End Function
Range.areas specifies the number of selection areas. Range.Areas
I tested your code and it works as expected. You can have a single selection area containing 1500 rows. Example: "A1:A1500" Or you can have a selection containing 2 areas with three rows each for a total of 6 rows. Example: "A1:A3" and "C4:C6".
This code might help you understand how the method returns information about the selected cells.
Public Function AantalZichtbareRows() As Integer
Dim rwCt As Long
Dim rwCt2 As Long
Dim r As Range
Dim n As Long
Dim I As Long
Set r = Selection.SpecialCells(xlCellTypeVisible)
n = r.Areas.Count
For I = 1 To n
rwCt = rwCt + r.Areas(I).Rows.Count
Next I
Set r = Selection
n = r.Areas.Count
For I = 1 To n
rwCt2 = rwCt2 + r.Areas(I).Rows.Count
Next I
Debug.Print n & " areas selected."
Debug.Print rwCt2 & " rows selected."
Debug.Print rwCt & " visible rows selected."
Debug.Print (rwCt2 - rwCt) & " hidden rows selected."
AantalZichtbareRows = rwCt
End Function
I have to search column B for a certain string and return a specific range of cells for all occurrences of the string in the file. I have code which searches and finds all occurrences of the string but have difficulty with copying into a new sheet the specific range of cells between Path and Owner. The catch is that the row numbers between Path and Owner are dynamic.
Excel structure
(including expected results for search string Kevin).
Macro
Sub FindString()
Dim intS As Integer
Dim rngC As Range
Dim strToFind As String, FirstAddress As String
Dim wSht As Worksheet
Application.ScreenUpdating = True
intS = 1
Set wSht = Worksheets("Search Results")
strToFind = Range("I3").Value 'This is where I obtain the string to be searched
With ActiveSheet.Range("B1:B999999")
Set rngC = .Find(what:=strToFind, LookAt:=xlPart)
If Not rngC Is Nothing Then
FirstAddress = rngC.Address
Do
(
'need help to find copy rows from column B based on values in column A
)
intS = intS + 1
Set rngC = .FindNext(rngC)
Loop While Not rngC Is Nothing And rngC.Address <> FirstAddress
End If
End With
Please help me or guide me as I'm a newbie to Excel.
This code will display the paths found (variable sPath), this is untested:
Sub FindString()
'Dim intS As Integer
Dim rngC As Range
Dim strToFind As String, FirstAddress As String
Dim wSht As Worksheet, lRowPath As Long, lRowOwner As Long, i As Long, sPath As String
'Application.ScreenUpdating = True
'intS = 1
Set wSht = Worksheets("Search Results")
strToFind = Range("I3").Value 'This is where I obtain the string to be searched
'With ActiveSheet.Range("B1:B999999")
With ActiveSheet.Range("B:B")
Set rngC = .Find(what:=strToFind, LookAt:=xlPart)
If Not rngC Is Nothing Then
FirstAddress = rngC.Address
Do
' Find the "Path:" above the found cell, note that offset too much is not handled: Cells(-1,1)
i = -1
Do Until InStr(1, rngC.Offset(i, -1).Value, "Path", vbTextCompare) > 0
i = i - 1
Loop
lRowPath = rngC.Row + i
' Find the Owner row above the found cell
i = -1
Do Until InStr(1, rngC.Offset(i, -1).Value, "Owner", vbTextCompare) > 0
i = i - 1
Loop
lRowOwner = rngC.Row + i
'need help to find copy rows from column B based on values in column A
sPath = ""
For i = lRowPath To lRowOwner - 1
sPath = sPath & ActiveSheet.Cells(i, "B").Value ' <-- Update
Next
Debug.Print "Searching " & strToFind; " --> " & sPath
'intS = intS + 1
Set rngC = .Find(what:=strToFind, After:=rngC, LookAt:=xlPart)
Loop Until rngC.Address = FirstAddress
End If
End With
End Sub
I suggest you load everything to memory first, then do your searches and manipulations.
You could use a user-defined type to store info about your paths:
Type PathPermissionsType
pth As String
owner As String
users As Dictionary
End Type
Note: to use Dictionary you need to go to Tools>References and set a checkmark next to Microsoft Scripting Runtime.
You can load all your info using something like this:
Function LoadPathPermissions() As PathPermissionsType()
Dim rngHeaders As Range
Dim rngData As Range
Dim iPath As Long
Dim nPath As Long
Dim iRow As Long
Dim nRow As Long
Dim vHeaders As Variant
Dim vData As Variant
Dim pathPermissions() As PathPermissionsType
Set rngHeaders = Range("A1:A12") 'or wherever
Set rngData = rngHeaders.Offset(0, 1)
'Load everything to arrays
vHeaders = rngHeaders.Value
vData = rngData.Value
nRow = UBound(vData, 1)
nPath = WorksheetFunction.CountIf(rngHeaders, "Path:")
ReDim pathPermissions(1 To nPath)
iRow = 1
'Look for first "Path:" header.
Do Until InStr(vHeaders(iRow, 1), "Path") <> 0
iRow = iRow + 1
Loop
'Found "Path:" header.
For iPath = 1 To nPath
With pathPermissions(iPath)
'Now look for "Owner:" header, adding to the path until it is found.
Do Until InStr(vHeaders(iRow, 1), "Owner") <> 0
.pth = .pth & vData(iRow, 1)
iRow = iRow + 1
Loop
'Found "Owner:" header.
.owner = vData(iRow, 1)
'"User:" header is on next row:
iRow = iRow + 1
'Now add users to list of users:
Set .users = New Dictionary
Do Until InStr(vHeaders(iRow, 1), "Path") <> 0
.users.Add vData(iRow, 1), vData(iRow, 1)
iRow = iRow + 1
If iRow > nRow Then Exit Do ' End of data.
Loop
End With
Next iPath
LoadPathPermissions = pathPermissions
End Function
Example usage:
Dim pathPermissions() As PathPermissionsType
pathPermissions = LoadPathPermissions()
Then to get an array containing the paths for a given user:
Function GetPathsForUser(ByVal user As String, pathPermissions() As PathPermissionsType) As String()
Dim iPath As Long
Dim iPathsWithPermission As Long
Dim nPathsWithPermission As Long
Dim pathsWithPermission() As String
For iPath = LBound(pathPermissions) To UBound(pathPermissions)
If pathPermissions(iPath).users.Exists(user) Then nPathsWithPermission = nPathsWithPermission + 1
Next iPath
ReDim pathsWithPermission(1 To nPathsWithPermission)
iPathsWithPermission = 0
For iPath = LBound(pathPermissions) To UBound(pathPermissions)
If pathPermissions(iPath).users.Exists(user) Then
iPathsWithPermission = iPathsWithPermission + 1
pathsWithPermission(iPathsWithPermission) = pathPermissions(iPath).pth
End If
Next iPath
GetPathsForUser = pathsWithPermission
End Function
Example usage:
Dim pathPermissions() As PathPermissionsType
Dim pathsWithPermission() As String
pathPermissions = LoadPathPermissions()
pathsWithPermission = GetPathsForUser("Kevin", pathPermissions)
Now pathsWithPermission is an array containing the paths for which Kevin is listed as user. Note that I haven't dealt with edge cases, like if Kevin is a not a user for any paths, etc. Up to you to do that.
Finally you can write the contents of that array to your sheet.