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:
Related
I apologize for the vague title as I'm not really sure where the error is. I think I'm having some compability issues with copying the elements of an array and then manipulating that data.
This is the code I have so far:
Sub listNotCompletedTasks()
Dim cell As Range
Dim sourceRange As Range
Dim targetRange As Range
Dim notCompleted() As Variant
Dim i As Integer
Dim lastr As Integer
'define sourceRange
lastr = Range("A" & Rows.count).End(xlUp).Row
Set sourceRange = Range("A2:A" & lastr)
'notCompleted is an array with all the offset cells of the cells in sourceRange
'that don't contain a "Completed" string
i = 0
For Each cell In sourceRange.Cells
If cell.Offset(0, 2).Value <> "Completed" Then 'if the cell in column C does not contain "completed"...
ReDim Preserve notCompleted(0 To i)
notCompleted(i) = cell.Value 'add cell in column A to array
i = i + 1
End If
Next cell
'define targetRange
lastRow = Cells(Rows.count, "Z").End(xlUp).Row
Set targetRange = Range("Z1:Z" & lastRow)
'copy all elements from the array to the targetRange
For i = 0 To UBound(notCompleted)
targetRange.Offset(i, 0).Value = notCompleted(i)
Next i
End Sub
Expected output:
This works well. The problem begins with the second step:
Sub listNoDuplicatesAndNoOfInstances()
Dim sourceRangeZ As Range
Dim targetRangeB As Range
Set sourceRangeZ = Sheets("SourceData").Range("Z2")
Set targetRangeB = Sheets("TargetSheet").Range("B17")
'add all of the unique instances of a string in Z from the notCompleted() array
Do Until IsEmpty(sourceRangeZ)
If Application.WorksheetFunction.CountIf(Sheets("TargetSheet").Range("B:B"), sourceRangeZ.Value) = 0 Then
targetRangeB.Value = sourceRangeZ.Value
Set targetRangeB = targetRangeB.Offset(1, 0)
Else
End If
Set sourceRangeZ = sourceRangeZ.Offset(1, 0)
Loop
'count every instance of those strings and add the value to the respective cell to the right
Set targetRangeB = Sheets("TargetSheet").Range("C17")
Do Until IsEmpty(targetRangeB.Offset(0, -1))
targetRangeB.Formula = "=COUNTIF(SourceData!Z:Z,Z" & targetRangeB.Row & ")"
Set targetRangeB = targetRangeB.Offset(2, 0)
Loop
End Sub
The first loop (the one that adds every unique instance of the strings to column B) works. The second loop (the one that returns the number of instances of each string) does not work, only returning zeroes. The thing is, if I manually do the steps of the first subroutine (use a Pivot Table to filter out the rows I need, then copy the relevant row and paste it to column Z), and then call the second subroutine, then it actually works!
So I'm assuming the first subroutine is the culprit. A "cheap" workaround that worked for me was to copy the range in Z to another column (using sourceRange.Copy/targetRange.PasteSpecial xlPasteAll) and then call the second subroutine. What am I doing wrong, and is there a better way to solve this?
A 2D array you can copy to sheet without looping.
Sub listNotCompletedTasks()
Dim wsSource As Worksheet, arNotCompl()
Dim lastrow As Long, i As Long, n As Long
Set wsSource = ThisWorkbook.Sheets("SourceData")
With wsSource
lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
ReDim arNotCompl(1 To lastrow, 1 To 1)
For i = 2 To lastrow
If .Cells(i, "C") <> "Completed" Then
n = n + 1
arNotCompl(n, 1) = .Cells(i, "A")
End If
Next
If n = 0 Then Exit Sub
'copy array to targetRange
lastrow = .Cells(.Rows.Count, "Z").End(xlUp).Row
.Cells(lastrow + 1, "Z").Resize(n) = arNotCompl
End With
End Sub
Add the formula in column C when you add the unique value to column B.
Sub listNoDuplicatesAndNoOfInstances()
Dim wsSource As Worksheet, wsTarget As Worksheet
Dim lastrow As Long, i As Long, n As Long
Dim arNotCompl(), v
Set wsSource = ThisWorkbook.Sheets("SourceData")
With wsSource
lastrow = .Cells(.Rows.Count, "Z").End(xlUp).Row
arNotCompl = .Range("Z2:Z" & lastrow).Value2
End With
Set wsTarget = ThisWorkbook.Sheets("TargetSheet")
n = 17
With wsTarget
For i = 1 To UBound(arNotCompl)
v = arNotCompl(i, 1)
If Application.WorksheetFunction.CountIf(.Range("B:B"), v) = 0 Then
.Cells(n, "B") = v
.Cells(n, "C").Formula = "=COUNTIF(SourceData!Z:Z,B" & n & ")"
n = n + 1
End If
Next
End With
End Sub
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 want to remove duplicates based on the text in Column I and sum the values in Column C, the data in the other columns doesn't matter.
I do not want a pivot table and I am aware they are the preferred option for this type of thing.
An example of what I'd like to achieve:
I found VBA code and tried to modify it. It doesn't delete all the lines.
Sub Sum_and_Dedupe()
With Worksheets("data")
'deal with the block of data radiating out from A1
With .Cells(1, 1).CurrentRegion
'step off the header and make one column wider
With .Resize(.Rows.Count - 1, .Columns.Count + 1).Offset(1, 0)
.Columns(.Columns.Count).Formula = "=sumifs(c:c, i:i, i2)"
.Columns(3) = .Columns(.Columns.Count).Value
.Columns(.Columns.Count).Delete
End With
'remove duplicates
.RemoveDuplicates Columns:=Array(9), Header:=xlYes
End With
.UsedRange
End With
End Sub
This should be an answer to your question.
However, code might require adaptation if the range in which you look becomes very long.
Option Explicit
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Dim LastRow As Long, LastCol As Long, a As Double, i As Long
Dim Rng As Range
Dim Cell As Variant, Estimate As Variant
Set wb = ThisWorkbook
Set ws = wb.Sheets(1)
LastRow = ws.Cells(ws.Rows.Count, "I").End(xlUp).Row
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
Set Rng = ws.Range(ws.Cells(2, 9), ws.Cells(LastRow, 9))
For Each Cell In Rng
i = 0
a = 0
For Each Estimate In Rng
If Estimate.Value = Cell.Value Then
i = i + 1 'Count nr of intances
a = a + ws.Cells(Estimate.Row, 3).Value 'sum booking value
If i > 1 Then
ws.Rows(Estimate.Row).Delete
i = 1
LastRow = LastRow - 1
End If
End If
Next Estimate
ws.Cells(Cell.Row, 3).Value = a 'Enter sum in booked this week
Next Cell
End Sub
You'll either need to change your current sheet name to data, or change the first two lines of this code to fit your needs. sh = the data sheet that you showed us. osh = an output sheet that this code will generate. Note also if column C or I move you can update the positions easily by changing colBooked and colEstimate. If you have more than a thousand unique estimate entries then make the array number larger than 999.
Sub summariseEstimates()
Dim sh As String: sh = "data"
Dim osh As String: osh = "summary"
Dim colBooked As Integer: colBooked = 3
Dim colEstimate As Integer: colEstimate = 9
Dim myArray(999) As String
Dim shCheck As Worksheet
Dim output As Worksheet
Dim lastRow As Long
Dim a As Integer: a = 0
Dim b As Integer
Dim r As Long 'row anchor
Dim i As Integer 'sheets
'Build summary array:
With Worksheets(sh)
lastRow = .Cells.Find("*", searchorder:=xlByRows, searchdirection:=xlPrevious).Row
For r = 2 To lastRow
If r = 2 Then 'first entry
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
Else
For b = 0 To a
If VBA.LCase(VBA.Replace(.Cells(r, colEstimate), " ", "")) = VBA.LCase(VBA.Replace(VBA.Split(myArray(b), ",")(0), " ", "")) Then 'match
myArray(b) = VBA.Split(myArray(b), ",")(0) & "," & VBA.Split(myArray(b), ",")(1) + .Cells(r, colBooked)
Exit For
End If
Next b
If b = a + 1 Then 'completed loop = no match, create new array item:
a = a + 1
myArray(a) = .Cells(r, colEstimate) & "," & .Cells(r, colBooked)
End If
End If
Next r
End With
'Create summary sheet:
On Error Resume Next
Set shCheck = Worksheets(osh)
If Err.Number <> 0 Then
On Error GoTo 0
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
Err.Clear
Else
On Error GoTo 0
If MsgBox("*" & osh & "* sheet already exists. Proceed to delete and recreate?", vbOKCancel, "Summary") = vbCancel Then
Exit Sub
Else
Application.DisplayAlerts = False
Worksheets(osh).Delete
Set output = Worksheets.Add(After:=Worksheets(sh))
output.Name = osh
End If
End If
'Output to summary sheet:
With Worksheets(osh)
.Cells(1, 1).Value = "ESTIMATE"
.Cells(1, 2).Value = "BOOKED THIS WEEK"
For b = 0 To a
.Cells(b + 2, 1).Value = VBA.Split(myArray(b), ",")(0)
.Cells(b + 2, 2).Value = VBA.Split(myArray(b), ",")(1)
Next b
.Columns("A:B").AutoFit
End With
End Sub
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