Excel VBA search within range from previous column - excel

I tried to implement this but I have a compiler error ("wrong qualification", or something like this, it's not an English version of Excel I have). I suppose it has to do with range / string things ?
Function SearchForTotal(givenLocation As Range, searchText As String) As Range
Debug.Print givenLocation 'gives $U$83
Dim startSearchFrom As String
'-1 because it's from previous column you'll be searching in
startSearchFrom = givenLocation.Offset(0, -1).Address
Debug.Print startSearchFrom
Dim i As Integer: i = startSearchFrom.Row
Do While i > 0
If (searchText = ThisWorkbook.Sheets("Sheet1").Range(startSearchFrom.column & i).Value) Then
Set SearchForTotal= Range(startSearchFrom.column & i)
Exit Do
End If
i = i - 1
Loop
End Function
The error comes from the line "Dim i As Integer: i = startSearchFrom.Row"
I also tried with the variable startSearchFrom as a range instead of a string (and then with the Set) but with this code I have a compiler error too ("types do not match").

startSearchFrom.column is a number so use .Cells(rowno,colno) rather than .Range()
Option Explicit
Function SearchForTotal(givenLocation As Range, searchText As String) As Range
Dim ws As Worksheet, iCol As Long, iRow As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
'-1 because it's from previous column you'll be searching in
iCol = givenLocation.Offset(0, -1).Column
iRow = givenLocation.Row
Do While iRow > 0
If (searchText = ws.Cells(iRow, iCol).Value) Then
Set SearchForTotal = ws.Cells(iRow, iCol)
Exit Do
End If
iRow = iRow - 1
Loop
End Function
Sub test()
Debug.Print SearchForTotal(Range("U83"), "test").Address
End Sub

Find Value Using Loop
Using the Find method would certainly be a better (more efficient) way.
Option Explicit
Function SearchForTotalLoop( _
ByVal GivenLocation As Range, _
ByVal SearchText As String) _
As Range
If GivenLocation Is Nothing Then Exit Function
' There's nothing to left of column `A`:
If GivenLocation.Column = 1 Then Exit Function
'-1 because it's from the previous column you'll be searching in
Dim rgStart As Range: Set rgStart = GivenLocation.Offset(0, -1)
Dim ws As Worksheet: Set ws = GivenLocation.Worksheet
Dim r As Long: r = rgStart.Row
Dim Col As Long: Col = rgStart.Column
Do While r > 0
If ws.Cells(r, Col).Value = SearchText Then ' A<>a
' To ignore case i.e. 'A = a', rather use the following:
'If StrComp(ws.Cells(r, Col).Value, SearchText, vbTextCompare) = 0 Then
Set SearchForTotal = ws.Cells(r, Col)
Exit Do
End If
r = r - 1
Loop
End Function
Sub SearchForTotalTEST()
' s - Start
' f - Found
Dim sCell As Range: Set sCell = Range("B83")
Dim fCell As Range: Set fCell = SearchForTotal(sCell, "Total")
If fCell Is Nothing Then Exit Sub
MsgBox "Starting Cell: " & sCell.Address & vbLf _
& "Found Cell: " & fCell.Address & vbLf _
& "Found Value: " & fCell.Value, vbInformation, "Find Total"
End Sub
EDIT
Using the Find method, you could do something like the following (not tested).
Function SearchForTotal( _
ByVal GivenLocation As Range, _
ByVal SearchText As String) _
As Range
' These two could be additionally used as arguments of the function.
Const FirstRow As Long = 1
Const ColOffset As Long = -1
If GivenLocation Is Nothing Then Exit Function
' There's nothing to left of column `A`:
If GivenLocation.Column + ColOffset < 1 Then Exit Function
If FirstRow > GivenLocation.Row Then Exit Function
Dim ws As Worksheet: Set ws = GivenLocation.Worksheet
If GivenLocation.Column + ColOffset > GivenLocation.Columns.Count _
Then Exit Function
If FirstRow > GivenLocation.Rows.Count Then Exit Function
Dim lCell As Range: Set lCell = GivenLocation.Cells(1).Offset(0, ColOffset)
Dim fCell As Range: Set fCell = ws.Cells(FirstRow, lCell.Column)
Dim rg As Range: Set rg = ws.Range(fCell, lCell)
Dim rCell As Range
Set rCell = rg.Find(SearchText, , xlFormulas, xlWhole, , xlPrevious)
If rCell Is Nothing Then Exit Function
Set SearchForTotal = rCell
End Function

Related

VBA Alert for items that in Column A and are not mapped to Column B

Good day. I have sheet with 2 columns A and B. I want to know if how many in the items in Column A and are not mapped to Column B and display it if what are those items. Thank you so much.
Return Not Matching Items
Excel
Plain
=UNIQUE(FILTER(A2:A21,ISNA(XMATCH(A2:A21,B2:B21))))
LET
=LET(vCol,A2:A21,lCol,B2:B21,fInc,ISNA(XMATCH(vCol,lCol)),
UNIQUE(FILTER(vCol,fInc)))
LET Variables
vCol - Value Column
lCol - Lookup Column
fInc - Filter Include
VBA
Sheet Module e.g. Sheet1
Private Sub Worksheet_Activate()
CheckMappings Me
End Sub
The rest goes into one or more standard modules e.g. Module1.
Simple Test
Sub CheckMappingsTEST()
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
CheckMappings ws
End Sub
Main
Sub CheckMappings(ByVal ws As Worksheet)
Const SEARCH_FIRST_CELL As String = "A2"
Const MATCH_FIRST_CELL As String = "B2"
Dim srg As Range: Set srg = RefColumn(ws.Range(SEARCH_FIRST_CELL))
If srg Is Nothing Then Exit Sub
Dim mrg As Range: Set mrg = RefColumn(ws.Range(MATCH_FIRST_CELL))
If mrg Is Nothing Then Exit Sub
Dim sData(): sData = GetColumnRange(srg)
Dim sDict As Object: Set sDict = DictColumn(sData)
If sDict Is Nothing Then Exit Sub
Dim mData(): mData = GetColumnRange(mrg)
Dim mDict As Object: Set mDict = DictColumn(mData)
If mDict Is Nothing Then Exit Sub
RemoveDictFromDict sDict, mDict
If sDict.Count = 0 Then
MsgBox "No items to fix.", vbInformation
Else
MsgBox "The following " & IIf(sDict.Count = 1, "item is", _
sDict.Count & " items are") & " not mapped:" & vbLf & vbLf _
& Join(sDict.Keys, vbLf) & vbLf & vbLf & "Please fix.", vbCritical
End If
End Sub
The Help
Reference Non-Empty Column
Function RefColumn( _
ByVal FirstCell As Range) _
As Range
With FirstCell.Cells(1)
Dim cel As Range: Set cel = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If Not cel Is Nothing Then Set RefColumn = .Resize(cel.Row - .Row + 1)
End With
End Function
Column To Array
Function GetColumnRange( _
ByVal rg As Range, _
Optional ByVal ColumnIndex As Long = 1) _
As Variant
With rg.Columns(ColumnIndex)
If .Rows.Count = 1 Then
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = .Value
Else
Data = .Value
End If
End With
GetColumnRange = Data
End Function
Unique From Array to Dictionary
Function DictColumn( _
Data() As Variant, _
Optional ByVal ColumnIndex As Variant) _
As Object
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare ' case-insensitive
Dim c As Long
If IsMissing(ColumnIndex) Then
c = LBound(Data, 2) ' use first column index
Else
c = CLng(ColumnIndex)
End If
Dim Key As Variant
Dim r As Long
For r = LBound(Data, 1) To UBound(Data, 1)
Key = Data(r, c)
If Not IsError(Key) Then ' exclude error values
If Len(CStr(Key)) > 0 Then ' exclude blanks
dict(Key) = Empty
End If
End If
Next r
If dict.Count = 0 Then Exit Function ' only error values and blanks
Set DictColumn = dict
End Function
Remove Matches
Sub RemoveDictFromDict( _
ByRef RemoveDict As Object, _
ByVal MatchDict As Object)
Dim rkey As Variant
For Each rkey In RemoveDict.Keys
If MatchDict.Exists(rkey) Then RemoveDict.Remove rkey
Next rkey
End Sub

Function for finding the row of a cell with a specific text

If I want to find the row number of a cell in column A with the text containing the string "Total Labor", how can I do that?
Another is how can I find the position of the cell containing that text if it can be in any column and row?
This is what I have, but it returns as an Empty.
TotalLaborPos is defined as a Variant.
lastrow = Range("A11").End(xlDown)
TotalLaborPos.Value = ActiveSheet.Match("Total Labor", Range("A11:A" & lastrow), 0)
It's Application.Match not ActiveSheet.Match
If it can be in any column/row then use Find()
E.g.
Dim f As Range
Set f = ActiveSheet.Cells.Find("Total Labor",lookat:=xlWhole,lookin:=xlValues)
If not f is nothing then
debug.print "found", f.address
end if
The Worksheet Row of the First Match in a Column
Option Explicit
Sub GetFirstMatchInColumnRowTEST()
Const First As String = "A11"
Const StringToMatch As String = "Total Labor"
Dim ws As Worksheet: Set ws = ActiveSheet
Dim fCell As Range: Set fCell = ws.Range(First)
Dim mRow As Long: mRow = GetFirstMatchInColumnRow(fCell, StringToMatch)
If mRow = 0 Then Exit Sub ' not found
' Continue with code...
Debug.Print ws.Cells(mRow, fCell.Column).Address(0, 0)
End Sub
Function GetFirstMatchInColumnRow( _
ByVal rg As Range, _
ByVal StringToMatch As String) _
As Long
If rg Is Nothing Then Exit Function ' no range
' Create a reference to the Search (Column) Range ('srg').
Dim wsrCount As Long: wsrCount = rg.Worksheet.Rows.Count
Dim fRow As Long: fRow = rg.Row
Dim srCount As Long: srCount = wsrCount - fRow + 1
Dim srg As Range: Set srg = rg.Cells(1).Resize(srCount)
' 1.) Using 'Range.Find'.
Dim mCell As Range: Set mCell = srg.Find(StringToMatch, _
srg.Cells(srg.Cells.Count), xlFormulas, xlWhole)
If mCell Is Nothing Then Exit Function ' not found
GetFirstMatchInColumnRow = mCell.Row
' ' 2.) Using 'Application.Match' '
' Dim rIndex As Variant: rIndex = Application.Match(StringToMatch, srg, 0)
' If IsError(rIndex) Then Exit Function ' not found
'
' GetFirstMatchInColumnRow = srg.Cells(rIndex).Row
End Function

