Sort Column Based on Another Column Order of Values - vba

I have two columns in spreadsheet1:
Col1 Col2
1 PDC
2 SR3
3 PDC
4 VBM
5 VBM
6 GAL
7 VBM
8 GAL
9 PDC
I have 1 column in spreadsheet2:
Col1
PDC
SR3
VBM
GAL
How can I sort Col1 and Col2 from spreadsheet1 based on the order in spreadsheet2 Col1?

Try this code:
Sub test()
Dim wb As Workbook
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim lastrow As Long
Dim rng As Range
'if workbook2 is already opened
Set wb = Workbooks("Book2") ' change Book2 to suit
'if workbook2 is not opened
'Set wb = Workbooks.Open("C:\Book2.xlsx")
'change sheet1 to suit
Set ws1 = ThisWorkbook.Worksheets("Sheet1")
'change sheet2 to suit
Set ws2 = wb.Worksheets("Sheet2")
With ws1
'change column B to column with your values "PDC", "SR3" and so on
lastrow = .Cells(.Rows.Count, "B").End(xlUp).Row
'change to address of range you wnat to sort
Set rng = .Range("A1:B" & lastrow)
With rng.Offset(, rng.Columns.Count).Resize(, 1)
.EntireColumn.Insert
.Offset(, -1).FormulaR1C1 = "=MATCH(RC[-1],'[" & wb.Name & "]" & ws2.Name & "'!C1:C1,0)"
.Offset(, -1).Value = .Offset(, -1).Value
End With
With rng.Resize(, rng.Columns.Count + 1)
.Sort Key1:=.Cells(1, .Columns.Count), Order1:=xlAscending, Header:=xlGuess
.Cells(1, .Columns.Count).EntireColumn.Delete
End With
End With
wb.Close
End Sub
Explanaiton:
There're two workbooks. Code should be places in first workbook (where you want to sort range)
The main idea is to add temporary column with formula like =MATCH(B1,[Book2]Sheet2!A:A,0) to get row numbers of values from column B (workbook1) in column A (workbook2).
Sort range based on this numbers.
Delete temporary column
Notes:
Next line assumed that column with "PDC", "SR3" (in workbook1) is last in selected range (Set rng = .Range("A1:B" & lastrow)):
.Offset(, -1).FormulaR1C1 = "=MATCH(RC[-1],'[" & wb.Name & "]" & ws2.Name & "'!C1:C1,0)"
if it's not true, change RC[-1] to, say RC[-2] if this column is last but one and so on.
C1:C1 part of formula means that in workbook2 column with "PDC", "SR3" is column A (column №1). If it's not true, change it to, say, C5:C5, which means that column is E (column №5).

I found this code which seems a bit simpler and works great.
Sub NewSortTest()
Dim keyRange As Variant
Dim sortNum As Long
keyRange = ActiveWorkbook.Worksheets("Sheet2").Cells.Range("A1:A10").Value
Application.AddCustomList ListArray:=keyRange
sortNum = Application.CustomListCount
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add key:=Range("A1:A20"), SortOn:=xlSortOnValues, Order:=xlAscending, CustomOrder:=sortNum, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range("A1:B20")
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
End Sub

Related

sorting for worksheets in 1 workbook

