Unable to arrange evenly scattered information from one sheet to another - excel

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

Related

Looping functions in VBA

My goal is to build a looping function that can take the *data and convert it into the *Goal Output
This is as far as I can make it with the code, my fundamental question is how to I nest my code inside of VBA to run 3 lines of code and then skip to line 6
*Data - sheet1
Layout
Machine 1
Work Center 1
Date
Machine 2
Work Center 2
Date
*Output - sheet2
Machine
Work Center
Date
Machine 1
Work Center 1
Date
Machine 1
Work Center 1
Date
*Goal Output - sheet 3
Machine
Work Center
Date
Machine 1
Work Center 1
Date
Machine 2
Work Center 2
Date
Code
Sub Fill_Data()
Sheet2.Activate
Set ws = Sheets("Sheet1")
Set ws2 = Sheets("Sheet2")
emptyrow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Dim i As Integer
For i = 1 To 3
ws.Cells(i, 1).Copy
ws2.Cells(emptyrow, i).PasteSpecial
Next i
emptyrow = emptyrow + 1
End Sub
The below creates the loop you are asking for, you would just need to modify to your specific need.
Sub Fill_Data()
Set ws = Sheets("Sheet1")
Set ws2 = Sheets("Sheet2")
ws.Range("A1").Activate
emptyrow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Dim i As Integer
Dim x As Integer
x = 1
For i = 1 To emptyrow
ws.Range(Cells(i, 1), Cells(i + 2, 1)).copy
ws2.Cells(x, 1).PasteSpecial Paste:=xlPasteAll, Operation:=xlNone, SkipBlanks:= _
False, Transpose:=True
i = i + 4
x = x + 1
Next i
End Sub
No need to nest any loops, you just need a couple extra incrementers to track everything.
Sub Fill_Data()
Dim ws As Worksheet
Dim ws2 As Worksheet
Set ws = Worksheets("Sheet1")
Set ws2 = Worksheets("Sheet2")
Dim emptyrow As Long
Dim lr As Long
Dim col As Long
Dim i As Long
With ws2
emptyrow = .Cells(Rows.Count, 1).End(xlUp).Row + 1
If emptyrow = 2 Then 'Populate Headers
.Cells(1, 1).Value = "Machine"
.Cells(1, 2).Value = "Work Center"
.Cells(1, 3).Value = "Date"
End If
End With
col = 1
With ws
lr = .Cells(Rows.Count, 1).End(xlUp).Row
For i = 1 To lr
If Not .Cells(i, 1).Value = "" And Not IsEmpty(.Cells(i, 1).Value) Then 'Skip blanks
ws2.Cells(emptyrow, col).Value = .Cells(i, 1).Value
If col = 3 Then 'Reset column and increment row
col = 1
emptyrow = emptyrow + 1
Else
col = col + 1
End If
End If
Next i
End With
End Sub
Transpose Groups of Data
Option Explicit
Sub TransposeGroupsOfData()
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Worksheets("Sheet1")
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, "A").End(xlUp).Row
Dim dws As Worksheet: Set dws = wb.Worksheets("Sheet2")
Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, "A").End(xlUp).Row
Dim dCell As Range: Set dCell = dws.Cells(dlRow + 1, "A")
Dim sfgr As Long ' Source First Group Row
Dim sr As Long ' Source Row
Dim dco As Long ' Destination Column Offset
For sfgr = 1 To slRow Step 5
sr = sfgr
For dco = 0 To 2
dCell.Offset(, dco).Value = sws.Cells(sr, "A").Value
sr = sr + 1 ' next source row
Next dco
Set dCell = dCell.Offset(1) ' next cell below
Next sfgr
MsgBox "Data exported.", vbInformation
End Sub

Coloring row when condition is met

