Exporting Excel rows to text files - excel

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

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

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

VBA Copy multiple sheets based on column A filter in each sheet and create new workbook

I have 3 consolidated sheet in a workbook which I need to segregate into 3 sheets in new workbook based on unique values in column A of each sheet:
In the "A" workbook, all 3 sheets each sheet should have only its information and needs to loop for all names.
Below the code that only moves data from workbook to workbook, but is not much helpful.
Backup Worksheets by Name
This is a somewhat simplified example that assumes that each table starts in A1, that the worksheets are not filtered, that the names are in column 1 ("A"), that the first worksheet (Sales) contains all the unique values (names),...
For each unique value (name) it copies only the worksheets from the list to a new workbook. Then it loops through all the worksheets in the new workbook and deletes the rows that do not contain the value leaving the headers intact. Finally, it saves the new workbook.
Option Explicit
Sub BackupByName()
Const wsNamesList As String = "Sales,Marketing,Operations"
Const First As String = "A1" ' You cannot change this...1
Dim wsNames() As String: wsNames = Split(wsNamesList, ",")
Dim swb As Workbook: Set swb = ThisWorkbook ' workbook containing this code
Dim dFolderPath As String: dFolderPath = swb.Path & "\"
' Assuming that "Sales" contains all names.
Dim ws As Worksheet: Set ws = swb.Worksheets(wsNames(0))
Dim rg As Range: Set rg = RefColumn(ws.Range(First).Offset(1))
If rg Is Nothing Then Exit Sub ' range reference cannot be created
Dim Data As Variant: Data = GetRange(rg)
Dim uData As Variant: uData = ArrUniqueData(Data)
If IsEmpty(uData) Then Exit Sub ' no unique values
Dim uUpper As Long: uUpper = UBound(uData)
Application.ScreenUpdating = False
Dim dwb As Workbook
Dim dws As Worksheet
Dim drg As Range
Dim n As Long
Dim nName As String
Dim dName As String
For n = 0 To uUpper
swb.Worksheets(wsNames).Copy
Set dwb = ActiveWorkbook
nName = uData(n)
For Each dws In dwb.Worksheets
'1... because of these simplifications.
Set rg = dws.Range(First).CurrentRegion.Columns(1)
rg.Columns.AutoFilter 1, "<>" & Name
Set drg = Nothing
On Error Resume Next
Set drg = rg.Resize(rg.Rows.Count - 1).Offset(1) _
.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
If Not drg Is Nothing Then
drg.EntireRow.Delete
End If
dws.AutoFilterMode = False
Next dws
dName = dFolderPath & nName & ".xlsx"
Application.DisplayAlerts = False
dwb.SaveAs Filename:=dName, FileFormat:=xlOpenXMLWorkbook ' 51
Application.DisplayAlerts = True
dwb.Close SaveChanges:=False
Next n
Application.ScreenUpdating = True
End Sub
Function RefColumn( _
ByVal FirstCellRange As Range) _
As Range
If FirstCellRange Is Nothing Then Exit Function
With FirstCellRange.Cells(1)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Function
Set RefColumn = .Resize(lCell.Row - .Row + 1)
End With
End Function
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
ReDim rData(1 To 1, 1 To 1): rData(1, 1) = rg.Value
Else
rData = rg.Value
End If
GetRange = rData
End Function
Function ArrUniqueData( _
Data As Variant, _
Optional ByVal CompareMethod As VbCompareMethod = vbTextCompare) _
As Variant
If IsEmpty(Data) Then Exit Function
Dim cLower As Long: cLower = LBound(Data, 2)
Dim cUpper As Long: cUpper = UBound(Data, 2)
Dim Key As Variant
Dim r As Long
Dim c As Long
With CreateObject("Scripting.Dictionary")
.CompareMode = CompareMethod
For r = LBound(Data, 1) To UBound(Data, 1)
For c = cLower To cUpper
Key = Data(r, c)
If Not IsError(Key) Then
If Len(Key) > 0 Then
.Item(Key) = Empty
End If
End If
Next c
Next r
If .Count = 0 Then Exit Function
ArrUniqueData = .Keys
End With
End Function

Excel VBA search within range from previous column

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

How to replace values using Arrays & Ranges?