I have searched the net for a macro that can help me to do sorting for worksheets in a workbook and modified it a little ( adding the exclude worksheets)
Sub SortDataWorksheets()
Dim wsh As Worksheet
For Each wsh In ThisWorkbook.Sheets
If wsh.Name <> "Dashboard" And wsh.Name <> "rawdata" And wsh.Name <> "template" And wsh.Name <> "macros instructions" And wsh.Name <> "Sheet1" _
And wsh.Name <> "Sheet2" And wsh.Name <> "inputlist" And wsh.Name <> "ProductList" And wsh.Name <> "NA" Then
'sort columns A to AL based on data in column B
wsh.Columns("A:AL").Sort key1:=Range("B3"), order1:=xlAscending, Header:=xlYes
End If
Next
End Sub
However, this doesnt work as excel will throw the
Run Time error '1004' :
The sort reference is not valid. Make sure that it's within the data you want to sort...
My data starts from Row 3 onwards, 1st 2 rows are headers. How do i exclude the first 2 rows for sorting?
Change from:
wsh.Columns("A:AL").Sort key1:=Range("B3"), order1:=xlAscending, Header:=xlYes
To:
wsh.Columns("A:AL").Sort key1:=wsh.Range("B3"), order1:=xlAscending, Header:=xlYes
Because if you do not refer to the parent worksheet, VBA takes as parent worksheet the ActiveSheet or the sheet in which the code is. Both would return an error in your case.
This works for me:
Sub SortDataWorksheets()
Dim wsh As Worksheet
Dim LastRow As Long
For Each wsh In ThisWorkbook.Sheets
With wsh
If .Name <> "Dashboard" And .Name <> "rawdata" And .Name <> "template" And _
.Name <> "macros instructions" And .Name <> "Sheet1" _
And .Name <> "Sheet2" And .Name <> "inputlist" And _
.Name <> "ProductList" And .Name <> "NA" Then
LastRow = .Cells.Find(What:="*", SearchOrder:=xlRows, SearchDirection:=xlPrevious, LookIn:=xlValues).Row
'sort columns A to AL based on data in column B
.Range("A2:AL" & LastRow).Sort key1:=.Range("B3"), order1:=xlAscending, Header:=xlYes
End If
End With
Next
End Sub
Notice the use of a specific range instead of columns.
I find it easier to read a Select Case rather than multiple IF..AND..THEN when ignoring sheets.
The code below will adjust to how many rows contain data in column B.
I'm still not sure which is the preferred method of sorting - single line, or what the macro recorder returns (similar to below).
Public Sub SortDataWorksheets()
Dim wsh As Worksheet
Dim lLastRow As Long
For Each wsh In ThisWorkbook.Worksheets
Select Case wsh.Name
Case "Dashboard", "rawdata", "template", "macros instructions", _
"Sheet1a", "Sheet2a", "inputlist", "ProductList", "NA"
'Do nothing
Case Else
lLastRow = wsh.Cells(wsh.Rows.Count, 2).End(xlUp).Row
With wsh.Sort
With .SortFields
.Clear
.Add Key:=Range("B5:B" & lLastRow), _
SortOn:=xlSortOnValues, _
Order:=xlAscending, _
DataOption:=xlSortNormal
End With
.SetRange Range("A5:C" & lLastRow)
.Header = xlNo
.MatchCase = False
.Orientation = xlTopToBottom
'.SortMethod = xlPinYin 'Only need if sorting Chinese characters.
.Apply
End With
End Select
Next wsh
End Sub

VBA Script to remove all lines other then my filtered values

