I have 1000s rows of data in 1 column that I need to transpose into columns, based on each row that is bold. The number of rows between bold ones is inconsistent, same as strings values.
I've created a simple code that worked perfectly while testing the first 100 rows. But when trying to run it through the entire list or some other parts (even 50 rows) it just stucks while running so I have to quite excel via task manager (with no error msg).
Sub Transpose_by_bold()
Dim x, y As Integer
y = 1
For x = 1 To 2000
If Range("B" & x).Font.Bold = True And Range("B" & x + 1).Font.Bold = True Then y = 1
If Range("B" & x).Font.Bold = True And Range("B" & x + 1).Font.Bold = False Then
Range("B" & x + 1).Cut Range("B" & x).Offset(0, y)
Range("B" & x + 1).EntireRow.Delete
y = y + 1
x = x - 1
End If
Next x
End Sub
I'd highly appreciate if you could give me a piece of idea what's wrong here?
Your code is currently hanging because once it moves to the end of the list X never increases, so it goes into an infinite loop. I've not tested FaneDuru's code, so it may be the answer, but the other choice would be to add some kind of escape clause to your code that bounces you out in the event of some condition you don't expect to ever happen naturally in your code - like a counter if range("b" & x).value = "" that gets reset when not true and, upon getting to some maximum value (say, 10 back to back empty cells) sets X equal to your max value (2000, in this case).
Don't forget, in the presence of weird stuff like this, you can step through your code with F8 and watch your values of X and Y in the Locals Window - if you do that, the fact that X gets stuck becomes quickly apparent.
sample counter (not terribly efficient, but it works):
Sub Transpose_by_bold()
Dim x, y As Integer
Dim Counter as Integer
y = 1
For x = 1 To 2000
If IsEmpty(Range("B" & x + 1)) Then
Counter = Counter + 1
Else
Counter = 0
End If
If Counter > 9 Then
x = 2001
End If
If Range("B" & x).Font.Bold = True And Range("B" & x + 1).Font.Bold = True Then y = 1
If Range("B" & x).Font.Bold = True And Range("B" & x + 1).Font.Bold = False Then
Range("B" & x + 1).Cut Range("B" & x).Offset(0, y)
Range("B" & x + 1).EntireRow.Delete
y = y + 1
x = x - 1
End If
Next x
End Sub
An alternate choice, I just realized (editing to note this), would be to count the maximum possible number of rows via an intersect of your column of interest and the used range of the sheet and then keep a counter that just checks how many total rows you've evaluated (your X counter right now is how many rows you'l end up with, not how many you've looked at, due to your x=x-1 line) and run your primary For loop on that total rows counter rather than on X.
Good luck!
Try the next code, please. I hope I could deduce the logic of your code. Especially, how to use y (incrementing the column to copy the range for each occurrence)... If the logic is correct, the code should be fast, deleting all rows at once:
Sub Transpose_by_bold()
Dim sh As Worksheet, x As Long, y As Long, rngDel As Range
Set sh = ActiveSheet 'use here your sheet
y = 1
For x = 1 To 2000
If Range("B" & x).Font.Bold = True And Range("B" & x + 1).Font.Bold = True Then y = 1
If Range("B" & x).Font.Bold = True And Range("B" & x + 1).Font.Bold = False Then
Range("B" & x).Offset(0, y).Value = Range("B" & x + 1).Value
If rngDel Is Nothing Then
Set rngDel = Range("B" & x + 1)
Else
Set rngDel = Union(rngDel, Range("B" & x + 1))
End If
y = y + 1
End If
Next x
If Not rngDel Is Nothing Then rngDel.EntireRow.Delete xlUp
End Sub
Related
I want to check for duplicates in a column and my code is able to do so. But when it finds a duplicate in column L i want it to add "+1" to the integer in column c. So if "L5 and L6" are the same, I want "C5" to be "C5+1". But I have not been able to figure out how to do so.
Sub check_duplicates()
Dim x As Long
Dim LastRow As Long
LastRow = Range("L65536").End(xlUp).Row
For x = LastRow To 1 Step -1
If Application.WorksheetFunction.CountIf(Range("L2:L" & x), Range("L" & x).Value) > 1 Then
Range("C" & x).Formula = "=LEFT(x) + 1"
End If
Next x
End Sub
That should solve your problem:
Sub check_duplicates()
Dim x As Long
Dim LastRow As Long
LastRow = Range("L65536").End(xlUp).Row
For x = LastRow To 1 Step -1
If Application.WorksheetFunction.CountIf(Range("L2:L" & x), Range("L" & x).Value) > 1 Then
Range("C" & x) = Left(Range("C" & x), 1) + 1 & Mid(Range("C" & x), 2)
End If
Next x
End Sub
Replace Range("C" & x).Formula = "=LEFT(x) + 1" with something like Range("C" & x)=Range("C" & x) + 1. Making the formula in C5=C5+1 would be circular and would cause an error. Alternatively, set a variable equal to range C5, add 1 to it, then set range C5 to this variable. I'm assuming column C is a set of integers here and not formulas.
You can increment the value by wrapping the Left function (VBA version) around the cell value to get the value to increment by one and then use the space to extract the value to the right `"P" in your example, then bring them back together.
See the code below. It will work for instances where the number increments above single digits and it also assumes there will always be a space after the number and before the text.
Sub check_duplicates()
Dim x As Long
Dim LastRow As Long
LastRow = Range("L65536").End(xlUp).Row
For x = LastRow To 1 Step -1
If Application.WorksheetFunction.CountIf(Range("L2:L" & x), Range("L" & x).Value) > 1 Then
Dim y As Long, x As String
'increment left number by 1
y = Left(Range("C" & x).Value, InStr(1, Range("C" & x).Value, " ") - 1) + 1
'extract text after space
x = Mid(Range("C" & x).Value, InStr(1, Range("C" & x).Value, " "))
Range("C" & x).Value = y & x ' bring together and set the cell value to new incremented value
End If
Next x
End Sub
I have 16k rows of data. There are two columns with time. What i need is to find rows where time doesn't match and move everyhing below in last 3 columns down on one, so at the end I'll have all rows with time match and those that dont would have last 3 columns blank in that row.
here what I have so far, but I'm new to VBA and this doesnt work(
Sub timeline()
Dim y As Long
With ThisWorkbook.Sheets("L5")
y = .Range("G" & .Rows.Count).End(xlUp).Row
End With
x = 2
Do While ThisWorkbook.Sheets("L5").Cells(x, 4) <> ""
If ThisWorkbook.Sheets("L5").Cells(x, 4).Value = ThisWorkbook.Sheets("L5").Cells(x, 7).Value Then
Else: ThisWorkbook.Sheets("L5").Range("Gx:Iy").Select
Selection.Offset(1, 0).Select
y = y + 1
End If
x = x + 1
Loop
The following code should do it. Check whether the right ranges and cells are used; I tried to figure it out from your code:
Sub timeline()
Dim y As Long
With ThisWorkbook.Sheets("L5")
y = .Range("G" & .Rows.Count).End(xlUp).Row
End With
x = 2
Do While ThisWorkbook.Sheets("L5").Cells(x, 4) <> ""
If ThisWorkbook.Sheets("L5").Cells(x, 4).Value = ThisWorkbook.Sheets("L5").Cells(x, 7).Value Then
' nothing
Else
ThisWorkbook.Sheets("L5").Range("G" & Trim(Str(x)) & ":I" & Trim(Str(y))).Cut
ThisWorkbook.Sheets("L5").Range("G" & Trim(Str(x + 1))).Select
ThisWorkbook.Sheets("L5").Paste
y = y + 1
End If
x = x + 1
Loop
End Sub
I am running sub where it compares two cells (B and D/or string Received) from one sheet ("DATA") with two cells (C, H) from another sheet ("Incoming_report"), and if they match it transposes I, G cells from Incoming to Data.
It is done by combining two cells from Incoming_report sheet and writing new value in Z column for example "123456" from C and H to f.e. "123456Received" (there another 5 statuses (Received, Rejected, Sent...., but I need the ones only that was Received)
Then I am taking from Data Sheet B column for example 123456 and only Received (there might be another 5 statuses, but I only need the one that was received).
That makes all sence to me and works pretty good, but I have to work with more than 500k rows in each sheet. What happens - 500,000 times two cells are combined and searched in Z column in another sheet among another 500,000 for possible match, if nothing found then N/A, and then 2 combination, 3rd, 4th... till 500,000. I added the Display status bar and I see how slowly it goes (only 900 rows per minute, so for one minor mapping it would take more than 10 hours). Here is the sub itself, can anyone share ideas how to improve it to make it work faster? Thanks a million.
Sub incoming_fetch()
Application.ScreenUpdating = False
Dim incr As Long
Dim x As String
n = Sheets("Data").Range("A" & Rows.Count).End(xlUp).Row
Z = Sheets("Incoming_report").Range("D" & Rows.Count).End(xlUp).Row
For i2 = 2 To Z
Sheets("Incoming_report").Range("Z" & i2).Value = Sheets("Incoming_report").Range("C" & i2).Value & Sheets("Incoming_report").Range("H" & i2).Value
Next i2
For i = 3 To n
Application.DisplayStatusBar = True
Application.StatusBar = i
x = Sheets("Data").Range("B" & i).Value & "Received"
If Sheets("Incoming_report").Range("Z:Z").Find(x, lookat:=xlWhole, LookIn:=xlFormulas) Is Nothing Then
Sheets("Data").Range("L" & i) = "N/A"
Sheets("Data").Range("M" & i) = "N/A"
Else
incr = Sheets("Incoming_report").Range("Z:Z").Find(x, lookat:=xlWhole, LookIn:=xlFormulas).Row
Sheets("DATA").Range("L" & i) = Sheets("Incoming_report").Range("I" & incr)
Sheets("DATA").Range("M" & i) = Sheets("Incoming_report").Range("G" & incr)
End If
Next i
End Sub
EDIT2: fixed source columns :
Sub incoming_fetch()
Dim i As Long, n As Long, z As Long, num As Long
Dim x As String
Dim shtIn As Worksheet, shtData As Worksheet
Dim dict As Object, arrC, arrH, arrG, arrI, v, arr, r1, r2
Dim t
Set dict = CreateObject("scripting.dictionary")
Set shtIn = Sheets("Incoming_report")
Set shtData = Sheets("Data")
n = shtData.Range("A" & Rows.Count).End(xlUp).Row
z = shtIn.Range("D" & Rows.Count).End(xlUp).Row
t = Timer
'get all values from Cols C, H, L, M
arrC = shtIn.Range(shtIn.Range("C2"), shtIn.Range("C" & z)).Value
arrH = shtIn.Range(shtIn.Range("H2"), shtIn.Range("H" & z)).Value
arrG = shtIn.Range(shtIn.Range("G2"), shtIn.Range("G" & z)).Value
arrI = shtIn.Range(shtIn.Range("I2"), shtIn.Range("I" & z)).Value
Debug.Print "Get Arrays: " & Timer - t
t = Timer
'create a lookup dictionary of all the ColC values
' (where ColH = "Received")
num = UBound(arrC, 1)
For i = 1 To num
v = arrC(i, 1)
If arrH(i, 1) = "Received" And Len(v) > 0 Then
dict(v) = Array(arrI(i, 1), arrG(i, 1))
End If
Next i
'free up some memory
Erase arrC: Erase arrH: Erase arrI: Erase arrG
Debug.Print "Filled dict: " & Timer - t
t = Timer
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
On Error GoTo haveError
For i = 3 To n
If i Mod 500 = 0 Then Application.StatusBar = i
x = shtData.Range("B" & i).Value
If dict.exists(x) Then
arr = dict(x)
r1 = arr(0)
r2 = arr(1)
Else
r1 = "N/A": r2 = "N/A"
End If
With shtData
.Range("L" & i) = r1
.Range("M" & i) = r2
End With
Next i
Debug.Print "Done: " & Timer - t
haveError:
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub
I have simple problem, but I am not able to find a "fix" for it.
I have the following code:
cmdSQLData.CommandText = Query 'some select
cmdSQLData.CommandType = adCmdText
cmdSQLData.CommandTimeout = 0
Set rs = cmdSQLData.Execute()
j = 1
x = 6 'the line I want the data to start
rs.MoveFirst
Do Until rs.EOF
Sheet1.Range("A" & x).Value = rs![name1]
Sheet1.Range("B" & x).Value = rs![name2]
Sheet1.Range("C" & x).Value = rs![name3]
Sheet1.Range("D" & x).Value = rs![name4]
.
.'lot more columns
.
Sheet1.Range("AC" & x).Value = rs![name28]
If x = 10 Then 'after each 10 lines to create another sheet
x = 6
j = j + 1
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name ="Sheet" & j
End If
Loop
cn.Close
Set cn = Nothing
Set rs = Nothing
Set cmdSQLData = Nothing
I need that when there are more "lines" in the record set than lets say 10, to have another sheet created, Sheet2, where to have the next 10 lines inserted and if the record set still has values to have another sheet created, Sheet3 and so on, until the record set has been fully parsed.
The problem is that for getting the data from record set to excel, i use Sheet1, hardcoded. I tryed to use instead of Sheet1, Sheet[j] or Sheet(j) and to increment "j" each time I create a new sheet, but I get an error, therefore I cannot use Sheet[j].Range("A" & x).Value=rs![name1] or Sheet(j).Range("A" & x).Value=rs![name1].
Any hints?
While I personally believe that collecting all data into one sheet before dividing them into set of x rows is much better, there's another way of going about it using your code. Instead of using Sheet1, use Sheets(j) where j is the sheet index. Also, you can do Sheets("Sheet" & j) as well.
Using the second approach, replace the respective block of your code with the following:
Set rs = cmdSQLData.Execute()
j = 1
x = 6 'the line I want the data to start
rs.MoveFirst
Do Until rs.EOF
With Sheets("Sheet" & j)
.Range("A" & x).Value = rs![name1]
.Range("B" & x).Value = rs![name2]
.Range("C" & x).Value = rs![name3]
.Range("D" & x).Value = rs![name4]
.
.'lot more columns
.
.Range("AC" & x).Value = rs![name28]
End With
x = x + 1
If x = 16 Then 'after each 10 lines to create another sheet
x = 6
j = j + 1
Worksheets.Add(After:=Worksheets(Worksheets.Count)).Name = "Sheet" & j
End If
Loop
You also forgot to increment your x so it will keep on overwriting row 6 again and again. Here, you should be targeting x=10 but x=16 since you start from row 6 based on the initial value of x. The ten rows including row 6 ends in row 15. Once x is equal to 16, it should trigger your IF block.
I have corrected that for you in the above code. Let us know if this helps.
Try this for adding new sheet:
Do Until rs.EOF
If x = 10 Then
Set ws = Thisworkbook.Sheets.Add(After:=Thisworkbook.Sheets(Thisworksheet.Sheets.Count)
ws.Name = "Sheet" & j
x = 6
j = j + 1 ' although you don't really need this, sheet number increment automatically.
End If
ws.Range("A" & x).Value = rs![name1]
.
.
x = x + 1
Loop
i just assumed you only have problem on adding sheet.
Hope this helps.
In my worksheet some cells values are based on other cells
Info worksheet
A1: 5
B1: =A1
Design worksheet
A1:
Is there a way to copy and read the value in B1? I'm trying to use the value in a for loop, with no luck.
Sheets("Info").Select
For i = 1 to 5
If Range("B" & i).Value <> 0 Then
Range("B" & i).Copy Destination:=Sheets("Design").Range("A" & x)
'Sheets("Design").Range("A" & x).Value = Sheets("Offerte").Range("B" & i).Value
x = x + 1
End If
Next i
Your example doesn't seem to match the code well. The line
If Range("B" & i).Value = 1 Then
means that nothing will be copied in your example. It's looking for a cell with 1 in it. Why do you need that If statement at all?
EDIT I am guessing you're just checking that there's something in the cell to copy? I would probably do it this way:
If Trim(Range("B" & i).Value) <> "" Then
Also - did you miss out setting x=1?
There is more than one way to do it. One of them is using 'offset', which is a function that really worth understand. It basically points to a amount of rows / columns from the original cell.
Sub test()
Dim oCell As Excel.Range
Dim i As Integer
Dim x As Integer
Set oCell = Sheets("Info").Range("B1")
x = 1
For i = 1 To 5
If oCell.Offset(i, 0).Value = 1 Then
oCell.Offset(i, 0).Copy Destination:=Sheets("Design").Range("A" & x)
x = x + 1
End If
Next i
End Sub
Besides, you can assert the value instead of using the copy property. Notice it won't work unless x is an integer > 0.
Sub test2()
Sheets(3).Select
x = 1
For i = 1 To 5
If Range("B" & i).Value = 1 Then
Sheets(4).Range("A" & x).Value = Range("B" & i).Value
'Sheets("Design").Range("A" & x).Value = Sheets("Offerte").Range("B" & i).Value
x = x + 1
End If
Next i
End Sub