I can replace values by mentioning them one by one.
I want to replace (oldarray) with (newarray) where both of them are derived from ranges.
i.e. oldarray = ("a2:a5") and newarray = ("b2:b5") instead of writing them one by one.
and also I need to replace each old value with adjacent cell value
i.e. a2 replaced by b2, and a3 replaced by b3.
is that possible?
Sub ReplaceValues()
Dim NewValues() As String
Dim NewValues() As String
OldValues = Split("BMV,MERCE", ",")
NewValues = Split("Jack,Sally", ",")
For i = 0 To UBound(OldValues)
With sheets("destination").Columns("Z:Z")
.Replace What:=OldValues(i), Replacement:=NewValues(i), LookAt:=xlWhole, SearchOrder:=xlByRows, MatchCase:=True
End With
Next
End Sub
Something like this should work:
Sub ReplaceValues()
Dim OldValues, NewValues, ws As Worksheet
Set ws = Thisworkbook.worksheets("Config") 'or whichever sheet...
OldValues = ws.Range("A2:A5").Value 'this gives a 2d array
NewValues = ws.Range("B2:B5").Value 'this too
For i = 1 To UBound(OldValues, 1)
With sheets("destination").Columns("Z:Z")
.Replace What:=OldValues(i, 1), Replacement:=NewValues(i, 1), _
LookAt:=xlWhole, SearchOrder:=xlByRows, MatchCase:=True
End With
Next
End Sub
Search and Replace (Application.Match)
If a value in the Destination column (Z) is found in the Search column (A), it will be replaced with the value in the same row of the Replace column (B).
This search (Application.Match) is not case-sensitive i.e. A = a.
Adjust the values in the constants section.
Only run replaceValues; the rest is being called by it.
The Code
Option Explicit
Sub replaceValues()
' Define constants.
' Source
Const srcName As String = "Sheet1"
Const sFirst As String = "A2"
Const rFirst As String = "B2"
' Destination
Const dstName As String = "Sheet2"
Const dFirst As String = "Z2"
' Workbook
Dim wb As Workbook
Set wb = ThisWorkbook
' Write from worksheets to arrays.
Dim ws As Worksheet ' Each Worksheet
Dim rng As Range ' Each Column Range
' Source
Dim sData As Variant ' Search Data Array
Dim rData As Variant ' Replace Data Array
Dim ColOffset As Long ' Search and Replace Column Offset
Set ws = wb.Worksheets(srcName)
Set rng = getColumnRange(getCellRange(ws, sFirst))
ColOffset = getCellRange(ws, rFirst).Column - rng.Column
sData = getColumn(rng)
rData = getColumn(rng.Offset(, ColOffset))
' Destination
Dim dData As Variant ' Destination Array
Set ws = wb.Worksheets(dstName)
Set rng = getColumnRange(getCellRange(ws, dFirst))
dData = getColumn(rng)
' Search and replace (in arrays).
Dim mData As Variant ' Match Data Array
mData = Application.Match(dData, sData, 0)
Dim cMatch As Variant
Dim i As Long
For i = 1 To UBound(dData, 1) ' or 'UBound(mData, 1)'
cMatch = mData(i, 1)
If IsNumeric(cMatch) Then
dData(i, 1) = rData(cMatch, 1)
End If
Next i
' Write from Destination Array to Destination Range.
rng.Value = dData
End Sub
Function getCellRange( _
ws As Worksheet, _
ByVal CellAddress As String) _
As Range
On Error Resume Next
Set getCellRange = ws.Range(CellAddress)
On Error GoTo 0
End Function
Function getColumnRange( _
FirstCell As Range) _
As Range
If Not FirstCell Is Nothing Then
With FirstCell
Dim rng As Range
Set rng = .Resize(.Worksheet.Rows.Count - .Row + 1)
Set rng = rng.Find("*", , xlFormulas, , , xlPrevious)
If Not rng Is Nothing Then
Set getColumnRange = .Resize(rng.Row - .Row + 1)
End If
End With
End If
End Function
Function getColumn( _
rng As Range) _
As Variant
If Not rng Is Nothing Then
If InStr(rng.Address, ":") > 0 Then
getColumn = rng.Value
Else
Dim Data As Variant
ReDim Data(1 To 1, 1 To 1)
Data(1, 1) = rng.Value
getColumn = Data
End If
End If
End Function
The code below reads the data from A2:A6 into an array SrcArr and the data from B2:B6 into another array I called ModArr. Then it creates a third array (OutArr) of the same size as the source and writes data from SrcArr into it modified according to the data in ModArr. Finally, the OutArr is written to column D. This is the setup and the result.
And here is the code that did it.
Sub ReplaceArray()
' 138
Dim SrcArr As Variant ' Source
Dim ModArr As Variant ' Modifier
Dim OutArr As Variant ' Output
Dim R As Long ' loop counter: rows
With ActiveSheet
SrcArr = .Range("A2:A6").Value
ModArr = .Range("B2:B6").Value
ReDim OutArr(1 To UBound(SrcArr), 1 To UBound(SrcArr, 2))
For R = 1 To UBound(SrcArr)
If ModArr(R, 1) = True Then
OutArr(R, 1) = SrcArr(R, 1) * 12
Else
If IsEmpty(ModArr(R, 1)) Then
OutArr(R, 1) = "No data"
Else
OutArr(R, 1) = 0
End If
End If
Next R
.Cells(2, "D").Resize(UBound(SrcArr), UBound(SrcArr, 2)).Value = OutArr
End With
End Sub

Resources