Background:
I am attempting to convert an old style of managing departments, facilities, and job titles at work to a new more convenient lookup table style format. Right now each set of data for new departments, facilities, and job titles are stored on separate sheets (a couple per week) and the sheet name is a date.
The issue:
The program runs fine for the majority of the workbook; however, I eventually get a
1004 - Application-defined or objected-defined error
When debugging, I find that my row value has to 1,048,577 and therefore caused the error. I am unsure how the iterating value is escaping my handling of it.
The code:
Sub cleanUp()
Dim wks As Worksheet
Dim wksNum As Long
Dim destWks As Worksheet
Dim rng As Range
Dim row As Long
Dim col As Long
Dim destRow As Long
Dim lastRow As Long
Dim itemType As String
Set destWks = ActiveWorkbook.Sheets("new")
destRow = 2
For wksNum = 1 To ActiveWorkbook.Sheets.Count
NextWks:
Set wks = ActiveWorkbook.Worksheets(wksNum)
If wks.Name = "new" Then
wksNum = wksNum + 1
GoTo NextWks
End If
lastRow = wks.Cells(wks.Rows.Count, 1).End(xlUp).row
For row = 1 To lastRow
NextRow:
Select Case wks.Cells(row, 1).Value
Case "New Hospitals"
itemType = "Hospital"
row = row + 1
GoTo NextRow
Case "New Departments"
itemType = "Department"
row = row + 1
GoTo NextRow
Case "New Job Titles"
itemType = "Job Title"
row = row + 1
GoTo NextRow
Case "none"
row = row + 1
GoTo NextRow
Case ""
row = row + 1
GoTo NextRow
End Select
destWks.Cells(destRow, 1).Value = wks.Name
destWks.Cells(destRow, 2).Value = itemType
wks.Range("A" & row & ":C" & row).Copy destWks.Range("C" & destRow)
destRow = destRow + 1
Next row
Next wksNum
End Sub
Example Sheet:
New Hospitals
None
New Departments
10 146 7205-DeptA
10 193 9178-DeptB
New Job Titles
004315 JobTitleA
A side question:
Is there a more elegant way of skipping to the next iteration of a for loop without using a GoTo statement. My thoughts are that these are causing my issue.
Thank you for any and all help.
The problem is if your data on any sheet ends with either of the group headings or none then it will start an eternal loop adding 1 to row until it reaches a row number that is not supported by the worksheet.
You have taken the loop outside the For Loop and so it does not stop.
It is better to test for the opposite and do the code instead of using the archaic Goto.
Use this:
Sub cleanUp()
Dim wks As Worksheet
Dim wksNum As Long
Dim destWks As Worksheet
Dim rng As Range
Dim row As Long
Dim col As Long
Dim destRow As Long
Dim lastRow As Long
Dim itemType As String
Set destWks = ActiveWorkbook.Sheets("new")
destRow = 2
For wksNum = 1 To ActiveWorkbook.Sheets.Count
Set wks = ActiveWorkbook.Worksheets(wksNum)
If wks.Name <> "new" Then
lastRow = wks.Cells(wks.Rows.Count, 1).End(xlUp).row
For row = 1 To lastRow
Select Case wks.Cells(row, 1).Value
Case "New Hospitals", "New Departments", "New Job Titles"
itemType = Replace(wks.Cells(row, 1).Value, "New ", "")
Case "None", ""
Case Else
destWks.Cells(destRow, 1).Value = wks.Name
destWks.Cells(destRow, 2).Value = itemType
wks.Range("A" & row & ":C" & row).Copy destWks.Range("C" & destRow)
destRow = destRow + 1
End Select
Next row
End If
Next wksNum
End Sub
Edited to simplify Select Case based on Scott Holtzman's now deleted answer
Output with provided data:
Related
Hello I am trying to copy a range into a single column. The range is a mix of blank cells and cells with values.I only want to copy and paste the cells with values and I would it to find the first blank cell and want it to walk itself down the column from there.
The code I have right now (besides taking forever) pastes in the first row.
Dim i As Integer
i = 1
ThisWorkbook.Worksheets("amount date").Select
For Row = 51 To 100
For col = 2 To 1000
If Cells(Row, col).Value <> "" Then
Cells(Row, col).Copy
Worksheets("sheet 2").Range("G" & i).PasteSpecial xlPasteValues
End If
Next
Next
Do While Worksheets("sheet 2").Range("G" & i).Value <> ""
i = i + 1
Loop
End Sub
This will work:
Sub qwerty()
Dim i As Long, r As Long, c As Long
i = 1
ThisWorkbook.Worksheets("amount date").Select
For r = 51 To 100
For c = 2 To 1000
If Cells(r, c).Value <> "" Then
Cells(r, c).Copy
Worksheets("sheet 2").Range("G" & i).PasteSpecial xlPasteValues
i = i + 1
End If
Next
Next
End Sub
Perhaps this will be a little faster (even though it seems to have been slow arriving).
Sub CopyRangeToSingleColumn()
' 20 Oct 2017
Dim LastRow As Long
Dim LastClm As Long
Dim Rng As Range, Cell As Range
Dim CellVal As Variant
Dim Spike(), i As Long
With ThisWorkbook.Worksheets("amount date")
With .UsedRange.Cells(.UsedRange.Cells.Count)
LastRow = Application.Max(Application.Min(.Row, 100), 51)
LastClm = .Column
End With
Set Rng = .Range(.Cells(51, "A"), .Cells(LastRow, LastClm))
End With
ReDim Spike(Rng.Cells.Count)
For Each Cell In Rng
CellVal = Trim(Cell.Value) ' try to access the sheet less often
If CellVal <> "" Then
Spike(i) = CellVal
i = i + 1
End If
Next Cell
If i Then
ReDim Preserve Spike(i)
With Worksheets("sheet 2")
LastRow = Application.Max(.Cells(.Rows.Count, "G").End(xlUp).Row, 2)
.Cells(LastRow, "G").Resize(UBound(Spike)).Value = Application.Transpose(Spike)
End With
End If
End Sub
The above code was modified to append the result to column G instead of over-writing existing cell values.
Do you need copy the whole row into one cell, row by row? For each loop shall be faster. I guess, this should work
Sub RowToCell()
Dim rng As Range
Dim rRow As Range
Dim rRowNB As Range
Dim cl As Range
Dim sVal As String
Set rng = Worksheets("Sheet3").Range("$B$51:$ALN$100") 'check this range
For Each rRow In rng.Rows
On Error Resume Next
Set rRowNB = rRow.SpecialCells(xlCellTypeConstants)
Set rRowNB = Union(rRow.SpecialCells(xlCellTypeFormulas), rRow)
On Error GoTo 0
For Each cl In rRowNB.Cells
sVal = sVal & cl.Value
Next cl
Worksheets("sheet4").Range("G" & rRow.Row - 50).Value = sVal
sVal = ""
Next rRow
End Sub
its quick for this range.
I doesnt find a solution for my problem in the WWW.
Hope you can help me:
I've imported a long text file with various information: it looks like this:
id 5
name node1
UPS_serial_number
WWNN 500507680350BD
status online
IO_group_id 0
IO_group_name io_grp0
partner_node_id 4
partner_node_name node2
config_node yes
UPS_unique_id
port_id 500507680456454
port_status active
port_speed 8Gb
port_id 500507680545644
port_status active
port_speed 8Gb
id 4
name node2
UPS_serial_number
WWNN 500507680200DDE8
status online
IO_group_id 0
IO_group_name io_grp0
partner_node_id 4
partner_node_name node1
config_node yes
UPS_unique_id
port_id 5005076803594BDE
port_status active
port_speed 8Gb
port_id 500507680235486F
port_status active
port_speed 8Gb
.
.
.
Its almost formatted in the right format like this:
[string || value]
I want to look in the first block and get the infos for name, id, WWPN - then copy the values to another worksheet.
Then look into the second block and get the same infos: name, id, WWPN and copy them.
Then next block and the next block and so on.
I have the following code:
Sub find_test()
Dim rng As Range
Dim rngCell As Variant
Dim LR As Long
Dim tRow
LR = Cells(1, 1).End(xlDown).Row
Set rng = Range("A1:A" & LR)
For Each rngCell In rng.Cells
tRow = rngCell.Row
If StrComp(rngCell.Value, "name") = 0 Then 'Node 1 Service IP
Worksheets("temp").Range("E16").Value = Worksheets("lsnodecanister").Range("B" & tRow).Value
End If
Next
End Sub
The Text blocks are almost seperated by an empty row.
Do you have any idea?
Hope it was understandable.
Thank you very much,
Best regards,
Kalain
something like
Sub SO1()
Dim lngRow As Long
Dim lngLastRowOfSection As Long
Dim rngFind As Range
Dim strName As String
lngRow = 1
Do Until Cells(lngRow + 1, 1).Value = ""
lngLastRowOfSection = Cells(lngRow, 1).End(xlDown).Row
Set rngFind = Range(Cells(lngRow, 1), Cells(lngLastRowOfSection, 1)).Find("name")
If Not rngFind Is Nothing Then
strName = rngFind.Offset(0, 1).Value
Debug.Print strName
End If
lngRow = Cells(lngLastRowOfSection, 1).End(xlDown).Row
If lngRow >= Rows.Count Then Exit Do
Loop
End Sub
I might have misunderstood the question. I think you meant that each line of your data has the name and data separated by a space. I manipulated your subroutine to put all values in column a into an array and then I split the array into columns B and C.
Sub find_test()
Dim rng As Range
Dim LR As Long
Dim tRow As Long
Dim myArray() As Variant, arrayCounter As Long
Dim lilStringArray
'
LR = ActiveSheet.Cells(ActiveSheet.Rows.Count, "A").End(xlUp).Row
ReDim myArray(1 To 1)
arrayCounter = 1
myArray = Range("A1:A" & LR)
tRow = 1
For i = LBound(myArray) To UBound(myArray)
If myArray(i, 1) <> "" Then
lilStringArray = Split(myArray(i, 1), " ")
Range("B" & tRow).Value = lilStringArray(0)
On Error Resume Next
Range("C" & tRow).Value = lilStringArray(1)
On Error GoTo 0
Else
Range("C" & tRow).Value = ""
End If
tRow = tRow + 1
Next i
End Sub
I have very little experience working with VBA, so I'm having a hard time looking up what I am trying to do because I am having a hard time putting what I am trying to do into words.
I have been struggling to write a code to do the below task for the past few days.
Basically what I am trying to do is to convert a set of data to different format.
This what my source data looks like.
Data:
and I need it to look like this
FinalLook:
I've a already setup a code which is lengthy and incomplete.
FIRST PART
I started with retrieving a part of a data (AQ:BA) and then convert to the format in sheet2 with the below code.
Sub FirstPart()
Dim lastRow As Long
Dim Laaastrow As Long
Sheets("sheet2").Range("a2:A5000").ClearContents
lastRow = Sheets("Sheet1").Range("c" & Rows.Count).End(xlUp).Row
Sheets("Sheet2").Range("A2:A" & lastRow).Value = Sheets("Sheet1").Range("c5:c" & lastRow).Value
Sheets("Sheet2").Range("b2:l" & lastRow).Value = Sheets("Sheet1").Range("aq5:ba" & lastRow).Value
End Sub
But.. the problem i am facing with this code is that it pulls all the data, i do not want it to pull all the values, but only the ones which is not empty or 0. In other words, if AQ6:BA6 is empty, script should skip this particular row and go the next one.
SECOND PART (converting the sheet2 data to the final format)
Sub NormalizeSheet()
Dim wsSheet2 As Worksheet
Dim wsSheet4 As Worksheet
Dim strKey As String
Dim clnHeader As Collection
Dim lngColumnCounter As Long
Dim lngRowCounterSheet2 As Long
Dim lngRowCounterSheet4 As Long
Dim rngCurrent As Range
Dim varColumn As Variant
Set wsSheet2 = ThisWorkbook.Worksheets("Sheet2")
Set wsSheet4 = ThisWorkbook.Worksheets("Sheet4")
Set clnHeader = New Collection
wsSheet4.Range("c2:c5000").ClearContents
wsSheet4.Range("e2:e5000").ClearContents
wsSheet4.Range("g2:g5000").ClearContents
lngColumnCounter = 2
lngRowCounterSheet2 = 1
Set rngCurrent = wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter)
Do Until IsEmpty(rngCurrent.Value)
clnHeader.Add rngCurrent.Value, CStr(lngColumnCounter)
lngColumnCounter = lngColumnCounter + 1
Set rngCurrent = wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter)
Loop
lngRowCounterSheet2 = 2
lngRowCounterSheet4 = 1
lngColumnCounter = 1
Do While Not IsEmpty(wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter))
Set rngCurrent = wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter)
strKey = rngCurrent.Value
lngColumnCounter = 2
Do While Not IsEmpty(wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter))
Set rngCurrent = wsSheet2.Cells(lngRowCounterSheet2, lngColumnCounter)
If rngCurrent.Value = "NULL" Then
Else
wsSheet4.Range("c" & lngRowCounterSheet4).Offset(1, 0).Value = strKey
wsSheet4.Range("e" & lngRowCounterSheet4).Offset(1, 0).Value = clnHeader(CStr(lngColumnCounter))
wsSheet4.Range("g" & lngRowCounterSheet4).Offset(1, 0).Value = rngCurrent.Value
lngRowCounterSheet4 = lngRowCounterSheet4 + 1
End If
lngColumnCounter = lngColumnCounter + 1
Loop
lngRowCounterSheet2 = lngRowCounterSheet2 + 1
lngColumnCounter = 1
Loop
End Sub
I got this code from another thread posted here on stakcoverflow, i modified a bit to get this work.
The problem i am encountering here is that if Sheet2 B2 is empty, the codes doesnt check sheet C2 instead it skips the whole row, which is not right here.
I know this sounds complicated, and this approach of mine may not be even feasible.
Is there ANY OTHER WAY to do this? Is there any other way to get this in a single shot instead of breaking down the data and move each set of columns to sheet2 then to final format?
See how you get on with this. You'll have to adjust range references, and possibly sheet names
Sub x()
Dim r As Long, c As Range
With Sheet1
For r = 5 To .Range("A" & Rows.Count).End(xlUp).Row
For Each c In .Range(.Cells(r, "AQ"), .Cells(r, "BK")).SpecialCells(xlCellTypeConstants)
If c.Value > 0 Then
Sheet2.Range("A" & Rows.Count).End(xlUp)(2).Value = .Range("B1").Value
Sheet2.Range("B" & Rows.Count).End(xlUp)(2).Value = .Cells(r, 1).Value
Sheet2.Range("C" & Rows.Count).End(xlUp)(2).Value = .Cells(r, 2).Value
Sheet2.Range("D" & Rows.Count).End(xlUp)(2).Value = .Cells(3, c.Column).Value
Sheet2.Range("E" & Rows.Count).End(xlUp)(2).Value = .Cells(4, c.Column).Value
Sheet2.Range("F" & Rows.Count).End(xlUp)(2).Value = "(blank)"
Sheet2.Range("G" & Rows.Count).End(xlUp)(2).Value = c.Value
End If
Next c
Next r
End With
Sheet2.Range("A1").Resize(, 7) = Array("TOPHEADER", "HEADER1", "HEADER2", "FROM", "TO", "TYPE", "UNIT")
End Sub
I need to find the first empty row number after the given row number.
please check the image below
for e.g: assume, my current row number is 6 then my output should be 10.
Something like this?
function FindFirstEmpty(i as long)
while cells(i,1).value <> ""
i = i + 1
wend
FindFirstEmpty = i
End function
Depends how you are obtaining the row from which to begin.
Dim startRow As Long
Dim i As Long
Dim lastRow As Long
Dim sh As Worksheet
startRow = 2 'Set first row to check
Set sh = ThisWorkbook.Worksheets("Sheet1")
lastRow = sh.Cells(sh.Rows.Count, "A").End(xlUp).Row
For i = startRow To lastRow
If sh.Cells(i, 1) = "" Then
MsgBox "First empty row: " & i
Exit For
End If
Next i
Have you tried something like this?
Note: This won't show you when the last row is empty.
I use CountA to see if the entire row is empty.
Function FirstEmtpyRow(startRow As Long) As Long
Do
startRow = startRow + 1
If startRow = rpws.Count Then Exit Function
Loop Until WorksheetFunction.CountA(Rows(startRow)) = 0
FirstEmtpyRow = startRow
End Function
You can use .End(xlDown) but you have to be careful that the immediately next cell is not blank or you could skip over it.
dim rw as long, nrw as long
rw = 6
with worksheets("sheet1")
with .cells(rw, "A")
if IsEmpty(.Offset(1, 0)) then
nrw = rw + 1
else
nrw = .end(xldown).offset(1, 0).row
end if
end with
end with
debug.print nrw
I have the below code that works great. It parses through all my sheets and finds the row in column A that I want and pastes it to a specified worksheet. However, I need it to copy the specified row plus the next X number of rows. Can someone help me accomplish this?
Sub FindValues()
Dim ws As Excel.Worksheet
Dim LastRow As Long
Dim i As Integer
For Each ws In Application.ThisWorkbook.Worksheets
LastRow = ws.Cells(Rows.Count, 1).End(xlUp).Row
i = 1
Do While i <= LastRow
If ws.Range("A" & i).Value = "OwnershipType Ownership Type" Then
ws.Rows(i).Copy Sheets("Summary").Range("A2")
i = i - 1
LastRow = LastRow - 1
End If
i = i + 1
Loop
Next
End Sub
You can amend the range of rows being copied on this line like so:
ws.Rows(i & ":" & i + 3).Copy Sheets("Summary").Range("A2")
If the match was found in row 1 for example, the code would render as ws.Rows(1:4).Copy
I have done some minor modifications. Just added (i + number of rows to be copied). Check the below code:
Used Integer copyrw in the code, you can set this integer to copy the number of rows.
Sub FindValues()
Dim ws As Excel.Worksheet
Dim LastRow As Long
Dim i As Integer
Dim copyrw as Integer
copyrw = 3
For Each ws In Application.ThisWorkbook.Worksheets
LastRow = ws.Cells(Rows.Count, 1).End(xlUp).Row
i = 1
Do While i <= LastRow
If ws.Range("A" & i).Value = "OwnershipType Ownership Type" Then
ws.Rows(i & ":" & i + copyrw).Copy Sheets("Summary").Range("A2")
i = i - 1
LastRow = LastRow - 1
End If
i = i + 1
Loop
Next
End Sub