I am trying to color the cell when condition is met and valid value for package of a product is found in another column in a different sheet.
There is a problem with the if statement.
Sub validation()
Dim lastRow_s As Long
Dim lastRow_m As Long
lastRow_s = Sheets("product").Cells(Rows.Count, "D").End(xlUp).Row
lastRow_m = Sheets("product").Cells(Rows.Count, "H").End(xlUp).Row
For i = 2 To lastRow_s
For j = 2 To lastRow_m
If Sheets("product").Cells(i,"D").Value =
Sheets("valid_package").Cells(j,"A").Value And
Sheets("product").Cells(i, "H").Value =
Sheets("valid_package").Cells(j,"B").Value Then
Sheets("product").Cells(i, "H").Value = vbGreen
End If
Next j
Next i
End Sub
I am trying to iterate over two columns to make sure that the product in column D has a valid package in column H in the product sheet. In the valid_package sheet there is a column for product and package that are valid for this products, so valid_package looks like this:
Product (this is column A from valid package)
Package (this is column B from valid package)
Product A
65x3
Product A
63x3
Product B
65x3
Product B
60x3
Product C
15
Product C
10x3
Product C
15
Product D
10
The product sheet is like this if you take only the two columns:
Product (this is column D from products)
Package (this is column H from products)
Product A
65x3
Product C
63x3
Product B
65x3
Product C
60x3
Product A
15
Product B
10x3
Product C
15
Product E
10
Product C
15
Product D
10
I want to highlight correct package in column H for sheet product or incorrect package in column H for sheet product, it doesn't matter what is colored.
I get
Expected: "line number or label or statement or end of statement.
Color Conditionally Matching Cells
Option Explicit
Sub TestAll()
ValidationQuickFix
ValidationReadable
ValidationEfficient
' Result on 1000 matches in 10000 rows of destination
' with only 10 rows of unique source values:
' Quick Fix: 6,1875
' Readable: 2,21484375
' Efficient: 0,87890625
End Sub
Sub ValidationQuickFix()
Dim t As Double: t = Timer
ThisWorkbook.Activate
Dim lastRow_s As Long
lastRow_s = Worksheets("valid_package").Cells(Rows.Count, "A").End(xlUp).Row
Dim lastRow_d As Long
lastRow_d = Worksheets("product").Cells(Rows.Count, "D").End(xlUp).Row
Dim i As Long, j As Long
For i = 2 To lastRow_d
For j = 2 To lastRow_s
If Worksheets("product").Cells(i, "D").Value = _
Worksheets("valid_package").Cells(j, "A").Value Then
If Worksheets("product").Cells(i, "H").Value = _
Worksheets("valid_package").Cells(j, "B").Value Then
Worksheets("product").Cells(i, "H").Interior.Color = vbGreen
Else
Worksheets("product").Cells(i, "H").Interior.Color = xlNone
End If
End If
Next j
Next i
Debug.Print "Quick Fix: " & Timer - t
End Sub
Sub ValidationReadable()
Dim t As Double: t = Timer
Dim wb As Workbook: Set wb = ThisWorkbook
Dim sws As Worksheet: Set sws = wb.Worksheets("valid_package")
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, "A").End(xlUp).Row
Dim dws As Worksheet: Set dws = wb.Worksheets("product")
Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, "D").End(xlUp).Row
Dim i As Long, j As Long
For i = 2 To dlRow
For j = 2 To slRow
If dws.Cells(i, "D").Value = sws.Cells(j, "A").Value Then
If dws.Cells(i, "H").Value = sws.Cells(j, "B").Value Then
dws.Cells(i, "H").Interior.Color = vbGreen
Else
dws.Cells(i, "H").Interior.Color = xlNone
End If
End If
Next j
Next i
Debug.Print "Readable: " & Timer - t
End Sub
Sub ValidationEfficient()
Dim t As Double: t = Timer
Dim wb As Workbook: Set wb = ThisWorkbook
Dim sws As Worksheet: Set sws = wb.Worksheets("valid_package")
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, "A").End(xlUp).Row
Dim srg1 As Range: Set srg1 = sws.Range("A2:A" & slRow)
Dim srg2 As Range: Set srg2 = sws.Range("B2:B" & slRow)
Dim dws As Worksheet: Set dws = wb.Worksheets("product")
Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, "D").End(xlUp).Row
Dim drg1 As Range: Set drg1 = dws.Range("D2:D" & dlRow)
Dim drg2 As Range: Set drg2 = dws.Range("H2:H" & dlRow)
Dim ddrg As Range
Dim dCell As Range
Dim sIndex As Variant
Dim dr As Long
For dr = 1 To drg1.Rows.Count
sIndex = Application.Match(drg1.Cells(dr).Value, srg1, 0)
If IsNumeric(sIndex) Then
If drg2.Cells(dr).Value = srg2.Cells(sIndex).Value Then
If ddrg Is Nothing Then
Set ddrg = drg2.Cells(dr)
Else
Set ddrg = Union(ddrg, drg2.Cells(dr))
End If
End If
End If
Next dr
If Not ddrg Is Nothing Then
drg2.Interior.Color = xlNone
ddrg.Interior.Color = vbGreen
End If
Debug.Print "Efficient: " & Timer - t
End Sub
Please, test the next code. It should be fast, using Find, placing the range to be colored in a Union range and coloring it at the code end. I hope that I correctly understood what you want and mostly what you have...
Sub validation()
Dim shP As Worksheet, shVP As Worksheet, rngColor As Range, rngA As Range, rngB As Range
Dim lastRow_P As Long, lastRow_VP As Long, cellMatch As Range, i As Long
Set shP = Sheets("product")
Set shVP = Sheets("valid_package")
lastRow_P = shP.cells(rows.Count, "D").End(xlUp).row
lastRow_VP = shVP.cells(rows.Count, "A").End(xlUp).row
Set rngA = shVP.Range("A2:A" & lastRow_VP)
For i = 2 To lastRow_P
Set cellMatch = rngA.Find(what:=shP.cells(i, "D").Value, LookIn:=xlValues, Lookat:=xlWhole)
If Not cellMatch Is Nothing Then
If cellMatch.Offset(0, 1).Value = shP.cells(i, "H").Value Then
If rngColor Is Nothing Then
Set rngColor = shP.cells(i, "H")
Else
Set rngColor = Union(rngColor, shP.cells(i, "H"))
End If
End If
End If
Next i
If Not rngColor Is Nothing Then rngColor.Interior.color = vbGreen
End Sub

