I want to merge rows in Excel: the content to merge can be in different columns, "C" or "D" in my example. Any way I can do this using VBA? The file has ~20k rows.
My File: http://i.imgur.com/yDPdaQC.png
Goal: http://i.imgur.com/SZ5t9oX.png
Edit with more details:
Some sentences from the C & D columns are divided in 2,3 and sometimes 4 rows. I would like to merge those strings at the "top" cell from their respective column, when "A" and "B" have a value.
Thanks for your help!
you can use this.
Sub Merge()
Dim ws As worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim ws2 As worksheet: Set ws2 = ThisWorkbook.Sheets("Sheet2")
Dim sheet2Rng As Range: Set sheet2Rng = ws2.UsedRange
Dim startRow As Integer: startRow = LastRow(ws) + 1
Dim ws2RowCount As Integer: sheet2Rng.Rows.Count
ChangeEvents False
ws.Range("A" & startRow).Resize(ws2RowCount, 4).value = sheet2Rng.value
ChangeEvents True
End Sub
Public Function LastRow(worksheet As worksheet) As Integer
LastRow = worksheet.Cells(Rows.Count, 1).End(xlUp).Row
End Function
Sub ChangeEvents(value As Boolean)
Application.EnableEvents = value
End Sub
Can you clarify? Are you trying to:
Create merged cells: C1 with D1, C2 with D2, etc? This will lose the contents of the D column.
Take the texts in column D and append them to the end of the column C cells;
Create a new column which contains the C + D appended texts
Something like this:
Sub SquishRows()
Dim sh1 As Worksheet, sh2 As Worksheet
Dim rng As Range, rr As Range
Dim rowdata As Variant
Dim i As Integer, idx As Integer, j as Integer
Set sh1 = Worksheets("Sheet1")
Set sh2 = Worksheets("Sheet2")
sh1.Activate
Set rng = Range("A2").Resize(sh1.UsedRange.rows.Count - 1, sh1.UsedRange.Columns.Count)
ReDim rowdata(Application.CountA(rng.Columns(1)), rng.Columns.Count - 1)
idx = 0
For i = 1 To rng.rows.Count
Set rr = rng.rows(i)
If Len(rr.Cells(1).Text) And Len(rr.Cells(2).Text) Then
idx = idx + 1
For j = 1 To rng.Columns.Count
rowdata(idx, j - 1) = rr.Cells(j).Text
Next
Else
For j = 3 To rng.Columns.Count
If Len(rr.Cells(j).Text) Then
rowdata(idx, j - 1) = rowdata(idx, j - 1) & " " & rr.Cells(j).Text
End If
Next
End If
Next
'push data to Sheet2
sh2.Range("A1").Resize(UBound(rowdata, 1) + 1, UBound(rowdata, 2) + 1).Value = rowdata
'add in header row
sh2.Range(sh1.UsedRange.rows(1).Address).Value = sh1.UsedRange.rows(1).Value
sh2.Activate
End Sub
Related
Would anyone will be able to help me with this script please?
As it stand, this current macro separate the data once the value/text have changes and insert new row but I just cannot work it out how to include the headers once the row have been inserted.
Sub Insert Row()
Dim ws As Worksheet
Dim lr As Long
Dim i As Long
Set ws = Worksheets("Sheet1") 'the sheet with the data
lr = ws.Range("A" & Rows.Count).End(xlUp).Row 'last row with data in Column A
For i = lr - 1 To 2 Step -1
If ws.Range("A" & i).Value <> ws.Range("A" & i + 1).Value Then ws.Range("A" & i + 1).EntireRow.Insert
Next i
End Sub
Thank you in advanced.
Duplicate Headers
A Quick Fix
Sub InsertHeaders()
Const FIRST_ROW As Long = 1
Const EMPTY_ROWS As Long = 1
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim LastRow As Long: LastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim r As Long
For r = LastRow To FIRST_ROW + 2 Step -1
With ws.Cells(r, "A")
If .Value <> .Offset(-1).Value Then
.EntireRow.Resize(EMPTY_ROWS + 1).Insert
ws.Rows(1).Copy ws.Rows(.Row - 1)
End If
End With
Next r
End Sub
Please ignore my comment, as I just realize that it will be a different result if in column A there is a cell with the same value.
Example Data :
Expected result after running the sub :
Sub test()
Dim rgHdr As Range: Dim rgData As Range: Dim cell As Range
Dim i As Integer: Dim arr: Dim ins As Integer:dim sh as worksheet
Set sh = Sheets("Sheet1") 'change if needed
ins = 3 'change if needed
With sh
.Range("A1").EntireRow.Resize(ins).Insert Shift:=xlDown
Set rgHdr = .Range("A1").EntireRow.Resize(1 + ins)
Set rgData = .Range("K" & 2 + ins, .Range("K" & Rows.Count).End(xlUp))
End With
Set arr = CreateObject("scripting.dictionary")
For Each cell In rgData: arr.Item(cell.Value) = 1: Next
For i = 1 To arr.Count - 1
rgHdr.Copy
sh.Cells(rgData.Find(arr.Keys()(i), _
after:=rgData.End(xlDown)).Row, 1).Insert Shift:=xlDown
Next
sh.Range("A1").EntireRow.Resize(ins).Delete
End Sub
sh = the sheets where the data is.
ins = skip how many blank rows.
The code use "insert copied cells" method, so it make three blank rows (the value of ins) before the header, then set the first three rows as rgHdr, set the rgData from K2 down to the last row with value.
arr = unique value in column K.
then it loop to each element in arr, get the first row occurence of the found cell which value is the looped element in arr, insert the copied rgHdr to that row.
Then finally it delete those three (ins value is 3) additional blank rows.
In column A, I have different text in each cell.
In between the text within a cell, there is a number in a specific structure - "####.##.####"
I would like to copy this number, if it exists, to column B in the same line.
If there is more than one number with the structure in the same cell, the next numbers should be copied to column C, D, E etc. on the same line.
Sub findValues()
Dim loopCounter, lastRow, nextBlank As Long
lastRow = Range("A" & Rows.Count).End(xlUp).Row
For loopCounter = 1 To lastRow Step 1
With Sheets("Sheet2")
nextBlank = .Range("A" & Rows.Count).End(xlUp).Offset(1).Row
If Cells(loopCounter, 1).Value Like "[0-9]{4}.[0-9]{2}.[0-9]{4}" Then
Cells(loopCounter, 2) = 1
End If
End With
Next loopCounter
End Sub
Split Column (Loop)
Option Explicit
Sub SplitColumnA()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets("Sheet2")
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim cCell As Range
Dim Words() As String
Dim Sentence As String
Dim r As Long, c As Long, n As Long
For r = 2 To lRow
Set cCell = ws.Cells(r, "A")
Sentence = Replace(CStr(cCell.Value), ")", "")
Words = Split(Sentence)
For n = 0 To UBound(Words)
If Words(n) Like "####.##.####" Then
c = c + 1
cCell.Offset(, c).Value = Words(n)
End If
Next n
c = 0
Next r
MsgBox "Data split.", vbInformation
End Sub
I'm trying to arrange some uniformly scattered information from a spreadsheet to another. The information I'm interested in is in Sheet2 and I wish to place them in a customized manner in Sheet1.
I've tried with:
Sub ArrangeInformation()
Dim ws As Worksheet, cel As Range
Set ws = ThisWorkbook.Sheets("Sheet2")
Set tws = ThisWorkbook.Sheets("Sheet1")
For Each cel In ws.Range("A1:A" & ws.Cells(Rows.Count, 1).End(xlUp).row)
tws.Range(cel(1, 1).Address) = cel
Next cel
End Sub
The above script just replicates the same information in Sheet1 how they are in Sheet2.
Data in Sheet2:
How I like to arrange them in Sheet1:
How can I arrange those information in sheet1?
we can use some math to get the correct columns and rows:
Sub ArrangeInformation()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet2")
Dim tws As Worksheet
Set tws = ThisWorkbook.Sheets("Sheet1")
Dim i As Long
i = 0
Dim j As Long
j = 0
Dim cel As Range
For Each cel In ws.Range("A1:A" & ws.Cells(ws.Rows.Count, 1).End(xlUp).Row)
If cel <> "" Then
tws.Cells(Int(i / 4) + 1, ((j) Mod 4) + 1) = cel
i = i + 1
j = j + 1
End If
Next cel
End Sub
In the math the 4s are the number of items in each pattern
The INT will increase by 1 every four rounds and the MOD will repeat 1,2,3,4 every 4 rounds.
Just because:
this can be done with a formula:
=INDEX(Sheet2!$A:$A,INT(ROW(1:1)-1)/5+MOD((COLUMN(A:A)-1),5)+1)
where the 2 5s are the pattern and the +1 is the starting row of the data.
Put that in sheet1 A1 and copy over and down.
Maybe use Resize to transfer your data:
Sub Test()
Dim lr As Long, x As Long, y As Long
Dim ws1 As Worksheet: Set ws1 = ThisWorkbook.Sheets("Sheet1")
Dim ws2 As Worksheet: Set ws2 = ThisWorkbook.Sheets("Sheet2")
y = 1
lr = ws1.Cells(ws1.Rows.Count, "A").End(xlUp).Row
For x = 1 To lr Step 5
ws2.Cells(y, 1).Resize(, 4) = Application.Transpose(ws1.Cells(x, 1).Resize(4))
y = y + 1
Next x
End Sub
And propbably faster, you could assign your range to an array:
Sub Test()
Dim arr As Variant
Dim lr As Long, x As Long, y As Long
Dim ws1 As Worksheet: Set ws1 = ThisWorkbook.Sheets("Sheet1")
Dim ws2 As Worksheet: Set ws2 = ThisWorkbook.Sheets("Sheet2")
y = 1
lr = ws1.Cells(ws1.Rows.Count, "A").End(xlUp).Row
arr = ws1.Range("A1:A" & lr)
For x = LBound(arr) To UBound(arr) Step 5
ws2.Cells(y, 1).Resize(, 4) = Array(arr(x, 1), arr(x + 1, 1), arr(x + 2, 1), arr(x + 3, 1))
y = y + 1
Next x
End Sub
You'll need to manage which rows and columns you write to inside your loop. Something like:
Sub ArrangeInformation()
Dim ws As Worksheet, cel As Range
'New variables to manage rows and columns to write to
Dim writecol as integer, writeRow as integer
Set ws = ThisWorkbook.Sheets("Sheet2")
Set tws = ThisWorkbook.Sheets("Sheet1")
'Start in Row 1, Column 1
writeRow = 1
writeCol = 1
For Each cel In ws.Range("A1:A" & ws.Cells(Rows.Count, 1).End(xlUp).row)
tws.Range(writeRow, writeCol) = cel
'Next Column, or reset to 1 and iterate rows
If writeCol = 4 Then
writeCol = 1
writeRow = writeRow + 1
Else
writeCol = writeCol + 1
End If
Next cel
End Sub
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I have this huge data of books of various publishers, some record in 4 lines some in 5 some in 3, each record ends with an empty cell, which looks like this:
1111
2222
3333
4444
emptyCell
5555
6666
7777
8888
9999
emptyCell
1234
5678
9999
What formula/macro code can be used to get the output of:
1111 2222 3333 4444
5555 6666 7777 8888 9999
1234 5678 9999
one of the possible solutions:
Sub test()
Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")
Dim rng As Range: Set rng = Range([A1], Cells(Rows.Count, "A").End(xlUp).Offset(1))
Dim cl As Range, key As Variant, strToAdd$: strToAdd = ""
For Each cl In rng
If cl.Value2 <> "" Then
strToAdd = strToAdd & " " & cl.Value2
Else
dic.Add strToAdd, Nothing
strToAdd = ""
End If
Next cl
Dim sh As Worksheet, i&: i = 1
Set sh = Worksheets.Add: sh.Name = "Result"
For Each x In dic
sh.Cells(i, "A").Value2 = x
i = i + 1
Next x
End Sub
test based on provided dataset:
UPDATE: in case when the results in a row should have their own cell
Sub test2()
Dim dic As Object: Set dic = CreateObject("Scripting.Dictionary")
Dim rng As Range: Set rng = Range([A1], Cells(Rows.Count, "A").End(xlUp).Offset(1))
Dim cl As Range, key As Variant, strToAdd$: strToAdd = ""
For Each cl In rng
If cl.Value2 <> "" Then
strToAdd = strToAdd & "|" & cl.Value2
Else
dic.Add strToAdd, Nothing
strToAdd = ""
End If
Next cl
Dim sh As Worksheet: Set sh = Worksheets.Add:
Dim x, y$, z&, i&: i = 1
sh.Name = "Result " & Replace(Now, ":", "-")
For Each x In dic
y = Mid(x, 2, Len(x))
For z = 0 To UBound(Split(y, "|"))
sh.Cells(i, z + 1).Value2 = Split(y, "|")(z)
Next z
i = i + 1
Next x
End Sub
test based on provided dataset:
Use the following VBA code to transpose data with spaces. This will not remove the original code.
Sub Transpose()
Dim rng As Range
Dim i As Long
Dim j As Long
Set rng = Cells(Rows.Count, 1).End(xlUp)
j = 1
For i = 1 To rng.Row Step 5
Cells(j, "B").Resize(1, 5).Value = _
Application.Transpose(Cells(i, "A").Resize(6, 1))
j = j + 1
Next
End Sub
source
Public Sub DataTranspose()
Dim NoRows As Long, CurrentRow As Long, OffsetColumn As Long
Dim ResetCurrentRow As Long, ResetOffsetColumn As Long
Dim i As Long
' Replace with your destination. This will start writing back to Row 1 Column B
ResetCurrentRow = 1
ResetOffsetColumn = 2
' Replace with reference to your sheet
With ActiveSheet
NoRows = .Cells(.Rows.Count, 1).End(xlUp).Row
CurrentRow = ResetCurrentRow
OffsetColumn = ResetOffsetColumn
For i = 1 To NoRows
If .Cells(i, 1) <> vbNullString Then
.Cells(CurrentRow, OffsetColumn).Value2 = .Cells(i, 1).Value2
OffsetColumn = OffsetColumn + 1
Else
CurrentRow = CurrentRow + 1
OffsetColumn = ResetOffsetColumn
End If
Next i
End With
End Sub
I interpreted the question as the cell values should have their own cell when they are copied to the row.
You need to define which workbook name, worksheet names and also which column the result should start and paste into (columnComparePaste = 2 'where 2 = Column B).
This is a possible solution then.
VBA Code
Sub CompareCopyFilter()
Dim CopyFromWorkbook As Workbook
Set CopyFromWorkbook = Workbooks("Book2.xlsm") 'Name the Workbook that should be copied from
Dim CopyToWorkbook As Workbook
Set CopyToWorkbook = Workbooks("Book2.xlsm") 'Name the Workbook that should be copied to
Dim CopyFromSheet As Worksheet
Set CopyFromSheet = CopyFromWorkbook.Worksheets("Sheet1") 'Name the Worksheet that should be copied from
Dim CopyToSheet As Worksheet
Set CopyToSheet = CopyToWorkbook.Worksheets("Sheet1") 'Name the Worksheet that should be copied to
Dim lrow As Long
Dim lrowCompare As Long
Dim lrowPasteCopyTo As Long
Dim Val As String
Dim ValCompare As String
Dim i As Long
Dim j As Long
Dim Test As String
Dim Test2 As String
Dim columnComparePaste As Long
Dim columnCompare As Long
columnComparePaste = 2 'Which column number the data should be past into (Column B = 2)
lrow = CopyFromSheet.Cells(Rows.Count, "A").End(xlUp).Row 'Find last row in sheet that should be copied from
lrowCompare = CopyToSheet.Cells(Rows.Count, "B").End(xlUp).Row 'Find last row in sheet that should be copied from
columnCompare = columnComparePaste 'Dummy variable to reset column number
For i = 1 To lrow 'Find last row in the range you want to copy from
Val = CopyFromSheet.Cells(i, "A").Value 'Get the value from the cell you want to copy from
If Val <> "" Then 'If cell is not empty then
CopyFromSheet.Activate 'Activate worksheet to copy from
CopyFromSheet.Range(Cells(i, "A"), Cells(i, "A")).Copy 'Copy cell from column A, row i
CopyToSheet.Activate 'Activate worksheet to paste into
CopyToSheet.Range(Cells(lrowCompare, columnCompare), Cells(lrowCompare, columnCompare)).PasteSpecial xlPasteValues 'Paste cell from into Column set earlier, add 1 column for each loop
columnCompare = columnCompare + 1 'When value is pasted to column, add 1 column for next loop to paste into
Else
lrowCompare = lrowCompare + 1 'For each empty cell add one row below previous to paste into
columnCompare = columnComparePaste 'Reset the column value where paste should start
End If
Next i
Application.CutCopyMode = False 'Deselect any copy selection
End Sub
Result in Excel:
I have 2 sheets, shown below
What I would like to do is, write a VBA subroutine that:
look for all instances of sheet 2 column A from sheet 1 column A,
then replace the corresponding sheet 1 column D with sheet 2 column B
Greatly appreciate your help.
You may give this a try...
Sub CompareAndReplaceData()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim lr As Long, i As Long
Dim Rng As Range, Cell As Range
Dim x, dict
Application.ScreenUpdating = False
Set ws1 = Sheets("Sheet1")
Set ws2 = Sheets("Sheet2")
lr = ws1.Cells(Rows.Count, 1).End(xlUp).Row
Set Rng = ws1.Range("A2:A" & lr)
x = ws2.Range("A1").CurrentRegion.Value
Set dict = CreateObject("Scripting.Dictionary")
For i = 2 To UBound(x, 1)
dict.Item(x(i, 1)) = x(i, 2)
Next i
For Each Cell In Rng
If dict.exists(Cell.Value) Then
ws1.Cells(Cell.Row, "D") = dict.Item(Cell.Value)
End If
Next Cell
Application.ScreenUpdating = True
End Sub