Sort excel columns across sheets - vba

I have an excel file with 10 sheets. The sheets all contain the same column headers but different data. I sorted the first sheet manually and now I want all the columns in the other sheets to match the first sheet's order, I can't do them all manually because it would take me forever. How can I make all the columns across the workbook in the same order based on the first sheet order? I know little about VBA so looking for some help.

Make sure you save the entire workbook before running a macro. There is no undo.
Hope this help:
Sub ColumnRearrangement()
'Horaciux 2014-06-23
Dim nextLabel As String
Dim currentLabel As String
Dim TotalPages As Integer
Dim TotalColumns As Integer
TotalPages = 10
TotalColumns = 200
'Insert a blank column in each page
For p = 2 To TotalPages
Sheets(p).Select
Columns("A:A").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("B1").Select
Next
For c = TotalColumns To 1 Step -1
Sheets(1).Select
'Debug.Print "-" & Cells(1, c).Text & "-" & Str(c)
nextLabel = Cells(1, c).Text
Sheets(2).Select
For oldCulumn = 2 To TotalColumns + 1
'Debug.Print Cells(1, oldCulumn).Text & "-" & Str(oldCulumn)
currentLabel = Cells(1, oldCulumn).Text
If currentLabel = nextLabel Then
'Debug.Print currentLabel & "-" & Str(oldCulumn)
Exit For
End If
Next
For p = 2 To TotalPages
Sheets(p).Select
Columns(oldCulumn).Select
Selection.Cut
Columns("A:A").Select
Selection.Insert Shift:=xlToRight
Next
Next
For p = 2 To TotalPages
Sheets(p).Select
Range("A1").Select
Next
End Sub

If you're looking to repeat the re-ordering of columns, you should be able to record a simple macro for this, as long as the columns in all the sheets are in the same (wrong) order. Open the second sheet (since you already arranged the first), and do the following:
Open the second sheet
Click record macro, give it any name
While it's recording, cut/insert the columns in the order you want within the second sheet
Once done, click stop recording
Now you have a macro that you can open the rest of the pages, and it will repeat your re-ordering. Open each sheet and click View Macros, then run the one you just created. If you save the file, it will save the macro as well.
EDIT: Excellent point by #horaciux, be sure to save the entire workbook before running a macro, since there is no undo.

Related

Macro VBA to Copy Column based on Header and Paste into another Sheet