How to make a simple "Sum" loop faster?

I am beginning to learn how to use loops and arrays but this one has me stuck. Below is a code that loops through cells and adds them together in column P.
Sub Loop_Test()
Dim sht1 As Worksheet
Dim lr As Long
Dim i As Long
Set sht1 = Worksheets("Sheet1")
lr = Fcst.Cells(Rows.Count, "A").End(xlUp).Row
With sht1
For i = 4 To lr
.Range("P" & i).Value = Application.Sum(Range("D" & i, "O" & i))
Next
End With
End Sub
Overall, this code works but it is very slow and I need to apply it to thousands of rows. I know that in order to make this faster, I need to turn the sum range into an array but I am not entirely sure how to do this when a loop is included.
Any help would be greatly appreciated.
Thanks,
G
Disclaimer: I know there are more efficient ways to sum cells together but this is just me playing around and learning.
Just do them all at once. Looping only adds time to process individual iterations.
With sht1.Range(sht1.cells(4, "P"), sht1.cells(lr, "P"))
.formula = "=sum(D4:O4)"
.Value = .value
End With
Use a variant array to limit the number of times that the vba accesses the worksheets:
Sub Loop_Test()
Dim sht1 As Worksheet
Set sht1 = Worksheets("Sheet1")
Dim fcst As Worksheet
Set fcst = Worksheets("Sheet2")
Dim lr As Long
lr = fcst.Cells(Rows.Count, "A").End(xlUp).Row
Dim dta As Variant
dta = fcst.Range(fcst.Cells(4, "D"), fcst.Cells(lr, "O")).Value
Dim otpt As Variant
ReDim otpt(1 To UBound(dta, 1), 1 To 1)
With sht1
Dim i As Long
For i = LBound(dta, 1) To UBound(dta, 1)
otpt(i, 1) = Application.Sum(Application.Index(dta, i, 0))
Next i
.Range("P4").Resize(UBound(dta, 1), 1).Value = otpt
End With
End Sub
Edit
The SUM(INDEX()) is slow it is quicker just to add the parts individually.
Sub Loop_Test()
Dim sht1 As Worksheet
Set sht1 = Worksheets("Sheet1")
Dim fcst As Worksheet
Set fcst = Worksheets("Sheet2")
Dim lr As Long
lr = fcst.Cells(Rows.Count, "A").End(xlUp).Row
Dim dta As Variant
dta = fcst.Range(fcst.Cells(4, "D"), fcst.Cells(lr, "O")).Value
Dim otpt As Variant
ReDim otpt(1 To UBound(dta, 1), 1 To 1)
With sht1
Dim i As Long
For i = LBound(dta, 1) To UBound(dta, 1)
Dim j as Long
For j = lbound(dta,2) to ubound(dta,2)
otpt(i, 1) = otpt(i, 1) + dta(i, j)
Next j
Next i
.Range("P4").Resize(UBound(dta, 1), 1).Value = otpt
End With
End Sub
Tested on 50,000 rows and result was near instantaneous.
Rather than looping over each row you can insert a summation formula into each row of column P with a single line of code:
.Range("P4:P" & lr).Formula="=SUM(D4:O4)"
assuming 4 is the starting row, and your variable lr is the last row.
Faster With an Array
Sub Loop_Test()
Const cSheet1 As Variant = "Sheet1"
Const cSheet2 As Variant = "Sheet2"
Const fr As Integer = 4
Dim sht1 As Worksheet
Dim fcst As Worksheet
Dim lr As Long
Dim i As Long
Dim vnt As Variant
Set sht1 = Worksheets(cSheet1)
Set fcst = Worksheets(cSheet2)
With fcst
lr = .Cells(.Rows.Count, "A").End(xlUp).Row
ReDim vnt(1 To lr - fr + 1, 1 To 1)
For i = 1 To UBound(vnt)
vnt(i, 1) = WorksheetFunction.Sum( _
.Range("D" & i + fr - 1, "O" & i + fr - 1))
Next
End With
sht1.Cells(fr, "P").Resize(UBound(vnt)) = vnt
End Sub

Excel VBA: find and replace with offset

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

Merge rows in Excel & VBA

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

Resources