Combine data from multiple worksheets to one sheet on key word from column

im sorry for making similar question but im run into a problem, bcs i don t know very good VBA coding...
I found many similar questions, and i found a code that i can apply to my needs.
I found code here But i don't know how to edit that code so that he can work in my Workbook. I have workbook with 35 worksheets, all with same format, values are in columns "A:F", in column "E" i have text "On Stock" and "Sent", i want all rows from all worksheets that have "On Stock" value in column "E" to be copied into one worksheet named "Blanko List". I tried to edit code myself, but it can t run, nothing happens. Thanks in advance.
Edited code
Sub CommandButton4_Click()
Dim wM As Worksheet, ws As Worksheet
Dim r As Long, lr As Long, nr As Long, y As Long
Dim c As Range, firstaddress As String
Application.ScreenUpdating = False
Set wM = Sheets("Blanko List")
lr = wM.Cells.Find("*", , xlValues, xlWhole, xlByRows, xlPrevious, False).Row
If lr > 1 Then wM.Range("A2:G" & lr).ClearContents
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Blanko List" Then
y = 0
On Error Resume Next
y = Application.CountIf(ws.Columns(7), "On Stock")
On Error GoTo 0
If y > 1 Then
firstaddress = ""
With ws.Columns(7)
Set c = .Find("On Stock", LookIn:=xlValues, LookAt:=xlWhole)
If Not c Is Nothing Then
firstaddress = c.Address
Do
nr = wM.Range("G" & Rows.Count).End(xlUp).Offset(1).Row
ws.Range("A" & c.Row & ":G" & c.Row).Copy wM.Range("A" & nr)
Application.CutCopyMode = False
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstaddress
End If
End With
End If
End If
Next ws
wM.Activate
Application.ScreenUpdating = True
''''
Original code:
Option Explicit
Sub GetYes()
Dim wM As Worksheet, ws As Worksheet
Dim r As Long, lr As Long, nr As Long, y As Long
Dim c As Range, firstaddress As String
Application.ScreenUpdating = False
Set wM = Sheets("Master")
lr = wM.Cells.Find("*", , xlValues, xlWhole, xlByRows, xlPrevious, False).Row
If lr > 1 Then wM.Range("A2:G" & lr).ClearContents
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> "Master" Then
y = 0
On Error Resume Next
y = Application.CountIf(ws.Columns(7), "Yes")
On Error GoTo 0
If y > 1 Then
firstaddress = ""
With ws.Columns(7)
Set c = .Find("Yes", LookIn:=xlValues, LookAt:=xlWhole)
If Not c Is Nothing Then
firstaddress = c.Address
Do
nr = wM.Range("G" & Rows.Count).End(xlUp).Offset(1).Row
ws.Range("A" & c.Row & ":G" & c.Row).Copy wM.Range("A" & nr)
Application.CutCopyMode = False
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstaddress
End If
End With
End If
End If
Next ws
wM.Activate
Application.ScreenUpdating = True
End Sub
Copy Criteria Rows
Option Explicit
Sub CopyCriteriaRows()
' Source
Const sCols As String = "A:F"
Const sfRow As Long = 2
Const scCol As Long = 5
Const sCriteria As String = "On Stock"
' Destination
Const dName As String = "Blanco List"
Const dFirst As String = "A2"
' Exceptions
Const ExceptionsList As String = "Blanco List" ' add more
Const ListSeparator As String = ","
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Write the names of the worksheets to be 'processed' to an array.
Dim wsNames As Variant
wsNames = ArrWorksheetNames(wb, ExceptionsList, ListSeparator)
If IsEmpty(wsNames) Then Exit Sub ' no worksheet found
' Create a reference to the first destination row range.
' Note that the number of columns is equal in source and destination.
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim cCount As Long: cCount = dws.Columns(sCols).Columns.Count
Dim drrg As Range: Set drrg = dws.Range(dFirst).Resize(, cCount)
Dim sws As Worksheet ' Source Worksheet
Dim srg As Range ' Source Range
Dim sfrrg As Range ' Source First Row Range
Dim drg As Range ' Destination Range
Dim Data As Variant ' Data Array
Dim cValue As Variant ' Current Value
Dim dr As Long ' Destination Row Counter
Dim sr As Long ' Source Row Counter
Dim c As Long ' Column Counter
For Each sws In wb.Worksheets(wsNames)
' Create a reference to the current Source First Row Range.
Set sfrrg = sws.Columns(sCols).Rows(sfRow)
Set srg = Nothing
' Create a reference to the current Source Range.
Set srg = RefColumns(sfrrg)
If Not srg Is Nothing Then ' the current Source Range is not empty
' Write the values from the current Source Range to the Data Array.
Data = GetRange(srg)
' Write the matches to the top of the Data Array. The size
' of the array stays the same but 'dr' is used: to track
' the number of, to move, and finally, to write (to the worksheet)
' the 'destination' values.
dr = 0
For sr = 1 To UBound(Data, 1)
cValue = Data(sr, scCol)
If StrComp(CStr(cValue), sCriteria, vbTextCompare) = 0 Then
dr = dr + 1
For c = 1 To cCount
Data(dr, c) = Data(sr, c)
Next c
End If
Next sr
If dr > 0 Then ' there have been matches
' Create a reference to the Destination Range.
Set drg = drrg.Resize(dr)
' Write only the 'destination' values (dr) from
' the Data Array to the Destination Range.
drg.Value = Data
' Create a reference to the next Destination First Row Range.
Set drrg = drrg.Offset(dr)
End If
End If
Next sws
' The 'Clear Range' is the range spanning
' from the last 'Destination First Row Range'
' (which was referenced, but was not written to)
' to the bottom-most row range of the worksheet.
Dim crg As Range
Set crg = drrg.Resize(dws.Rows.Count - drrg.Row + 1)
crg.ClearContents
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the names of the worksheets of a workbook ('wb'),
' that are not included in a list ('ExceptionsList'),
' in an array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ArrWorksheetNames( _
ByVal wb As Workbook, _
Optional ByVal ExceptionsList As String = "", _
Optional ByVal ListSeparator As String = ",", _
Optional ByVal FirstIndex As Long = 0) _
As Variant
If wb Is Nothing Then Exit Function
Dim wsCount As Long: wsCount = wb.Worksheets.Count
If wsCount = 0 Then Exit Function ' There could e.g. only be charts.
Dim IndexDiff As Long: IndexDiff = FirstIndex - 1
Dim LastIndex As Long: LastIndex = wsCount + IndexDiff
Dim Arr() As String: ReDim Arr(FirstIndex To LastIndex)
Dim n As Long: n = IndexDiff
Dim ws As Worksheet
If Len(ExceptionsList) = 0 Then
For Each ws In wb.Worksheets
n = n + 1
Arr(n) = ws.Name
Next ws
Else
Dim Exceptions() As String
Exceptions = Split(ExceptionsList, ListSeparator)
For Each ws In wb.Worksheets
If IsError(Application.Match(ws.Name, Exceptions, 0)) Then
n = n + 1
Arr(n) = ws.Name
End If
Next ws
End If
Select Case n
Case IndexDiff
Exit Function
Case Is < LastIndex
ReDim Preserve Arr(FirstIndex To n)
End Select
ArrWorksheetNames = Arr
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the range spanning from the first row
' of a given range ('rg') to the row containing the bottom-most
' non-empty cell of the given range's columns.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefColumns( _
ByVal rg As Range) _
As Range
If rg Is Nothing Then Exit Function
With rg.Rows(1)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , xlByRows, xlPrevious)
If lCell Is Nothing Then Exit Function ' empty range
Set RefColumns = .Resize(lCell.Row - .Row + 1)
End With
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Returns the values of a range in a 2D one-based array.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetRange( _
ByVal rg As Range) _
As Variant
If rg Is Nothing Then Exit Function
Dim rData As Variant
If rg.Rows.Count + rg.Columns.Count = 2 Then ' one cell only
ReDim rData(1 To 1, 1 To 1): rData(1, 1) = rg.Value
Else
rData = rg.Value
End If
GetRange = rData
End Function
' Irrelevant to the Question,
' but for a better understanding of `ArrWorksheetNames`.
Sub ArrWorksheetNamesTEST()
Const ExceptionsList As String = "Sheet1,Sheet2,Sheet3,Sheet4"
Const ListSeparator As String = ","
Const FirstIndex As Long = 4
Dim wb As Workbook: Set wb = ThisWorkbook
Dim wsNames As Variant
wsNames = ArrWorksheetNames(wb, ExceptionsList, ListSeparator, FirstIndex)
If IsEmpty(wsNames) Then
Debug.Print "No worksheets."
Else
Debug.Print "[" & LBound(wsNames) & "," & UBound(wsNames) & "]" _
& vbLf & Join(wsNames, vbLf)
End If
End Sub
You can use this to develop an array of values and then dump them into some collection sheet.
Sub grabAllSheets()
Const exclude_Sheet = "Result" ' name of sheet to drop data
Const tangoText = "On Stock"
Dim ws As Worksheet, aCell As Range
ReDim allvalues(1 To 6, 1 To 1)
Dim i As Long, c As Long
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> exclude_Sheet Then
For Each aCell In Intersect(ws.Range("E:E"), ws.UsedRange).Cells
If aCell.Value = tangoText Then
i = i + 1
ReDim Preserve allvalues(1 To 6, 1 To i)
For c = 1 To Range("F:F").Column
allvalues(c, i) = ws.Cells(aCell.Row, c).Value
Next c
End If
Next aCell
End If
Next ws
Dim theRow As Long
With Sheets(exclude_Sheet)
theRow = .Cells(.Rows.Count, 1).End(xlUp).Row
.Cells(IIf(theRow = 1, 1, theRow + 1), 1).Resize(i, 6).Value = _
Application.WorksheetFunction.Transpose(allvalues)
End With
End Sub

Macro/VBA: highlight rows based on 2 conditions

Objective is to highlight rows that meet two different conditions:
If column A is equal to the previous workday (taking into consideration of holidays mentioned in the Reference sheet)
If column B is not equal to "AA"
I have the following code, but am unable to get appropriate rows highlighted (no rows get highlighted due to condition #1 not being met):
Sub code()
Dim lrow As Long
lrow = Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To lrow
If Cells(i, "A").Value = "=WORKDAY(today(),-1,Reference!$A$2:$A$12)" And Cells(i, "B").Value <> "AA" Then Cells(i, 1).EntireRow.Interior.ColorIndex = 6
Next i
End Sub
You could try this:
Option Explicit
Sub code()
Dim i As Long, lrow As Long
Dim objRangeHolidays As Range
Set objRangeHolidays = Worksheets("Reference").Range("$A$2", "$A$12")
lrow = Cells(rows.Count, "A").End(xlUp).row
For i = 2 To lrow
If CDate(Cells(i, "A").Value) = CDate(Application.WorksheetFunction.WorkDay(Date, -1, objRangeHolidays)) And Cells(i, "B").Value <> "AA" Then
Cells(i, 1).EntireRow.Interior.ColorIndex = 6
End If
Next i
Set objRangeHolidays = Nothing
End Sub
Your original code does not work as "=WORKDAY(today(),-1,Reference!$A$2:$A$12)" is a literal string on VBA, not a function call.
We use CDate() function to make our cell values comparable with WorksheetFunction.Workday() function.
WorksheetFunction.Today() is the same as Date() in VBA.
objRangeHolidays holds holidays defined in Reference sheet.
This is my test result:
Highlight Entire Rows
Adjust the values in the constants section.
Option Explicit
Sub highlightPreviousWorkday()
' Source
Const sName As String = "Sheet1"
Const sFirst As String = "A2"
Const sCritCol As String = "B"
Const sCriteria As String = "AA"
Const sColorIndex As Long = 6
' Holiday
Const hName As String = "Reference"
Const hFirst As String = "A2"
' Workbook
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
wb.Activate ' `Evaluate` will fail if not active.
' Source
Dim srg As Range
With wb.Worksheets(sName).Range(sFirst)
Dim slCell As Range
Set slCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If slCell Is Nothing Then Exit Sub
Set srg = .Resize(slCell.Row - .Row + 1)
End With
' Holiday
Dim Holiday As String
With wb.Worksheets(hName).Range(hFirst)
Dim hlCell As Range
Set hlCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If Not hlCell Is Nothing Then
Holiday = ",'" & hName & "'!" _
& .Resize(hlCell.Row - .Row + 1).Address
End If
End With
' Evaluation
Dim evDate As Variant
evDate = Evaluate("WORKDAY(TODAY(),-1" & Holiday & ")")
' Combine
Dim drg As Range
If VarType(evDate) = vbDouble Then
Dim sCell As Range
Dim sValue As Variant
Dim sString As String
For Each sCell In srg.Cells
sValue = sCell.Value
If VarType(sValue) = vbDate Then
If CDbl(sValue) = evDate Then
sString = CStr(sCell.EntireRow.Columns(sCritCol).Value)
If sString <> sCriteria Then
Set drg = getCombinedRange(drg, sCell)
End If
End If
End If
Next sCell
End If
' Color
Application.ScreenUpdating = False
srg.EntireRow.Interior.ColorIndex = xlNone
If Not drg Is Nothing Then
drg.EntireRow.Interior.ColorIndex = sColorIndex
End If
Application.ScreenUpdating = True
End Sub
Function getCombinedRange( _
ByVal BuiltRange As Range, _
ByVal AddRange As Range) _
As Range
If BuiltRange Is Nothing Then
Set getCombinedRange = AddRange
Else
Set getCombinedRange = Union(BuiltRange, AddRange)
End If
End Function

Exporting Excel rows to text files

There are a few solutions I've seen but they don't specifically do what i'm trying to.
What I need to be able to do:
each row to create a new text file
each cell is a new line in this text file
the file name is the value in column 2
the file extension ".nfo"
the folder to be saved into is the value (an absolute path) in column 1
loop from row 3 to the first null row
I would post code but I have no idea where to start. Does anyone have any ideas?
Export Rows to Text Files
Copy the complete code into a standard module.
Before running exportRowsToTextFiles, adjust the values in its constants section and the worksheet (e.g. Set ws = ThisWorkbook.Worksheets("Sheet1")).
Uncomment the various Debug.Print lines to better understand how it works by monitoring the output in the Immediate window.
Option Explicit
Sub exportRowsToTextFiles()
Const First As String = "A3" ' First Data Cell Address
Const fCol As Long = 1 ' First Column
Const fpCol As Long = 1 ' File Path Column
Const fbnCol As Long = 2 ' File Base Name Column
Const fExt As String = ".nfo" ' File Extension
Const ccSep As String = vbLf ' Cell Contents Separator
Dim pSep As String: pSep = Application.PathSeparator
If ActiveSheet Is Nothing Then Exit Sub ' if run from an Add-in
If TypeName(ActiveSheet) <> "Worksheet" Then Exit Sub ' if e.g. chart
Dim ws As Worksheet: Set ws = ActiveSheet
Dim brrg As Range: Set brrg = refBottomRightRange(ws.Range(First))
'Debug.Print "Bottom Right Range: " & brrg.Address
Dim nerg As Range: Set nerg = refNonEmptyRange(brrg)
If nerg Is Nothing Then Exit Sub
'Debug.Print "Non-Empty Range: " & nerg.Address
Dim Data As Variant: Data = getRange(nerg)
'Debug.Print "Data Array:", "Rows=" & UBound(Data, 1), _
"Columns=" & UBound(Data, 2)
Dim rDat As Variant: ReDim rDat(0 To UBound(Data, 2) - fCol)
Dim FilePath As String
Dim r As Long, c As Long, n As Long
For r = 1 To UBound(Data, 1)
If Len(Data(r, fpCol)) > 0 Then
If Len(Data(r, fbnCol)) > 0 Then
FilePath = Data(r, fpCol) & pSep & Data(r, fbnCol) & fExt
'Debug.Print FilePath
n = -1
For c = fCol To UBound(Data, 2)
n = n + 1
rDat(n) = Data(r, c)
'Debug.Print r, c, n, rDat(n)
Next c
End If
End If
writeStringToFile FilePath, Join(rDat, ccSep)
Next r
End Sub
Function refBottomRightRange( _
ByVal FirstCell As Range) _
As Range
If FirstCell Is Nothing Then Exit Function
With FirstCell.Worksheet
Set refBottomRightRange _
= .Range(FirstCell(1), .Cells(.Rows.Count, .Columns.Count))
End With
End Function
Sub refBottomRightRangeTEST()
Dim FirstCell As Range: Set FirstCell = Range("C5")
Dim rg As Range: Set rg = refBottomRightRange(FirstCell)
If Not rg Is Nothing Then Debug.Print rg.Address
End Sub
Function refBottomRightResize( _
ByVal FirstCell As Range) _
As Range
If FirstCell Is Nothing Then Exit Function
With FirstCell
Set refBottomRightResize = .Resize(.Worksheet.Rows.Count - .Row + 1, _
.Worksheet.Columns.Count - .Column + 1)
End With
End Function
Sub refBottomRightResizeTEST()
Dim FirstCell As Range: Set FirstCell = Range("C5")
Dim rg As Range: Set rg = refBottomRightResize(FirstCell)
If Not rg Is Nothing Then Debug.Print rg.Address
End Sub
Function refNonEmptyRange( _
ByVal rg As Range) _
As Range
If rg Is Nothing Then Exit Function
Dim lCell As Range
Set lCell = rg.Find("*", , xlFormulas, , xlByRows, xlPrevious)
If lCell Is Nothing Then Exit Function
With rg.Resize(lCell.Row - rg.Row + 1)
Set refNonEmptyRange = .Resize(, _
.Find("*", , , , xlByColumns, xlPrevious).Column - .Column + 1)
End With
End Function
Sub refNonEmptyRangeTEST()
Dim irg As Range: Set irg = Range("C5:F10")
Dim rg As Range: Set rg = refNonEmptyRange(irg)
If Not rg Is Nothing Then Debug.Print rg.Address
End Sub
Function getRange( _
ByVal rg As Range) _
As Variant
If rg Is Nothing Then Exit Function
If rg.Rows.Count = 1 And rg.Columns.Count = 1 Then
Dim Data As Variant: ReDim Data(1 To 1, 1 To 1): Data(1, 1) = rg.Value
getRange = Data
Else
getRange = rg.Value
End If
End Function
Sub writeStringToFile( _
ByVal FilePath As String, _
ByVal FileText As String)
On Error GoTo clearError ' if file path is invalid (folder doesn't exist)
Dim FileNum As Long: FileNum = FreeFile
Open FilePath For Output As #FileNum
Print #FileNum, FileText
Close #FileNum
ProcExit:
Exit Sub
clearError:
Resume ProcExit
End Sub
As an example, I used the answer from the link I posted in comments.
I put a simple loop inside that loops the range, creating a row in the text file for each value.
Then I call with from another sub (not something you have to do) from within a loop that loop through all the rows, and for each row, passes the range of all the used column in said row. This specific code requires you to add a reference to Microsoft Scripting Runtime.
Option Explicit
Sub SaveNfo()
Dim ws As Worksheet, rng As Range, LastColumn As Range, rngRow As Variant
Set ws = Worksheets(1)
Set rng = ws.Range("A3:A" & ws.Cells(ws.Rows.Count, "A").End(xlUp).Row) 'start on row 3, include all rows with a filepath
For Each rngRow In rng
If Not rngRow = "" Then
SaveTextToFile rngRow & rngRow.Offset(, 1), _
ws.Range(rngRow.Offset(, 2), Cells(rngRow.Row, ws.Cells(rngRow.Row, ws.Columns.Count).End(xlToLeft).Column))
End If
Next
End Sub
Private Sub SaveTextToFile(filePath As String, rng As Range)
Dim cell As Variant
Dim fso As FileSystemObject
Set fso = New FileSystemObject
Dim fileStream As TextStream
' Here the actual file is created and opened for write access
Set fileStream = fso.CreateTextFile(filePath)
' Write something to the file
For Each cell In rng
fileStream.WriteLine cell
Next
' Close it, so it is not locked anymore
fileStream.Close
End Sub
If the file name column doesn't include .nfo you can add that in the code manually:
SaveTextToFile rngRow & rngRow.Offset(, 1), _ Becomes
SaveTextToFile rngRow & rngRow.Offset(, 1) & ".nfo", _
rngRow points to the "A" column, for the path.
rngRow.Offset(, 1) is then the "B" column, for the name.
rngRow.Offset(, 2) is then ofc "C", where we start looking for data to put in the file.
Or, if you want the really short version:
Sub SaveNfo()
Dim rngRow As Variant, cell As Variant, fso As Object, fileStream As Object
For Each rngRow In Range("A3:A" & Cells(Rows.Count, "A").End(xlUp).Row)
If Not rngRow = "" Then
Set fso = CreateObject("Scripting.FileSystemObject")
Set fileStream = fso.CreateTextFile(rngRow & rngRow.Offset(, 1))
For Each cell In Range(rngRow.Offset(, 2), Cells(rngRow.Row, Cells(rngRow.Row, Columns.Count).End(xlToLeft).Column))
fileStream.WriteLine cell
Next
fileStream.Close
End If
Next
End Sub

Resources