Background: This is my first time dealing with macros. I will have two worksheets that I’ll be using. The first sheet, ‘Source’ will have data available. The second sheet, ‘Final’ will be blank and is going to be where the macro will be pasting the data I’d like it to collect from the ‘Source’ sheet.
* I want the macro to find the specified header in the ‘Source’ sheet, copy that cell containing the header all the way down to the last row of existing data (instead of the entire column), and paste it onto the ‘Final’ sheet in a specified column (A, B, C, etc.). *
The reason why I have to specify which headers to find is because the headers in the ‘Source’ sheet won’t always be in the same position, but the ‘Final’ sheet’s headers will always be in the same position – so I CAN’T just record macros copying column A in ‘Source’ sheet and pasting in column A in ‘Final’ sheet. Also, one day the ‘Source’ sheet may have 170 rows of data, and another day it may have 180 rows.
Although, it would probably be best to copy the entire column since one of the columns will have a few empty cells rather than to the last row of existing data. I’m assuming it would stop copying when it reaches the first empty cell in the column chosen which would leave out the remaining data after that empty cell in the column – correct me if I’m wrong. If copying the entire column is the best way, then, please provide that as part of the possible solution. I’ve attached an example of the before & after result I would like accomplished:
Example of Result
Find Header=X, copy entire column -> Paste into A1 in ‘Final’ sheet
Find Header=Y, copy entire column -> Paste into B1 in ‘Final’ sheet
Etc..
I’m sorry if my wording isn’t accurate – I tried to explain the best I could. It’d be awesome if someone could help me out on this! Thanks!
u can try with this. i think its clear and step-by-step. it can be very optimized, but to start with vba i think its better this way.
the name of the column must be the same in both sheets.
Sub teste()
Dim val
searchText = "TEXT TO SEARCH"
Sheets("sheet1").Select ' origin sheet
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
x = Selection.Columns.Count ' get number of columns
For i = 1 To x 'iterate trough origin columns
val = Cells(1, i).Value
If val = searchText Then
Cells(1, i).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("sheet2").Select ' destination sheet
Range("A1").Select
Range(Selection, Selection.End(xlToRight)).Select
y = Selection.Columns.Count ' get number of columns
For j = 1 To y 'iterate trough destination columns
If Cells(1, j).Value = searchText Then
Cells(1, j).Select
ActiveSheet.Paste
Exit Sub
End If
Next j
End If
Next i
End Sub
good luck
I modified an answer I gave to another user with similar problem for your case,
I use dictionary function in most of my data sheets so that I can shift columns around without breaking the code, the below code you can shift your columns around and it will still work
the only main restriction is
1. your header names must be unique
2. your header name of interest must be exactly the same.
i.e. your source header of interest is PETER then your Data table should have a header with PETER and it must be unique.
Sub RetrieveData()
Dim wb As Workbook
Dim ws_A As Worksheet
Dim ws_B As Worksheet
Dim HeaderRow_A As Long
Dim HeaderLastColumn_A As Long
Dim TableColStart_A As Long
Dim NameList_A As Object
Dim SourceDataStart As Long
Dim SourceLastRow As Long
Dim Source As Variant
Dim i As Long
Dim ws_B_lastCol As Long
Dim NextEntryline As Long
Dim SourceCol_A As Long
Set wb = ActiveWorkbook
Set ws_A = wb.Worksheets("Sheet A")
Set ws_B = wb.Worksheets("Sheet B")
Set NameList_A = CreateObject("Scripting.Dictionary")
With ws_A
SourceDataStart = 2
HeaderRow_A = 1 'set the header row in sheet A
TableColStart_A = 1 'Set start col in sheet A
HeaderLastColumn_A = .Cells(HeaderRow_A, Columns.Count).End(xlToLeft).Column 'Get number of NAMEs you have
For i = TableColStart_A To HeaderLastColumn_A
If Not NameList_A.Exists(UCase(.Cells(HeaderRow_A, i).Value)) Then 'check if the name exists in the dictionary
NameList_A.Add UCase(.Cells(HeaderRow_A, i).Value), i 'if does not exist record name as KEY and Column number as value in dictionary
End If
Next i
End With
With ws_B 'worksheet you want to paste data into
ws_B_lastCol = .Cells(HeaderRow_A, Columns.Count).End(xlToLeft).Column ' Get number of DATA you have in sheet B
For i = 1 To ws_B_lastCol 'for each data
SourceCol_A = NameList_A(UCase(.Cells(1, i).Value)) 'get the column where the name is in Sheet A from the dictionaary
If SourceCol_A <> 0 Then 'if 0 means the name doesnt exists
SourceLastRow = ws_A.Cells(Rows.Count, SourceCol_A).End(xlUp).Row
Set Source = ws_A.Range(ws_A.Cells(SourceDataStart, SourceCol_A), ws_A.Cells(SourceLastRow, SourceCol_A))
NextEntryline = .Cells(Rows.Count, i).End(xlUp).Row + 1 'get the next entry line of the particular name in sheet A
.Range(.Cells(NextEntryline, i), _
.Cells(NextEntryline, i)) _
.Resize(Source.Rows.Count, Source.Columns.Count).Cells.Value = Source.Cells.Value
End If
Next i
End With
End Sub

Merging Cells row by row without losing any data

