How to eliminate a row with a "file directory" for a parameter of hour - excel

I have rows in excel with a file structure for example.
Row 1 c:\User\Folder100\13-25\File100.log
Row 2 c:\User\Folder200\11-16\File200.log
Row 3 c:\User\Folder300\21-20\File300.log
Row 4 c:\User\Folder400\13-25\File400.log
Row 5 c:\User\Folder400\9-10\File400.log
Row 6 c:\User\Folder500\8-16\File500.log
Row 7 c:\User\Folder600\8-16\File600.log
Row 8 c:\User\Folder700\11-16\File700.log
Row 9 c:\User\Folder700\9-40\File700.log
With the first rows there aren't any problem because the file logs are different but with the rows (4 and 5) a There are the same log in two different folders "c:\User\Folder400\13-25\" and c:\User\Folder400\9-10\ I would like to keep just 13-25(eliminate Row 5) because has more recent time.
Also with the lines 8 and 9 I just want to keep row 8 (11-16)
Row 1 c:\User\Folder100\13-25\File100.log
Row 2 c:\User\Folder200\11-16\File200.log
Row 3 c:\User\Folder300\21-20\File300.log
Row 4 c:\User\Folder400\13-25\File400.log
Row 6 c:\User\Folder500\8-16\File500.log
Row 7 c:\User\Folder600\8-16\File600.log
Row 8 c:\User\Folder700\11-16\File700.log
(eliminated row 5 and 9)
Do you know any Idea how to made it in VBA¿?

The code below
uses a RegEx to extract the folder name and file number into two new columns (see pic below)
sorts the columns by column B and then by column C descending
delete the entire row where duplicate exists in column B using Excels Remove Duplicates functionality (the latest time comes first in column CV so it is preserved)
Removes the two working columns
Update: The code below assumes that both the 1st folder after "User" and the file name much match for it to be a duplicate - the initial guidelines are still ambigious. This code does solve the example shown in the question
Sub Sliced()
Dim lngRow As Long
Dim lngCalc As Long
Dim objReg As Object
Dim objDic As Object
Dim rng1 As Range
Dim X()
Dim Y()
Set rng1 = Range([a1], Cells(Rows.Count, "A").End(xlUp))
'See Patrick Matthews excellent article on using Regular Expressions with VBA
Set objReg = CreateObject("vbscript.regexp")
objReg.Pattern = "(.+\\){2}(.+\\)(\d+)\-\d+\\(.+)"
'Speed up the code by turning off screenupdating and setting calculation to manual
'Disable any code events that may occur when writing to cells
With Application
lngCalc = .Calculation
.ScreenUpdating = False
.Calculation = xlCalculationManual
.EnableEvents = False
End With
'Test each area in the user selected range
X = rng1.Value2
Y = X
For lngRow = 1 To UBound(X)
'replace the leading zeroes
X(lngRow, 1) = objReg.Replace(X(lngRow, 1), "$2$4")
Y(lngRow, 1) = objReg.Replace(Y(lngRow, 1), "$3")
Next
Columns("B:C").Insert
rng1.Offset(0, 1) = X
rng1.Offset(0, 2) = Y
With ActiveSheet.Sort
.SortFields.Clear
.SortFields.Add Key:=rng1.Offset(0, 1), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SortFields.Add Key:=rng1.Offset(0, 2), _
SortOn:=xlSortOnValues, Order:=xlDescending, DataOption:=xlSortNormal
.SetRange rng1.Cells(1).Offset(0, 1).Resize(rng1.Rows.Count, 2)
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
ActiveSheet.UsedRange.RemoveDuplicates Columns:=2, Header:=xlNo
Columns("B:C").Delete
'cleanup the Application settings
With Application
.ScreenUpdating = True
.Calculation = lngCalc
.EnableEvents = True
End With
Set objReg = Nothing
End Sub