I am currently in the process of creating a VBA Script where i extract a list of raw data and filter out values Apple, Banana, and Oranges. I then delete all the other rows if it is not the values mentioned above.
So for example i have apple, banana, orange, grape, mandarin, avocado, coconut, lemon, watermelon.
I only want to keep apple, banana and orange in the end. If it has any of the other fruits i want that whole row of information removed.
Sub RMWO_Clean()
Dim ws As Worksheet
Dim rng As Range
Dim lastRow As Long
Set ws = ActiveWorkbook.Sheets("Sheet1")
lastRow = ws.Range("Q" & ws.Rows.Count).End(xlUp).Row
Set rng = ws.Range("Q1:Q" & lastRow)
Columns("AF:AF").Select
Selection.TextToColumns Destination:=Range("AA1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=False, Tab:=True, _
Semicolon:=False, Comma:=False, Space:=False, Other:=False, FieldInfo _
:=Array(1, 1), TrailingMinusNumbers:=True
With rng
.AutoFilter Field:=1,Criteria1:="<>*Apple*", Operator:=xlAnd, Criteria2:="<>*Banana*"
.Offset(1, 0).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
ws.AutoFilterMode = False
End Sub
I know that you cannot use
Criteria3:=xx
I have also tried
Criteria1:=Array("<>Apple", "<>Banana", "<>Orange")
But this seems to only leave orange behind.
Are you able to let me know what i am doing wrong?
Criteria1:=Array("<>Apple", "<>Banana", "<>Orange") needs Operator:=xlFilterValues operator, and yet won't work with those "<>"
so you can fool it by thinking the other way around:
filter "good" records
delete all records that are not good
like follows:
With rng
.AutoFilter Field:=1, Criteria1:=Array("Apple", "Banana", "Orange"), Operator:=xlFilterValues
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0) ' reference 'records' only (skip headers)
Select Case Application.Subtotal(103, .Cells) ' count number of filtered cells
Case 0 'if no cells to save
.EntireRow.Delete ' delete all rows
Case Is < .Count 'if there's at least one row to delete
Set saveRng = .SpecialCells(xlCellTypeVisible) ' store cells to save
.Parent.AutoFilterMode = False 'remove filter
saveRng.EntireRow.Hidden = True 'hide cells to save
.SpecialCells(xlCellTypeVisible).EntireRow.Delete 'delete visible cells
saveRng.EntireRow.Hidden = False 'bring cells to save visible back
End Select
End With
.Parent.AutoFilterMode = False
End With
Starting with:
I run:
Dim myRange As Range
Set myRange = ActiveSheet.Range("$A$1:$A$4")
myRange.AutoFilter Field:=1, _
Criteria1:="<>*Banana*", Operator:=xlAnd, Criteria2:="<>*apple*"
...and I get:
...and then I run:
myRange.AutoFilter Field:=1
...and I get:
I can delete the non-filtered rows with:
Rows("2:7").Delete Shift:=xlUp
Putting it all together, you could do something like:
Sub DeleteRowsExceptApplesAndBananas()
Const startCell = "A1"
Dim rgFilter As Range
'get range to filter
With Sheets("Sheet1")
Set rgFilter = Range(.Range(startCell), .Range(startCell).End(xlDown))
'set filter
rgFilter.AutoFilter 1, "<>*Banana*", xlAnd, "<>*apple*"
'delete rows beginning one below startCell's row
Range(.Range(startCell).Offset(1).Row & ":" & _
.Range(startCell).End(xlDown).Row).Delete (xlUp)
'un-filter
rgFilter.AutoFilter 1
End With
End Sub
It doesn't seem to me that Range.AutoFilter will do what you want it to here, precisely because you can only use two criteria for it.
I'd personally prefer to solve this problem with a loop operation, like so:
Option Compare Text
Sub Macro1()
Dim ws As Worksheet
Dim rng As Range
Dim col As String
Dim i As Integer
Set ws = ActiveWorkbook.Sheets("Sheet1")
col = "A"
i = 1
Set rng = ws.Range(col & i)
Do
Select Case rng.FormulaR1C1
Case "apple", "orange", "banana"
i = i + 1
Case Else
rng.Delete xlShiftUp
End Select
Set rng = ws.Range(col & i)
Loop Until rng.FormulaR1C1 = ""
End Sub
The code above assumes that you've already done all the preprocessing you've needed to do to extract your list of fruits, and that that list begins in cell A1 of Sheet1, although you can of course modify that code to put the list anywhere you'd like.
Version 1 bellow uses a "reverse" AutoFilter
Version 2, moves all rows to keep to a new sheet, and deletes the old (very fast for a lot of rows)
.
Version 1
Option Explicit
Public Sub DeleteRowsForCriteria()
Const FILTER_COL = "Q"
Const To_KEP = "apple banana orange"
Dim ws As Worksheet, lr As Long
Set ws = Sheet1 'Or: Set ws = ThisWorkbook.Worksheets("Sheet1")
lr = ws.Cells(ws.Rows.Count, FILTER_COL).End(xlUp).Row
Application.ScreenUpdating = False
ws.Range("AF1:AF" & lr).TextToColumns Destination:=ws.Range("AA1"), _
TextQualifier:=xlDoubleQuote, _
FieldInfo:=Array(1, 1), _
TrailingMinusNumbers:=True
Dim filterCol As Range, toKep As Variant, keep As Range
Set filterCol = ws.Range("Q1:Q" & lr)
toKep = Split(To_KEP)
With filterCol 'Reverse Filter - show all rows to keep ("apple banana orange")
.AutoFilter Field:=1, Criteria1:=toKep, Operator:=xlFilterValues
If .SpecialCells(xlCellTypeVisible).Count > 1 Then
Set keep = .SpecialCells(xlCellTypeVisible).EntireRow
End If
.AutoFilter 'Unhide all rows
keep.Rows.Hidden = True 'Hide all rows to keep ("apple banana orange")
.SpecialCells(xlCellTypeVisible).EntireRow.Delete 'Delete unwanted (now visible)
End With
keep.Rows.Hidden = False 'Unhide rows to keep ("apple banana orange")
Application.ScreenUpdating = True
End Sub
.
Version 2
Public Sub DeleteRowsForCriteriaFast()
Const FILTER_COL = "Q"
Const To_KEP = "apple banana orange"
Dim ws1 As Worksheet, ws2 As Worksheet, lr As Long, wsName As String, keep As Range
Set ws1 = Sheet1 'Or: Set ws = ThisWorkbook.Worksheets("Sheet1")
lr = ws1.Cells(ws1.Rows.Count, FILTER_COL).End(xlUp).Row
Application.ScreenUpdating = False
ws1.Range("AF1:AF" & lr).TextToColumns Destination:=ws1.Range("AA1"), _
TextQualifier:=xlDoubleQuote, _
FieldInfo:=Array(1, 1), _
TrailingMinusNumbers:=True
Dim filterCol As Range, toKep As Variant
Set filterCol = ws1.Range("Q1:Q" & lr)
toKep = Split(To_KEP)
Application.DisplayAlerts = False
Set ws2 = ThisWorkbook.Worksheets.Add(After:=ws1)
wsName = ws1.Name
With filterCol
.AutoFilter Field:=1, Criteria1:=toKep, Operator:=xlFilterValues
If .SpecialCells(xlCellTypeVisible).Count > 1 Then
.EntireRow.Copy
ws2.Cells.PasteSpecial xlPasteColumnWidths
ws2.Cells.PasteSpecial xlPasteAll 'Paste data on new sheet
ws1.Delete: ws2.Name = wsName: ws2.Cells(1).Select
End If
End With
Application.ScreenUpdating = True
Application.DisplayAlerts = True
Application.CutCopyMode = False
End Sub
.
TextToColumns default parameters
DataType:=xlDelimited
ConsecutiveDelimiter:=False
Tab:=False
Semicolon:=False
Comma:=False
Space:=False
Other:=False