I'm getting data that is in 1, 2, or 3 columns (possibly more). I need each row to combine the data in the respective row without losing any of the data from any columns.
I managed to get some code together that will combine the cells properly, but I'm struggling to use this code to look through each row and combine the cells in that row, for all rows that contain data.
Here is what I have so far:
Sub JoinAndMerge()
'joins all the content in selected cells
'and puts the resulting text in top most cell
'then merges all cells
Dim outputText As String
Const delim = " "
On Error Resume Next
For Each cell In Selection
outputText = outputText & cell.value & delim
Next cell
With Selection
.Clear
.Cells(1).value = outputText
.Merge
.HorizontalAlignment = xlGeneral
.VerticalAlignment = xlCenter
.WrapText = True
End With
End Sub
And here's what I've got as far as trying to get it to look through each row.
Sub JoinAndMerge2()
'joins all the content in selected cells
'and puts the resulting text in top most cell
'then merges all cells
Dim outputText As String
Const delim = " "
On Error Resume Next
Dim cell_value As Variant
Dim counter As Integer
Dim xlastRow As Long
Dim xlastColumn As Long
xlastRow = Worksheets("Sheet48").UsedRange.Rows.Count
xlastColumn = Worksheets("Sheet48").UsedRange.Columns.Count
'Looping through A column define max value
For i = 1 To xlastRow
'Row counter
counter = 1
'Take cell one at the time
cell_value = ThisWorkbook.ActiveSheet.Cells(1, i).value
For Each cell In Selection
outputText = outputText & cell.value & delim
Next cell
With Selection
.Clear
.Cells(1).value = outputText
.Merge
.HorizontalAlignment = xlGeneral
.VerticalAlignment = xlCenter
.WrapText = True
End With
counter = counter + 1
Next i
End Sub
How do I get this to loop properly through each row?
If it helps, before on left, after on right:
I never recommend merging cells, but if you must...
This is dynamic by row (determined by Column A) and column. Each merge size is dependent on each rows furthest right non-blank column. Therefore, some merged cells will span 2 columns and some will span 3. If you don’t want that to be the case, you will need to find the max used column and merge by that column index
I.E. replacing MyCell.Resize(1, i -1).Merge with MyCell.Resize(1, MaxCol).Merge where MaxCol is your max used column.
Option Explicit
Sub Merger()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim MyCell As Range, Merged As String, i, iArr
Application.ScreenUpdating = False
Application.DisplayAlerts = False
For Each MyCell In ws.Range("A2:A" & ws.Range("A" & ws.Rows.Count).End(xlUp).Row)
For i = 1 To ws.Cells(MyCell.Row, ws.Columns.Count).End(xlToLeft).Column
Merged = Merged & Chr(32) & MyCell.Offset(, i - 1) 'Build String
Next i
MyCell.Resize(1, i - 1).Merge 'Merge
MyCell = Right(Merged, Len(Merged) - 1) 'Insert String
Merged = "" 'Reset string for next loop
MyCell.HorizontalAlignment = xlGeneral
MyCell.VerticalAlignment = xlCenter
MyCell.WrapText = True
Next MyCell
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
It can't be stressed too much. Merged cells should be avoided.
They play havoc when dragging an area to populate cells. They interrupt the double click autofill and make copying and pasting an exercise in frustration. They delay development and add complexity to formulas and VBA code all the while creating more opportunities for an error to occur or a bug to go unnoticed.
So i urge you to reconsider using merged cells.
Almost to prove the point, you'll find "*****" on a few lines in the two solutions below. Each one of those lines needs to be handled uniquely. Care to guess why? The merged cell you currently have in row 1. That merged cell can cause those lines to either halt with an error or continue with the possibility of unwanted consequences depending on which cell addresses actually hold row 1 data.
Merged cells are absolutely horrid and considered among the greatest of Excel sins.
Here are two ways forward without merged cells...
In VBA (psuedo code)
For (Columns, 2, LastColumn, Step 2)
For(Rows, 3, LastRow)
With Worksheet
If .Cells(Row,Column) <> vbNullString then
.cells(Row,Column-1)=.cells(Row,Column-1).Value2 _
& StringDeliminator & .cells(Row,Column).Value2
End If
End with
Next Rows
Columns (Column).EntireColumn.Delete*****
Next Columns
Using formulas in a worksheet
Add a new column C
In cell C3 use the formula
=If(A3<>"",C3=A3 & " " & B3,"")
Drag the formula down(copy to other columns if needed)
Ctrl Shift Up to select all the formulas
Copy *****
Paste Special Values *****
Delete columns A and B *****
There is one situation where merged cells are ok...
if you're in a situation where you're against the wall, there is nothing you can do because your manager doesn't care if your work is incompatible with his analyst's automation tools and refuses to accept center across selection as a viable alternative because "i know what center does and it does not help, you have to merge cells to get the text centered over those columns ".... if this is your situation then merged cells are ok, just use this method:: first, start looking for another job (or a promotion above your manager, your company should already be looking) and second, submit the broken merged cell version to the snowflake and quitely slip the functional version to your analyst as a preliminary estimate
That's the only time I authorize you to use merged cells.