This does not exactly serve the purpose, but serves to illustrate a way by which you could go about problems like this.
It takes into account the filename and the time string preceding it only. The folder can be added if necessary.
Main Module:
Option Explicit
Private dict As dictionary
'Prints the rows you need (time criterion applied)
Private Sub FindDuplicates()
Dim lastRow As Long, row As Long
Dim x As Variant, v As Variant
Dim fileName As String, timeString As String
Set dict = CreateObject("Scripting.Dictionary")
'Determine last row
lastRow = Worksheets(1).Cells(Rows.Count, 1).End(xlUp).row
'Iterate and store in dictionary
For row = 1 To lastRow
x = Split(Cells(row, 1), Application.PathSeparator)
fileName = x(UBound(x))
timeString = x(UBound(x) - 1)
AddDictEntry fileName, row, timeString
Next row
'Print results
For Each v In dict.Keys
Debug.Print "FileName: " & v & ", Recent Version: " & dict.Item(v)
Next
End Sub
To add/remove dictionary entries:
Private Sub AddDictEntry(fileName As String, rowNo As Long, timeString As String)
Dim timeParts As Variant, timeLong As Long
'converts time string to long, for comparison
timeParts = Split(timeString, "-")
timeLong = CInt(timeParts(0)) * 100 + CInt(timeParts(1))
'Adds entry to dictionary if time is more recent
If (dict.Exists(fileName)) Then
If CInt(dict.Item(fileName)) < timeLong Then
dict(fileName) = timeLong
End If
Else
dict.Add fileName, timeLong
End If
End Sub
Input:
c:\User\Folder100\13-25\File100.log
c:\User\Folder200\11-16\File200.log
c:\User\Folder300\21-20\File300.log
c:\User\Folder400\13-25\File400.log
c:\User\Folder400\9-10\File400.log
c:\User\Folder300\22-20\File300.log
Output:
FileName: File100.log, Recent Version: 1325
FileName: File200.log, Recent Version: 1116
FileName: File300.log, Recent Version: 2220
FileName: File400.log, Recent Version: 1325

Related

How to speed up vba code that delete rows when column Q has blank cells