sort data left to right in excel vba

I want to sort complete sheet data by column header alphabetically.
Below code works fine but i have to manually enter data range in variables(keyrange and datarange) every time, Since number of columns/rows varies in every file. I tried different ways in below code. Can you advise Is there a way that the last column automatically selected ??like in below W is last column with data in file and code should pick up last column.
Similarly last row of columns should pick up into range (like 485 is last row of file in below code), IS it possible ?
Sub sortfile2()
Dim keyrange As String
Dim DataRange As String
keyrange = "A1:W1"
DataRange = "A1:W485"
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
Range(Selection, Selection.End(xlDown)).Select
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Sheet1").Sort.SortFields.Add Key:=Range(keyrange), _
SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With ActiveWorkbook.Worksheets("Sheet1").Sort
.SetRange Range(DataRange)
.Header = xlYes
.MatchCase = False
.Orientation = xlLeftToRight
.SortMethod = xlPinYin
.Apply
End With
End Sub
If the source rane is dynamic, you can go with
bottom= Range("A1").End(xlDown).Row
Set DataRange = Range("A1").CurrentRegion.Resize(bottom - 1).Offset(1)
Note that CurrentRegion itself is not enough. you should combine it with Resize and Offset. İf you try with only CurrentRegion and go with F8, you can see why.
Yes, determining last column and last row is possible.
If you want to determine last column in first (1) row, use the code:
Cells(1, Columns.Count).End(xlToLeft).Column
If you want to get last row in first column, use following:
Cells(Rows.Count, 1).End(xlUp).Row
This is for the first column / row, so you can change it as you want.
This is range of data.
Sub test()
Dim rngDB As Range
Dim Ws As Worksheet
Dim r As Long, c As Long
Set Ws = ActiveSheet
With Ws
r = .Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
c = .Cells.Find("*", SearchOrder:=xlByColumns, SearchDirection:=xlPrevious).Column
Set rngDB = .Range("a1", .Cells(r, c))
rngDB.Select
End With
End Sub
Or
range("a1").CurrentRegion
yes, Michal answer and other source helped to find exact required output
'Find the last non-blank cell in row 1
lCol = Cells(1, Columns.Count).End(xlToLeft).Column
d = Replace(Cells(1, lCol).Address(True, False), "$1", "")
'Find the last non-blank cell in column 1
lRow = Cells(rows.Count, 1).End(xlUp).row
keyrange = "A1:" & d & 1
DataRange = "A1:" & d & lRow
'below line is to print (for debugging) the calculated range
MsgBox (keyrange)
MsgBox (DataRange)

Excel 2013: Sorting columns based on first row value using VBA