Column and Row Indexing - VBA

I am trying to write something that does the following in excel macro (VBA):
For 'column X' of 'spreadsheet'
Copy range(row(1):row(5)
Paste to 'other spreadsheet' in range (row(1):row(5)) and column(Y)
And I want that to loop through the first spreadsheet for every column in the spreadsheet. This is what I have for 1 column:
Sheets("Info").Range("B3:B6").Value = Worksheets("Temp").Range("HK5:HK8").Value
Sheets("Info").Range("C3:C6").Value = Worksheets("Temp").Range("HK10:HK13").Value
This is what I want to do, however for every column within the first spreadsheet (there is 300 columns, manually would be tedious).
EDIT: This is another way i have found that may help explain the comments left below:
For i = 2 To 3
Worksheets("Info").Range(Cells(3, i), Cells(6, i)).Value = Worksheets("Temp").Range(Cells(5, i), Cells(8, i)).Value
Next i
I hoping this loops over the columns (2 - 290) currently its only from column 2 to 3 for testing purposes. I want the cells from TEMP worksheet from every column ('i') from row 5-8 and I want to put that into the INFO worksheet in column ('i') rows 3-6. Hope this helps!
Your description isn't consistent and I am not sure what is wrong with your final code. I have left a commented out debug statement so you can see what ranges are being worked with.
But
Use Option Explicit at the top of your code
Make sure to declare i as Long
Switch of ScreenUpdating to speed up performance
Use variables to hold the worksheets
Fully qualify Cells references with their worksheet
Public Sub test()
Dim i As Long
Dim wsInfo As Worksheet
Dim wsTemp As Worksheet
Set wsInfo = ThisWorkbook.Worksheets("Info")
Set wsTemp = ThisWorkbook.Worksheets("Temp")
Application.ScreenUpdating = False
For i = 2 To 290
' Debug.Print wsInfo.Name & "!" & wsInfo.Range(wsInfo.Cells(3, i), wsInfo.Cells(6, i)).Address & " = " & wsTemp.Name & "!" & wsTemp.Range(wsTemp.Cells(5, i), wsTemp.Cells(8, i)).Address
wsInfo.Range(wsInfo.Cells(3, i), wsInfo.Cells(6, i)).Value = wsTemp.Range(wsTemp.Cells(5, i), wsTemp.Cells(8, i)).Value
Next i
Application.ScreenUpdating = True
End Sub

Insert copied cells from one sheet to another sheet

I want to copy all rows that have a specific value in column E and then insert them (NOT PASTE! so i want to insert new rows start at cell A29) on another sheet.
The sheet I want to copy from is called "owssvr" and the one I want to copy to is called "AOB Approval Form". I want to insert the copied rows starting Cell A29 in the "AOB Approval Form".
When i run the code, nothing happens. No error message pops up.
Few definition of my code below:
LastRow: The last row of the "owssvr" sheet
PrimaryAOB: value that i want to lookup for in column 5. It is on the "AOB Approval Form" sheet
Here is my code:
For k = 2 To lastRow
If Worksheets("owssvr").Range("E" & k).Value = primaryAOB Then
Worksheets("owssvr").Rows(k).Copy
Worksheets("AOB Approval Form").Rows(k + 27).Insert Shift:=xlDown
Application.CutCopyMode = False
End If
Next k
THANK YOU!
I copied your code into a new module in a blank workbook, then made the necessary mods to make it run (which it did). It looks the same as yours:
Sub Question()
Dim k As long, lastRow As Long
Dim primaryAOB As String
lastRow = Sheets(1).Range("E" & (ActiveSheet.Rows.Count)).End(xlUp).Row
primaryAOB = Sheets(2).Range("A1").Text
For k = 2 To lastRow
If Worksheets("owssvr").Range("E" & k).Value = primaryAOB Then
Worksheets("owssvr").Rows(k).Copy
Worksheets("AOB Approval Form").Rows(k + 27).Insert Shift:=xlDown
Application.CutCopyMode = False
End If
Next k
End Sub
Since this worked, you may have just had some little syntax error somewhere while defining the variables. show us more of your procedure, that may reveal the issue! Also, have you run your code line by line? (F8)

How to remove a certain value from a table that will vary in size in Excel

I'm new to the community and I apologize if there is a thread elsewhere, but I could not find it!
I'm currently diving into VBA coding for the first time. I have a file that I dump into a worksheet that currently I'm manually organizing and pushing out. When put into the worksheet, it delimits itself across the cells. This dump file will have varying row and column lengths every time I get it in a given day and dump into a work sheet. For example, one day it may be twenty rows and one day it may be thirty.
A certain roadblock in my VBA code creation process has presented itself. I'm trying to create a code that will parse through the worksheet to remove any time a certain value appears (See below image - I'm referring to the (EXT)). After doing so I'm trying to concatenate the cells in the row up until there is a space (which with the rows that have (EXT), there usually isn't a space after until the (EXT) is removed).
The code I made works for now but I recognize it's not very efficient and not reliable if the names extend longer than two cells. I was hoping someone on here could provide me with guidance. So, I'm looking for two things:
For the code to scan the whole active used range of the table and remove (EXT). As it may appear in various columns.
A way to concatenate the cells in every row in the active range from A to the cell before a blank cell
Keep in mind I have no coding background, I'm learning and I'm not familiar with VBA terms and whatnot all that much just yet - so if you could please explain in laymen's terms I'd appreciate it. I hope all of this makes sense... Thanks in advance!
This is just an example of part of what the dump code looks like, so my code probably doesn't match with the example below - I just wanted to provide a visual:
http://i.imgur.com/IwDDoYd.jpg
The code I currently have:
Sub DN_ERROR_ORGANIZER()
' Removes any (EXT) in Column 3 in actual dump data file
For i = 200 To 1 Step -1
If (Cells(i, 3).value = "(EXT)") Then
Cells(i, 3).Delete Shift:=xlToLeft
End If
Next i
' Removes any (EXT) in Column 4 in actual dump data file
For j = 200 To 1 Step -1
If (Cells(j, 4).value = "(EXT)") Then
Cells(j, 4).Delete Shift:=xlToLeft
End If
Next j
' Removes any (EXT) in Column 5 in actual dump data file
For k = 200 To 1 Step -1
If (Cells(k, 5).value = "(EXT)") Then
Cells(k, 5).Delete Shift:=xlToLeft
End If
Next k
' Places a new column before A and performs a concatenate on cells B1 and C1 to
' form a name, then copies all through column A1 to repeat on each row
Columns("A:A").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Range("A1").Select
ActiveCell.FormulaR1C1 = "=PROPER(CONCATENATE(RC[1],"", "", RC[2]))"
Range("A1").Select
Selection.AutoFill Destination:=Range("A1:A51")
Range("A1:A51").Select
End Sub
edited: to keep the comma after the first "name" only
this should do:
Sub main()
Dim cell As Range
With Worksheets("names")
With Intersect(.UsedRange, .Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).EntireRow)
For Each cell In .Rows
cell.Cells(1, 2).Value = Replace(Replace(Replace(Join(Application.Transpose(Application.Transpose(cell.Value)), " "), " ", " "), " (EXT)", ""), " ", ", ", , 1)
Next cell
.Columns(1).FormulaR1C1 = "=PROPER(RC[1])"
.Columns(1).Value = .Columns(1).Value
.Offset(, 1).Resize(, .Columns.Count - 1).ClearContents
End With
End With
End Sub
just remember to change "names" to you actual worksheet name
edited 2:
code for stopping cells to be processed at every line at the last one before the first blank one
Sub main()
Dim cell As Range, dataRng As Range
With Worksheets("names") '<--| change "names" to you actual worksheet name
Set dataRng = Intersect(.UsedRange, .Range("A1", .Cells(.Rows.Count, 1).End(xlUp)).EntireRow)
For Each cell In dataRng.Columns(1).Cells
cell.Offset(, 1).Value = Replace(Replace(Replace(Join(Application.Transpose(Application.Transpose(.Range(cell, cell.End(xlToRight)).Value)), " "), " ", " "), " (EXT)", ""), " ", ", ", , 1)
Next cell
With dataRng
.Columns(1).FormulaR1C1 = "=PROPER(RC[1])"
.Columns(1).Value = .Columns(1).Value
.Offset(, 1).Resize(, .Columns.Count - 1).ClearContents
End With
End With
End Sub
I believe you are quite close to achieve what you are asking for and, based on your request, I will not give you a solution but some guidance to complete it by yourself.
First 3 loops: You could simplify by having a single set of nested loops: An outer loop running from 3 to 5, an inner loop running from 200 to 1; the outer loop will run over index, say "p", the inner over index, say "q", and your reference to cells would become Cells(q,p). If you need to run this over more than 3 rows, just start the outer loop from, say, 3 and till, say 10000 (being 10000 the maximal number of rows your data may display) and add a condition that if the first cell of the row is empty, you exit the outer loop.
The second part (this is what I understood) is to take the 2-3 first cells and concatenate them into a new cell (i.e. the column you add at the left). Once again, you can just loop over all your rows (much the same as in the outer loop mentioned above), except that now you will be looking at the cells in columns 2-4 (because you added a column at the left). The same exit condition as above can be used.
I'm not sure if this is what you were looking for, but this is what I understood you were looking for.
After reading user3598756's answer, I realized that I missed the boat with my original answer.
Sub DN_ERROR_ORGANIZER()
Dim Target As Range
Set Target = Worksheets("Sheet1").UsedRange
Target.Replace "(EXT)", ""
With Target.Offset(0, Target.Columns.Count).Resize(, 1)
.FormulaR1C1 = "=PROPER(C1&"", ""&TEXTJOIN("" "",TRUE,RC[-" & (Target.Columns.Count - 1) & "]:RC[-1]))"
.Value = .Value
End With
Target.Delete
End Sub
UPDATE
If you are running an older version of Excel that doesn't support TEXTJOIN then use this:
Sub DN_ERROR_ORGANIZER()
Dim Data
Dim x As Long, y As Long
Dim Target As Range
Dim Text As String
Set Target = Worksheets("Sheet1").UsedRange
Target.Replace "(EXT)", ""
Data = Target.Value
For x = 1 To Target.Rows.Count
Data(x, 1) = Data(x, 1)
For y = 2 To Target.Columns.Count
If Data(x, y) <> vbNullString Then Text = Text & " " & Data(x, y)
Next
If Len(Text) Then Data(x, 1) = Data(x, 1) & "," & Text
Text = vbNullString
Next
Target.ClearContents
Target.Columns(1).Value = Data
End Sub