I have a sheet of almost 100000 rows & column A to Q
I have a code that delete entire rows if column Q has blank cells.
I have tried this code on 4000 rows it is running in 3 minutes but when I take 100000 rows it just processing for hours.
I will be very great full if some help/guide me in speeding up this code.
The code is :
Sub DeleteBlank()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Dim lo As ListObject
set lo = sheets("BOM 6061").ListObjects(1)
Sheets("BOM 6061").Activate
lo.AutoFilter.ShowAllData
lo.range.AutoFilter Field:=17, Criteria1:=""
Application.DisplayAlerts = False
Application.Calculation = xlCalculationAutomatic
lo.DataBodyRange.SpecialCells(xlCellsTypeVisible).Delete
Application.DisplayAlerts = True
lo.AutoFilter.ShowAllData
End Sub
Remove Criteria Rows in an Excel Table Efficiently
In a nutshell, if you don't sort the criteria column, deleting the rows may take 'forever'.
The following will do just that, keeping the initial order of the remaining rows.
Option Explicit
Sub DeleteBlankRows()
Const wsName As String = "BOM 6061"
Const tblIndex As Variant = 1
Const CriteriaColumnNumber As Long = 17
Const Criteria As String = ""
' Reference the table.
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets(wsName)
Dim tbl As ListObject: Set tbl = ws.ListObjects(tblIndex)
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
' Remove any filters.
If tbl.ShowAutoFilter Then
If tbl.AutoFilter.FilterMode Then tbl.AutoFilter.ShowAllData
Else
tbl.ShowAutoFilter = True
End If
' Add a helper column and write an ascending integer sequence to it.
Dim lc As ListColumn: Set lc = tbl.ListColumns.Add
lc.DataBodyRange.Value = _
ws.Evaluate("ROW(1:" & lc.DataBodyRange.Rows.Count & ")")
' Sort the criteria column ascending.
With tbl.Sort
.SortFields.Clear
.SortFields.Add2 tbl.ListColumns(CriteriaColumnNumber).Range, _
Order:=xlAscending
.Header = xlYes
.Apply
End With
' AutoFilter.
tbl.Range.AutoFilter Field:=CriteriaColumnNumber, Criteria1:=Criteria
' Reference the filtered (visible) range.
Dim svrg As Range
On Error Resume Next
Set svrg = tbl.DataBodyRange.SpecialCells(xlCellTypeVisible)
On Error GoTo 0
' Remove the filter.
tbl.AutoFilter.ShowAllData
' Delete the referenced filtered (visible) range.
If Not svrg Is Nothing Then svrg.Delete
' Sort the helper column ascending.
With tbl.Sort
.SortFields.Clear
.SortFields.Add2 lc.Range, Order:=xlAscending
.Header = xlYes
.Apply
.SortFields.Clear
End With
' Delete the helper column.
lc.Delete
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
' Inform.
MsgBox "Blanks deleted.", vbInformation
End Sub
I would not use an Autofilter on large data sets as they can take quite a bit of time trying to enumerate the available options before actually filtering the data. The AutoFilter.ShowAllData takes just as much time. For my super simple test dataset, which consisted of 26 columns of 1000000 rows, it took 30+ seconds for each to process.
From what I can tell you are filtering the list to show only the blank items and then deleting the blank rows. Since the filtering is what is causing the delay we could just loop through each row looking at a specific column and if it is blank you can just delete it. Below is an example of how to do this.
**Edit: After testing I found this to be much slower than what you would want. Check out the next example below as it is super fast.
Option Explicit
Sub DeleteBlank()
Application.ScreenUpdating = False
Dim calcType As Integer
Dim rowCount, columnNumToCheck, currow, dataStartRow As Long
Dim WkSht As String
Dim lo As ListObject
WkSht = "BOM 6061" 'The name of the worksheet where the table is located.
columnNumToCheck = 17 'The column number to check for blank cells.
calcType = Application.Calculation
Application.Calculation = xlCalculationManual
Set lo = Sheets(WkSht).ListObjects(1)
rowCount = lo.ListRows.Count
dataStartRow = (lo.DataBodyRange.Row - 1)
For currow = rowCount To 1 Step -1
If Sheets(WkSht).Cells((currow + dataStartRow), columnNumToCheck).Value = "" Then
Call DeleteRows(WkSht, (currow + dataStartRow))
End If
Next currow
Application.Calculation = calcType
Application.ScreenUpdating = True
End Sub
Private Sub DeleteRows(sheetNameIn As String, startRow As Long, Optional optionalEndRow As Long)
If IsNull(optionalEndRow) Or optionalEndRow = 0 Then
optionalEndRow = startRow
End If
Worksheets(sheetNameIn).Range(startRow & ":" & optionalEndRow).Delete Shift:=xlUp
End Sub
If you are able to sort your data where the blank cells are all together you could use the below to perform a single delete function remove them all at once. This deleted 70000 rows in a few seconds.
Sub DeleteBlankWithSort()
'Application.ScreenUpdating = False
Dim columnNumToCheck, tableLastRow, lrow As Long
Dim calcType As Integer
Dim WkSht As String
Dim lo As ListObject
WkSht = "BOM 6061" 'The name of the worksheet where the table is located.
columnNumToCheck = 17 'The column number to check for blank cells.
calcType = Application.Calculation
Application.Calculation = xlCalculationManual
Set lo = Sheets(WkSht).ListObjects(1)
tableLastRow = FindLastRow(WkSht, (columnNumToCheck))
With lo.Sort
.SortFields.Clear
.SortFields.Add _
Key:=Range("Table1[[#All],[q]]"), _
SortOn:=xlSortOnValues, _
Order:=xlDescending, _
DataOption:=xlSortNormal
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
lrow = FindLastRow(WkSht, (columnNumToCheck), (tableLastRow))
Call DeleteRows(WkSht, (tableLastRow), (lrow + 1))
Application.Calculation = calcType
Application.ScreenUpdating = True
End Sub
Private Sub DeleteRows(sheetNameIn As String, startRow As Long, Optional optionalEndRow As Long)
If IsNull(optionalEndRow) Or optionalEndRow = 0 Then
optionalEndRow = startRow
End If
Worksheets(sheetNameIn).Range(startRow & ":" & optionalEndRow).Delete Shift:=xlUp
End Sub
Private Function FindLastRow(sheetNameIn As String, columnNum As Long, Optional optionalStartRow As Long) As Long
'finds the last row of the column passed in the sheetname passed in
If IsNull(optionalStartRow) Or optionalStartRow = 0 Then
optionalStartRow = 1048576
End If
FindLastRow = Worksheets(sheetNameIn).Range(Cells(optionalStartRow, columnNum).Address).End(xlUp).Row
End Function
I had an simple example of this from a while ago. Advanced filtering is the fastest way to filter in place or to filter and copy in excel/vba. In advanced filtering you usually have your filters listed out in columns/rows and can have as many as you need, use >"" for filtering out blanks on a column, should take no time at all. In my example it might be different as this was used alongside sheetchange to autofilter if anything was added to the filters.
Sub Advanced_Filtering_ModV2()
Dim rc As Long, crc As Long, trc As Long
Dim wb As Workbook, ws As Worksheet
Set wb = ActiveWorkbook: Set ws = wb.Worksheets("sheet1")
ws.Range("AA1").Value = ws.Range("Q1").Value: ws.Range("AA2").Value = ">"""""
On Error Resume Next
ws.ShowAllData: rc = ws.Range("A" & Rows.Count).End(xlUp).Row
ws.Range("A1:V" & rc).AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=ws.Range("AA1:AA2")
On Error GoTo 0
End Sub

Value of cell that depends on another dynamic cell within a range

Range A1:A5 is filled with dynamic data, though the data is limited to 5 values, the sequence could differ. It is also possible that only 4 or less is presented. The values are also unique.
Column B's value will depend on Column A.
Example:
A B
1 item2 USD18
2 item1 USD15
3 item3 USD4
4 item5 USD23
5 item4 USD11
How do I accomplish this on VBA?
Quite tricky. Please try this code after adjusting the items marked "change to suit".
Sub SetSequence()
' 156
Const DataClm As Long = 2 ' change to suit (2 = column B)
Const ItemClm As Long = 1 ' change to suit (1 = column A)
Dim Wb As Workbook
Dim Ws As Worksheet
Dim DataRng As Range ' sorted given data (column B in your example)
Dim Results As Variant ' results: sorted 1 to 5
Dim TmpClm As Long ' a column temporarily used by this macro
Dim Tmp As String ' working string
Dim R As Long ' oop counter: rows
Dim i As Long ' index of Results
Results = Array("Item1", "Item2", "Item3", _
"Item4", "Item5") ' modify list items as required (sorted!)
Set Wb = ThisWorkbook ' modify as needed
Set Ws = Wb.Worksheets("Sheet1") ' change to suit
With Ws
With .UsedRange
TmpClm = .Column + .Columns.Count
End With
' create a copy of your data (without header) in an unused column
.Range(.Cells(2, DataClm), .Cells(.Rows.Count, DataClm).End(xlUp)) _
.Copy .Cells(1, TmpClm)
Set DataRng = .Range(.Cells(1, TmpClm), .Cells(.Rows.Count, TmpClm).End(xlUp))
With .Sort.SortFields
.Clear
.Add2 Key:=Ws.Cells(1, TmpClm), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
End With
With .Sort
.SetRange DataRng
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
' blanks are removed, if any
Set DataRng = .Range(.Cells(1, TmpClm), .Cells(.Rows.Count, TmpClm).End(xlUp))
' start in row 2 of DataClm and look at next 5 cells
For R = 2 To 6
' skip over blanks
Tmp = .Cells(R, DataClm).Value
If Len(Trim(Tmp)) Then
i = WorksheetFunction.Match(Tmp, DataRng, 0)
.Cells(R, ItemClm).Value = Results(i - 1)
End If
Next R
.Columns(TmpClm).ClearContents
End With
End Sub
The code creates a sorted copy of the items you have in column B and draws the output in column A from the similarly sorted list of results. Blanks are ignored. But if there is one blank in the input list (column B) there will be only 4 items in the sorted input list and therefore none of the items can be assigned "Item 5" in column A.
I've replaced my answer with the one below from your edit which completely changed things.
See if this is what you're looking for:
Dim ValueArr As Variant
ValueArr = Array("USD15", "USD18", "USD4", "USD11", "USD23")
For i = 1 To 5
If Range("A" & i) <> "" Then
Range("B" & i) = ValueArr(Right(Range("A" & i), 1) - 1)
End If
Next i
This code is based off of using the number on the end of item to know which value to put in place. If the row is blank it will skip over it.

performance issue - Rearranging columns based on column header

I have an Excel Workbook with hundreds of columns to be rearranged. Having tried different approaches to rearrange those columns I have developed my own solution, because it's faster than what I have found here and elsewhere:
How to rearrange the excel columns by the columns header name
https://code.adonline.id.au/rearrange-columns-excel-vba/
My code:
What I basically do is searching the header row for a certain string and copy that column to a temp/helper sheet, when done I search for the next term and so on until all categories are searched. Afterwards I copy the chunk back to the main sheet in the correct order.
edit: it is of vital importance to keep the formatting of each column, so putting everything in an array does not work, because the formatting information will be gone.
Sub cutColumnsToTempAndMoveBackSorted()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
Call declareVariables
iCountCompanies = lngLastCol - iColStart + 1
' Timer
Dim StartTime As Double
Dim SecondsElapsed As Double
' Remember time when macro starts
StartTime = Timer
iStartColTemp = 0
wsTempCompanies.UsedRange.Delete
' First copy all columns with "ABC"
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "ABC" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Copy
wsTempCompanies.Columns(iStartColTemp).Insert
End If
Next i
' Then copy all columns with "DDD"
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "DDD" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Copy
wsTempCompanies.Columns(iStartColTemp).Insert
End If
Next i
' Then copy all columns with "CCC"
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "CCC" Or ws.Cells(iRowCategory, i) = "" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Copy
wsTempCompanies.Columns(iStartColTemp).Insert
End If
Next i
' Then copy all columns with "EEE"
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "EEE" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Copy
wsTempCompanies.Columns(iStartColTemp).Insert
End If
Next i
Dim iLastColTemp As Integer: iLastColTemp = iStartColTemp
iStartColTemp = 1
ws.Range(Col_Letter(iColStart) & ":" & Col_Letter(lngLastCol)).Delete 'Col_Letter function gives back the column ist characters instead of column ID
' Move back to Main Sheet
wsTempCompanies.Range(Col_Letter(iStartColTemp) & ":" & Col_Letter(iLastColTemp)).Copy
ws.Range(Col_Letter(iColStart + 1) & ":" & Col_Letter(lngLastCol + 1)).Insert
ws.Columns(iColStart).Delete
'Determine how many seconds code took to run
SecondsElapsed = Round(Timer - StartTime, 2)
'Notify user in seconds
Debug.Print "Time: " & SecondsElapsed & " Sekunden."
ende:
Application.ScreenUpdating = True
Call activateApplication ' All kinds of screenupdates, such as enableevents, calculations, ...
End Sub
I am still not happy with my solution as it takes just too much time when there are more than 50 columns. Sometimes I have over 300.
Any suggestion to boost the performance?
The below might be of some help, if it is not too much effort.
Sample Dataset in one sheet (let's call this the Main sheet) with,
(Row 2) Sample Header row (includes the lookup keywords - ABC, DDD, CCC, EEE)
(Row 1) A Temp Row (formulated to show Header Order numbers)
References sheet which lists the lookup keywords in required left-to-right sort order
Back in the Main sheet, we'd like to generate the sequence numbers in Row 1.
As highlighted in the 1st image, it can be done with the below MATCH formula in the cell A1,
=MATCH(TRUE,ISNUMBER(SEARCH(References!$A$2:$A$5,A2)),0)
This is required as an array formula and hence should be executed by hitting Ctrl+Shift+Enter
Now copy the cell A1 across columns (in Row 1) through the last column
Row 1 will now contain sequence numbers 1..n, where n is the numbers of rows found in the References sheet. It may also contain #N/A error value returned by the MATCH formula if no match is found from the 'References' sheet
Now, apply sort (Sort Option: Left to Right) and Sort By Row 1.
The columns should now be sorted as per requirement and with formatting intact.
Result (Sorted)
Please note that a column header not matching any keywords has been moved to the end.
Once you find everything in place, now you can go ahead and delete the (Row 1) temp row in the Main sheet
P.S: While I haven't computed the performance of this approach on a large dataset, I'm sure it will be fairly quick.
Please test the next code, please. Most of the credit must go to #Karthick Ganesan for his idea. The code only puts his idea in VBA:
Sub reorderColumnsByRanking()
Dim sh As Worksheet, arrOrd As Variant, lastCol As Long, i As Long
Dim El As Variant, boolFound As Boolean, isF As Long
Set sh = ActiveSheet 'use here your necessary sheet
lastCol = sh.Cells(1, Columns.Count).End(xlToLeft).Column
arrOrd = Split("ABC|1,DDD|2,CCC|3,EEE|4", ",") 'load criteria and their rank
'insert a helping row____________________
sh.Range("A1").EntireRow.Insert xlAbove
'________________________________________
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
'Rank the columns_______________________________________________________________
For i = 1 To lastCol
For Each El In arrOrd
If IsFound(sh.Cells(2, i), CStr(Split(El, "|")(0))) Then
sh.Cells(1, i).Value = Split(El, "|")(1): boolFound = True: Exit For
End If
Next
If Not boolFound Then sh.Cells(1, i).Value = 16000
boolFound = False
Next i
'_______________________________________________________________________________
'Sort LeftToRight_____________________________________________________________
sh.Sort.SortFields.Add2 key:=sh.Range(sh.Cells(1, 1), sh.Cells(1, lastCol)) _
, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With sh.Sort
.SetRange sh.Range(sh.Cells(1, 1), sh.Cells(1, lastCol)).EntireColumn
.Header = xlYes
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With
'____________________________________________________________________________
'Delete helping first row____
sh.Rows(1).Delete xlDown
'____________________________
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationManual
End Sub
Private Function IsFound(rng As Range, strS As String) As Boolean
Dim fC As Range
Set fC = rng.Find(strS)
If Not fC Is Nothing Then
IsFound = True
Else
IsFound = False
End If
End Function
Here's my take on the solution. It's pretty similar to the one in your first link by #BruceWayne except this will go straight to the correct column rather than checking each one.
At the moment the code looks for partial matches - so "ABCDEF" would be found for both "ABC" and "DEF". Change xlPart to xlWhole in the FIND command to have it match against exact headings.
Sub Test()
Dim CorrectOrder() As Variant
Dim OrderItem As Variant
Dim FoundItem As Range
Dim FirstAddress As String
Dim NewOrder As Collection
Dim LastColumn As Range
Dim NewPosition As Long
Dim tmpsht As Worksheet
CorrectOrder = Array("ABC", "DEF", "GHI", "JKL")
With ThisWorkbook.Worksheets("Sheet1")
Set LastColumn = .Cells(2, .Columns.Count).End(xlToLeft) 'Return a reference to last column on row 2.
Set NewOrder = New Collection
With .Range(.Cells(2, 1), LastColumn) 'Refer to the range A2:LastColumn.
'Search for each occurrence of each value and add the column number to a collection in the order found.
For Each OrderItem In CorrectOrder
Set FoundItem = .Find(What:=OrderItem, After:=.Cells(1, 1), LookIn:=xlValues, LookAt:=xlPart)
If Not FoundItem Is Nothing Then
FirstAddress = FoundItem.Address
Do
NewOrder.Add FoundItem.Column
Set FoundItem = .FindNext(FoundItem)
Loop While FoundItem.Address <> FirstAddress
End If
Next OrderItem
End With
End With
'Providing some columns have been found then move them in order to a temporary sheet.
If NewOrder.Count > 1 Then
NewPosition = 2
Set tmpsht = ThisWorkbook.Worksheets.Add
For Each OrderItem In NewOrder
ThisWorkbook.Worksheets("Sheet1").Columns(OrderItem).Cut _
tmpsht.Columns(NewPosition)
NewPosition = NewPosition + 1
Next OrderItem
'Copy the reordered columns back to the original sheet.
tmpsht.Columns(2).Resize(, NewOrder.Count).Cut _
ThisWorkbook.Worksheets("Sheet1").Columns(2)
'Delete the temp sheet.
Application.DisplayAlerts = False
tmpsht.Delete
Application.DisplayAlerts = True
End If
End Sub
You can use Cut which is significantly faster (on PC it is around 20-30 times faster than Copy/Insert approach. Cut also preserves formatting.
Here, is an example how it can be implemented into your code:
For i = iColStart To lngLastCol
If ws.Cells(iRowCategory, i) = "EEE" Then
iStartColTemp = iStartColTemp + 1
ws.Columns(i).Cut wsTempCompanies.Columns(iStartColTemp)
End If
Next i
If for some reason, you are not allowed to cut elements from ws, then it is probably good idea to create temporary copy of that working to work on.

Find a row based on a cell value, copy only part of the row in to a new sheet and the rest of the row into the next row of the new sheet

I have a file that has 5 transaction codes ("IPL","ISL","IMO","IIC","CAPO").
I need my macro to find the first 4 transaction codes in column dc of worksheets("sort area"), if it locates it, then take the contents of DE-FN and copy values to a new sheet.
for the last transaction code, i need the macro to find the transaction code in dc, and if it's there take the contents of the row but only the subsequent 8 columns (DE-DL) copy paste values in to worksheet("flat file") and then take the next 8 columns (DM-DS) from the original sheet ("sort area") and copy values in worksheet("flat file") but the following row
for the first part of the macro, i have it separated in to two parts, where i am copying the contents of the entire row, pasting values in to a new sheet, and then sorting the contents and deleting unneeded columns in the new sheet.
I'm struggling because my code is skipping some rows that contain IPL and i don't know why.
i have no idea how to do the last part, CAPO.
Part A (this takes the IPL transaction code and moves it to the new sheet ("flat file"):
Sub IPLFlat()
Dim xRg As Range
Dim xCell As Range
Dim xRRg1 As Range
Dim xRRg2 As Range
Dim I As Long
Dim J As Long
Dim K As Long
Dim xC1 As Long
Dim xFNum As Long
Dim xDShName As String
Dim xRShName As String
xDShName = "sort area"
xRShName = "flat file"
I = Worksheets(xDShName).UsedRange.Rows.Count
J = Worksheets(xRShName).UsedRange.Rows.Count
xC1 = Worksheets(xDShName).UsedRange.Columns.Count
If J = 1 Then
If Application.WorksheetFunction.CountA(Worksheets(xRShName).UsedRange) = 0 Then J = 0
End If
Set xRg = Worksheets(xDShName).Range("DC2:DC" & I)
On Error Resume Next
Application.ScreenUpdating = False
For K = 1 To xRg.Count
If CStr(xRg(K).Value) = "IPL" Then
Set xRRg1 = xRg(K).EntireRow
Set xRRg2 = Worksheets(xRShName).Range("A" & J + 1).EntireRow
xRRg2.Value = xRRg1.Value
If CStr(xRg(K).Value) = "IPL" Then
K = K + 1
End If
J = J + 1
End If
Next
Application.ScreenUpdating = True
'Sort Flatfile tab
Worksheets("flat file").Activate
With ActiveSheet.Sort
.SortFields.Add Key:=Range("DF1"), Order:=xlAscending
.SetRange Range("A1", Range("FG" & Rows.Count).End(xlUp))
.Header = xlNo
.Apply
End With
Columns("A:DD").EntireColumn.Delete
Here is a solution:
Sub stackOverflow()
Dim sortSheet As Worksheet, flatSheet As Worksheet, newSheet As Worksheet
Set sortSheet = ThisWorkbook.Sheets("sort area")
Set flatSheet = ThisWorkbook.Sheets("flat file")
Dim rCount As Long, fCount As Long
rCount = sortSheet.Cells(sortSheet.Rows.Count, 1).End(xlUp).Row
For i = 2 To rCount
Select Case sortSheet.Cells(i, 107).Value
Case "IPL", "ISL", "IMO", "IIC"
Set newSheet = ThisWorkbook.Sheets.Add
sortSheet.Range(sortSheet.Cells(i, 109), sortSheet.Cells(i, 170)).Copy 'de->109 fn->170
newSheet.Paste
Case "CAPO"
With flatSheet
fCount = .Cells(.Rows.Count, 1).End(xlUp).Row
sortSheet.Range(sortSheet.Cells(i, 109), sortSheet.Cells(i, 116)).Copy 'de->109 dl->116
.Range(.Cells((fCount + 1), 1), .Cells((fCount + 1), 8).PasteSpecial Paste:xlPasteValues
sortSheet.Range(sortSheet.Cells(i, 117), sortSheet.Cells(i, 123)).Copy 'dm->117 ds->123
.Range(.Cells((fCount + 2), 1), .Cells((fCount + 2), 6).PasteSpecial Paste:xlPasteValues
End With
End Select
Next i
End Sub
I hope I understood your problem correctly and that this helps,
Cheers

VBA: Split sheet on certain rule

I need help with VBA which will split current sheet Test1 depending values from A rows.
Test1 sheet is in format:
Now i need to split sheet Test1 into two (or more) sheets which will contains all rows which begins with 1.1 and 1.4 (this values will be same rule, but different numbers).
So after run VBA code, it will be created sheet Test1-1 (green region) containing all data which starts with 1.1:
1.1
1.1.1
1.1.2
1.1.3
And second sheet Test1-2 (red region) which starts with 1.4:
1.4
1.4.1
1.4.2
After creation origin Test1 sheet can be removed.
Can you please give me help or guide i don't have any clue/idea to achieve this.
With the below code the output will be:
Two Sheets:
Test1-1
Test1-4
If you want to get this output:
Test1-1
Test1-2
You should:
Sort data based on the first column
Create another variable with initial value 1 and every time that Sheetname change value instead of use Sheetname variable , use the new variable.
Guidlines for:
Sorting:
Option Explicit
Sub Sort()
Dim LR As Long
With ThisWorkbook.Worksheets("Test1")
LR = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
ThisWorkbook.Worksheets("Test1").Sort.SortFields.Clear
ThisWorkbook.Worksheets("Test1").Sort.SortFields.Add2 Key:=Range("A1"), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Test1").Sort
.SetRange Range("A2:D" & LR)
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub
New Variable
From: ActiveWorkbook.Worksheets("Test1-" & SheetName)
To: ActiveWorkbook.Worksheets("Test1-" & NewVariable)
Try:
Option Explicit
Sub test()
Dim LR As Long
Dim LRN As Long
Dim i As Long
Dim SheetName As String
Dim wsTest As Worksheet
Dim wsNew As Worksheet
With ThisWorkbook.Worksheets("Test1")
LR = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
For i = LR To 1 Step -1
With ThisWorkbook.Worksheets("Test1")
SheetName = Mid(.Range("A" & i), InStr(1, .Range("A" & i).Value, ".") + 1, 1)
End With
Set wsTest = Nothing
On Error Resume Next
Set wsTest = ActiveWorkbook.Worksheets("Test1-" & SheetName)
On Error GoTo 0
If wsTest Is Nothing Then
Worksheets.Add.Name = "Test1-" & SheetName
End If
With ActiveWorkbook.Worksheets("Test1-" & SheetName)
LRN = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
ThisWorkbook.Worksheets("Test1").Range("A" & i & ":D" & i).Cut ActiveWorkbook.Worksheets("Test1-" & SheetName).Range("A" & LRN + 1)
Next i
Application.DisplayAlerts = False
ThisWorkbook.Worksheets("Test1").Delete
Application.DisplayAlerts = True
End Sub

Resources