I would like to implement an Excel macro that sorts all columns from column "C" to the last column containing data (columns A and B shall not be affected).
The columns shall be sorted from A->Z based on the cell value of their first row (which is a string).
So far, I came up with the following code which I do not like that much because it contains hardcoded numbers for the Sort range making the code not really robust.
Sub SortAllColumns()
Application.ScreenUpdating = False
'Sort columns
With ActiveWorkbook.Worksheets("mySheet").Sort
.SetRange Range("C1:ZZ1000")
.Header = xlYes
.MatchCase = False
.Orientation = xlLeftToRight
.Apply
End With
Application.ScreenUpdating = True
End Sub
Searching the internet, one may find tons of suggestions getting the last used column or row. However most of them will blow up the code more than I expected.
I am not a VBA expert and it would be great if someone could make a suggestion how this problem can be solved in an elegant and efficient way.
If this is important: We will definitely not have more that 1000 rows and 1000 columns.
Any suggestion is highly appreciated.
edited:
changed temporary sheet adding statement to have it always as the last one
revised its deletion statement accordingly
should your need be to sort columns by moving them so as to have their headers sorted from left to right, then try this code
Option Explicit
Sub main()
Dim lastCol As Long
With Sheets("mySheet")
lastCol = .cells(1, .Columns.Count).End(xlToLeft).Column
Call OrderColumns(Range(.Columns(3), Columns(lastCol)))
End With
End Sub
Sub OrderColumns(columnsRng As Range)
Dim LastRow As Long
With columnsRng
LastRow = GetColumnsLastRow(columnsRng)
With .Resize(LastRow)
.Copy
With Worksheets.Add(after:=Worksheets(Worksheets.Count)).cells(1, 1).Resize(.Columns.Count, .Rows.Count) 'this will add a "helper" sheet: it'll be removed
.PasteSpecial Paste:=xlPasteAll, Transpose:=True
.Sort key1:=.Columns(1), Order1:=xlAscending, Orientation:=xlTopToBottom, Header:=xlNo
.Copy
End With
.PasteSpecial Paste:=xlPasteAll, Transpose:=True
Application.DisplayAlerts = False: Worksheets(Worksheets.Count).Delete: Application.DisplayAlerts = True 'remove the "helper" sheet (it's the (n-1)th sheet)
End With
End With
End Sub
Function GetColumnsLastRow(rng As Range) As Long
Dim i As Long
'gets last row of the given columns range
GetColumnsLastRow = -1
With rng
For i = 1 To .Columns.Count
GetColumnsLastRow = WorksheetFunction.Max(GetColumnsLastRow, .Parent.cells(.Parent.Rows.Count, .Columns(i).Column).End(xlUp).row)
Next i
End With
End Function
it makes use of a "helper" temporary (it gets deleted by the end) sheet.
Thanks to the suggestions and revisions of #SiddharthRout I got this:
Sub SortAllColumns()
Application.ScreenUpdating = False
Dim ws As Worksheet
Dim LastRow As Long
Dim LastColumn As Long
Dim LastColumnLetter As String
Set ws = ThisWorkbook.Sheets("mySheet")
'Get range
With ws
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
LastColumnLetter = Split(.Cells(, LastColumn).Address, "$")(1)
'Sort columns
Range("C1:" & LastColumnLetter & LastRow).Select
.Sort.SortFields.Clear
.Sort.SortFields.Add Key:=Range("C1:" & LastColumnLetter & 1), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With .Sort
.SetRange ws.Range("C1:" & LastColumnLetter & LastRow)
.Header = xlYes
.MatchCase = False
.Orientation = xlLeftToRight
.Apply
End With
End With
Application.ScreenUpdating = True
End Sub

VBA: multiple copy and paste from one sheet to another

I'm new with vba and have created a macro that copy columns from all worksheets (sourceSheet) in the workbook over to Sheet1 (destSheet) and also creating an header in Sheet1 (destSheet).
It's working fine when I'm running it with columns in order, like A to D (A:D) but I want to copy the columns
A from sourceSheet to B in destSheet
B from sourceSheet to C in destSheet
C from sourceSheet to D in destSheet
D from sourceSheet to E in destSheet
E from sourceSheet to G in destSheet
J from sourceSheet to H in destSheet
K from sourceSheet to I in destSheet
O from sourceSheet to J in destSheet
I also want to insert a blank row in F in destSheet.
Someone that can help me with this ?
Sub Test()
Dim sourceSheet As Worksheet 'Define Source Sheet
Dim sourceRows As Integer 'Define Source Row
Dim destSheet As Worksheet 'Define Destination Sheet
Dim lastRow As Integer 'Define Last Row
Dim sourceMaxRows As Integer 'Define Source Max Rows
Dim totalRows As Integer 'Define Total Rows
Dim destRange As String 'Define Destination Range
Dim sourceRange As String 'Define Source Range
lastRow = 1
Worksheets.Add().Name = "Sheet1"
For Each sourceSheet In Worksheets
If sourceSheet.Name <> "Sheet1" Then
sourceSheet.Activate
sourceMaxRows = sourceSheet.Cells(Rows.Count, "A").End(xlUp).Row
totalRows = lastRow + sourceMaxRows - 4
Let sourceRange = "A5:D" & sourceMaxRows
Range(sourceRange).Select
Selection.Copy
sourceSheet.Select
Set destSheet = Worksheets("Sheet1")
destSheet.Activate
Let destRange = "B" & lastRow & ":E" & totalRows
Range(destRange).Select
destSheet.Paste
destSheet.Select
lastRow = lastRow + sourceMaxRows - 4
End If
Next
Rows("1:1").Select
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A1").Select
ActiveCell.FormulaR1C1 = "Product_Id"
Range("B1").Select
ActiveCell.FormulaR1C1 = "Category"
Range("C1").Select
ActiveCell.FormulaR1C1 = "Brand"
Range("D1").Select
ActiveCell.FormulaR1C1 = "Model"
Range("E1").Select
ActiveCell.FormulaR1C1 = "EAN"
Range("F1").Select
ActiveCell.FormulaR1C1 = "UPC"
Range("G1").Select
ActiveCell.FormulaR1C1 = "SKU"
Range("H1").Select
ActiveCell.FormulaR1C1 = "Supplier_Shop_Price"
Range("I1").Select
ActiveCell.FormulaR1C1 = "Invoice_Price"
Range("J1").Select
ActiveCell.FormulaR1C1 = "In_Stock"
Range("A1").Select
MsgBox "Updated"
End Sub
You need to build-up an algorithm on the following logic: for each sheet you run, the source column is "1" and the destination column is "2": at the end of each loop, you need to increment both variables by 2. Here is how your code algorithm should look like (I let you do the job of rearranging your specific code to the algorithm):
sourceColumnIndex = 1
destColumnIndex = 2
sourceColumn = Split(Cells(1, sourceColumnIndex).Address, "$")(1)
destColumn = Split(Cells(1, destColumnIndex).Address, "$")(1)
For Each sourceSheet In Worksheets
'do your job here: for example:
'...
sourceMaxRows = sourceSheet.Cells(Rows.Count, sourceColumn).End(xlUp).Row
'...
destRange = destColumn & lastRow & ":E" & totalRows
'...
'before to go to the next, readapt the indexes:
sourceColumnIndex = sourceColumnIndex + 2
destColumnIndex = destColumnIndex + 2
sourceColumn = Split(Cells(1, sourceColumnIndex).Address, "$")(1)
destColumn = Split(Cells(1, destColumnIndex).Address, "$")(1)
'we can go to the next with "C" and "D", then with "E" and "F" etc.
Next sourceSheet
NOTE 1
Such a function:
sourceColumn = Split(Cells(1, sourceColumnIndex).Address, "$")(1)
is just converting the column number to the associated letter by using the address and splitting it by "$".
NOTE 2
A passage like this is useless as well as slower:
Range("A1").Select
ActiveCell.FormulaR1C1 = "Product_Id"
Rather, try to re-factor your code like this:
Range("A1").FormulaR1C1 = "Product_Id"
without need of selecting the cell, but directly writing on its property (in this case I would rather use .Value, but you might want to use .FormulaR1C1, you know better than me.
AFTER THAT CHRISTMAS007 HAS CLEANED YOUR QUESTION
Well, clearly the key of all this is to use a variable letter. I might suggest you to embed the split into a function that converts numbers in letters:
Function myColumn(ByVal num As Integer) As String
myColumn = Split(Cells(1, num).Address, "$")(1)
End Function
and every time working with numbers. You can call the above function like this:
num = 1
Range(myColumn(num) & 1).Select
the above will select for you the range "A1", because you passed the number "1" into the function.
Of course, being your request a bit more detailed, this is something you should study yourself. But the idea is anyway that one: define the indexes at the beginning, such as indSource and indDest, then...
decrease them at your pleasure with indSource = indSource - 1 (or -2, or -3)
increase them at your pleasure with indDest = indDest + 1 (or +2, or +3)
and work within the loop to get your desired results.
I did it easier, by deleting the rows and added one new blank column in the macro and it's working as I want, I just added the code to the macro:
For Each sourceSheet In Worksheets
sourceSheet.Activate
Columns("F:I").Delete
Columns("H:J").Delete
Columns("I:N").Delete
Columns("E:E").Insert
Next