Loop through column that has blanks - excel - excel

I want to know how to loop this a column that blanks inside the column.
I am trying to run a script where if there is a group of a data together, it will make a new column. I got it from here: https://stackoverflow.com/a/15418263/15730901
The problem is that only works for the first column, if I try it a second time on a different column it will stop at the blank because of the loop condition. Is there anyway to change the loop condition to check for the whole column instead of stopping on a blank cell?
Code
sub AddBlankRows()
'
dim iRow as integer, iCol as integer
dim oRng as range
set oRng=range("a1")
irow=oRng.row
icol=oRng.column
do
'
if cells(irow+1, iCol)<>cells(irow,iCol) then
cells(irow+1,iCol).entirerow.insert shift:=xldown
irow=irow+2
else
irow=irow+1
end if
'
loop while not cells (irow,iCol).text=""
'
end sub
Thank you for your time,

Use Range.Find to find the last non-blank cell in the column
lastRow = Columns(iCol).Find("*", SearchOrder:=xlByRows, SearchDirections:=xlPrevious).Row
The your loop becomes
for iRow = lastRow - 1 to firstRow Step -1
if cells(irow + 1, iCol) <> cells(irow,iCol) then
cells(irow + 1,iCol).entirerow.insert shift:=xldown
end if
next iRow

Inserting a Row After a Group of Data
Here's a link to an answer that I posted where the OP was using the same code but wanted it to work for multiple columns. The question has been deleted by the author, so you may not have enough reputation to see it.
A Quick Fix
Option Explicit
Sub AddBlankRows()
Dim rg As Range: Set rg = Range("A1")
Dim r As Long: r = rg.Row
Dim c As Long: c = rg.Column
Dim lRow As Long: lRow = Range("A" & Rows.Count).End(xlUp).Row
Do Until r > lRow
If Len(Cells(r + 1, c).Value) > 0 And Len(Cells(r, c).Value) > 0 _
And Cells(r + 1, c).Value <> Cells(r, c).Value Then
Cells(r + 1, c).EntireRow.Insert Shift:=xlDown
r = r + 2
Else
r = r + 1
End If
Loop
'
End Sub

Related

Insert new rows to separate groups of data via a for loop

I'm using a for loop but I'm open suggestions if there's a better way to separate the data!
I want to insert two new rows whenever the integer in Column 11 or "K" changes. Column K represents groups of data and each is named with integers between 1 and 10 (inclusive). Each group varies in size, hence why I wanted a for loop to check each time the group increments to trigger the insertion of the rows.
For example:
From the data below two blank rows should be inserted below K11 and below K18. This will result in the data being separated by two blank rows whenever two groups were 'touching' each other.
K2 = 1, K3 = 1, K4 = 1 ... K11 = 1
K12 = 2, K13 = 2, K14 = 2... K18 = 2
K19 = 3, K20 = 3 ...
I've put together the following for loop but it inserts 500 (the counter limit) rows after the first group and no row inserts for the remaining groups. Can you explain why this happens and how I can work around this?
Dim LCounter As Integer
For LCounter = 2 To 500
If Cells(LCounter + 1, 11).Value <> Cells(LCounter, 11) Then
Rows(LCounter + 1).Insert shift:=xlShiftDown
End If
Next LCounter
Try this way, please. It should be very fast even for big ranges:
Sub SeparateGroupsByEmptyRows()
Dim LCounter As Long, col As Long, rng As Range
col = 11
For LCounter = 2 To 500
If cells(LCounter + 1, col).Value <> cells(LCounter, col).Value Then
If rng Is Nothing Then
Set rng = cells(LCounter + 1, col)
Else
Set rng = Union(rng, cells(LCounter + 1, col))
End If
End If
Next LCounter
'For the case of two or more consecutive groups of only one row each:
If InStr(rng.Address(0, 0), ":") > 0 Then Set rng = makeDiscontinuu(rng)
rng.EntireRow.Insert Shift:=xlDown
End Sub
Function makeDiscontinuu(rng As Range) As Range
Dim A As Range, c As Range, strAddress As String
For Each A In rng.Areas
If A.cells.count = 1 Then
strAddress = strAddress & A.Address(0, 0) & ","
Else
For Each c In A.cells
strAddress = strAddress & c.Address(0, 0) & ","
Next c
End If
Next A
Set makeDiscontinuu = Range(left(strAddress, Len(strAddress) - 1))
End Function
try this, should be one empty row separation (not tested)
Dim LCounter As Integer, lcEnd as integer: lcEnd =500
For LCounter = 2 To lcEnd
If Cells(LCounter + 1, 11).Value <> Cells(LCounter, 11) and Cells(LCounter + 1, 11)<> "" Then
Rows(LCounter + 1).Insert shift:=xlShiftDown
lcEnd =lcEnd +1
End If
Next LCounter
Insert Rows Before Change of Cell Value
The first procedure uses For...Next to solve the problem by looping backwards.
The second procedure uses Do...Loop illustrating the complications when looping forwards.
The Code
Option Explicit
Sub insertBeforeChangeForNext()
Const iRows As Long = 2 ' Number of Rows to Insert
Const cCol As Long = 11 ' Criteria Column
Const fRow As Long = 2 ' First Row
' Either...
Const lRow As Long = 500 ' Last Row
' ...or rather determine the last non-empty row:
'Dim lRow As Long: lRow = Cells(Rows.Count, cCol).End(xlUp).Row ' LR
If lRow <= fRow Then
Exit Sub
End If
Application.ScreenUpdating = False
Dim LCounter As Long ' Rows Counter
For LCounter = lRow - 1 To fRow Step -1
If Cells(LCounter + 1, cCol).Value <> Cells(LCounter, cCol).Value Then
Rows(LCounter + 1).Resize(iRows).Insert
End If
Next LCounter
Application.ScreenUpdating = True
End Sub
Sub insertBeforeChangeDoLoop()
Const iRows As Long = 2 ' Number of Rows to Insert
Const cCol As Long = 11 ' Criteria Column
Const fRow As Long = 2 ' First Row
' Either...
Const lRow As Long = 500 ' Initial Last Row
' ...or rather determine the last non-empty row:
'Dim lRow As Long: lRow = Cells(Rows.Count, cCol).End(xlUp).Row ' ILR
Dim Current As Long: Current = fRow ' Current Row
Dim Last As Long: Last = lRow ' Current Last Row
Application.ScreenUpdating = False
Do While Current < Last
If Cells(Current + 1, cCol).Value <> Cells(Current, cCol).Value Then
Rows(Current + 1).Resize(iRows).Insert
Last = Last + iRows
Current = Current + iRows
End If
Current = Current + 1
Loop
Application.ScreenUpdating = True
End Sub

How can I compare cells in different rows and insert-right if lower cell if not the same?

How can I compare cells in two rows and and match the lower cell value to the upper cell value, if the two are different? Here is some the same data I am working with and what I hope to see after the code runs.
Before:
[ID_GLOBAL], [ID_UNIQUE], [ID_REAL], [ID_1], [ID_2], [GAS_ADJ], [OBJ_ADJ]
[ID_GLOBAL], [ID_UNIQUE], [ID_REAL], [GAS_ADJ], [OBJ_ADJ]
After:
[ID_GLOBAL], [ID_UNIQUE], [ID_REAL], [ID_1], [ID_2], [GAS_ADJ], [OBJ_ADJ]
[ID_GLOBAL], [ID_UNIQUE], [ID_REAL], NULL AS [ID_1], NULL AS [ID_2], [GAS_ADJ], [OBJ_ADJ]
I think the code will basically look like this, but I haven't gotten the insert-right working properly.
Sub CompareCellsDiffRows()
Dim bothrows As Range, i As Integer
Set bothrows = Selection
With bothrows
For i = 1 To .Columns.Count
If Not StrComp(.Cells(1, i), .Cells(2, i), vbBinaryCompare) = 0 Then
' magic happens here
End If
Next i
End With
End Sub
As you may have guessed, I'm dealing with hundreds of fields in several tables and trying to Union everything together, so all these field names have to match up in the correct order.
Thanks.
I am assuming, as per your example, that the first row is the one that will be always complete.
Sub CompareRowDifferences()
Dim sht As Worksheet
Dim i, LastColumn As Long
Set sht = ThisWorkbook.Worksheets("Transposed Fields")
LastColumn = sht.Cells.SpecialCells(xlLastCell).Column
With sht
For i = 1 To LastColumn
If StrComp(.Cells(1, i), .Cells(2, i), vbBinaryCompare) <> 0 Then
.Cells(2, i).Insert Shift:=xlToRight
.Cells(2, i).Value2 = "NULL AS " & .Cells(1, i).Value2
End If
Next i
End With
End Sub
Hope it helps
Regarding your second question (if they are not ordered) and assuming always that the first line is the ones that rules...
Sub CompareRowDifferences()
Dim sht As Worksheet
Dim i, j, LastColumn As Long
Set sht = ThisWorkbook.Worksheets("Transposed Fields")
LastColumn = sht.Cells.SpecialCells(xlLastCell).Column
j = 0
With sht
For i = 1 To LastColumn
Test = Application.WorksheetFunction.CountIf(Range _
(Cells(2, i), Cells(2, LastColumn + j)), .Cells(1, i).Value2)
If Test >= 1 Then
.Cells(2, i).Insert Shift:=xlToRight
.Cells(2, i).Value2 = .Cells(1, i).Value2
Else
.Cells(2, i).Insert Shift:=xlToRight
.Cells(2, i).Value2 = "NULL AS " & .Cells(1, i).Value2
End If
j = j + 1
Next i
Range(Cells(2, LastColumn), Cells(2, LastColumn + j)).ClearContents
End With
End Sub
This procedure identifies and uses the row with a higher number of fields (i.e. no-empty cells), and uses it as "model" to update the other row regardless of the position of the fields in the other row.
Sub Headers_Comparison(rInput As Range)
Dim aOut As Variant, aSrc As Variant, aTrg As Variant
Dim bMatch As Byte, bRow As Byte, b As Byte
With WorksheetFunction
Rem Validate Fields in Rows
If .CountA(rInput.Rows(1)) > .CountA(rInput.Rows(2)) Then
bRow = 2
aSrc = .Transpose(.Transpose(rInput.Rows(1).Value2))
aTrg = .Transpose(.Transpose(rInput.Rows(2).Value2))
Else
bRow = 1
aSrc = .Transpose(.Transpose(rInput.Rows(2).Value2))
aTrg = .Transpose(.Transpose(rInput.Rows(1).Value2))
End If
aOut = aTrg
For b = 1 To UBound(aSrc)
bMatch = 0
On Error Resume Next
bMatch = .Match(aSrc(b), aTrg, 0)
On Error GoTo 0
aOut(b) = IIf(bMatch > 0, vbNullString, "NULL AS ") & aSrc(b)
Next: End With
rInput.Rows(bRow).Value = aOut
End Sub
It should be called in this manner:
Call Headers_Comparison(rSel) 'update with required range
I think I just figured it out!
Sub CompareRowDifferences()
Dim i As Integer
Dim sht As Worksheet
Dim LastColumn As Long
Set sht = ThisWorkbook.Worksheets("Transposed Fields")
LastColumn = sht.UsedRange.Columns(sht.UsedRange.Columns.Count).Column
For i = 1 To LastColumn
If Not StrComp(sht.Cells(1, i), sht.Cells(2, i), vbBinaryCompare) = 0 Then
' magic happens here
Set Rng = sht.Cells(2, i)
Rng.Insert Shift:=xlToRight
sht.Cells(2, i).Value = "NULL AS " & sht.Cells(1, i).Value
End If
Next i
End Sub
This seems to work. Although, this is a pretty simple solution. I understand it would be much more complex if the order of names the lower row changed. This works ONLY because the names in row 2 match the names in row 1, there are just fewer names. I would love to see what the code would look like if the order of the row 2 names was switch around, compared to the row 1 names.

Copy rows between two values VBA and paste to new sheet loop

I am trying to figure out some code here, I have looked on a few sites now, including here and it almost works but it is most likely my datasheet that is causing the issue.
This: Search for two values and copy everything in between in a loop
and this: I need code to copy between two rows and paste into the another sheet with our giving any values?
Would probably work, however the first value cannot be found. Let me explain.
I have an exported report from a website, it groups the totals with a name (value 1) and then the word totals for: (word 2).
What I need it to do is only copy and paste where value 1 is met , and value 2 will always be "totals for:".
Problem is with this loop is that there are blanks between each group of data, so it finds the first "totals for:" but cannot find my first value because it is between about 20 blank cells. (19 groups of data - with a blank row between each group).
How can i retro fix the above codes so that it keeps going down the rows, regardless of blanks to find the first value, then find the second value. Copy that range to a new sheet, and repeat this with a new value 1?
Sub MoveRows()
Dim rownum As Integer
Dim colnum As Integer
Dim startrow As Integer
Dim endrow As Integer
rownum = 1
colnum = 1
With ActiveWorkbook.Worksheets("Sheet1")
Do
If .Cells(rownum, 1).Value = "LIFEC - Supp Life - Ch" Then
startrow = rownum
End If
rownum = rownum + 1
Loop Until .Cells(rownum, 1).Value = "Totals for:"
endrow = rownum
ActiveWorkbook.Worksheets("Sheet1").Range(startrow & ":" & endrow).Copy
End With
ActiveWorkbook.Worksheets("Sheet2").Paste
End Sub
Sub Star123()
Dim rownum As Long
Dim colnum As Long
Dim startrow As Long
Dim endrow As Long
Dim lastrow As Long
rownum = 1
colnum = 1
lastrow = Worksheets("Sheet1").Range("A65536").End(xlUp).Row
With ActiveWorkbook.Worksheets("Sheet1").Range("A1:A" & lastrow)
For rownum = 1 To lastrow
Do
If .Cells(rownum, 1).Value = "LIFEC - Supp Life - Ch" Then
startrow = rownum
End If
rownum = rownum + 1
If (rownum > lastrow) Then Exit For
Loop Until .Cells(rownum, 1).Value = "Totals for:"
endrow = rownum
rownum = rownum + 1
Worksheets("Sheet1").Range(startrow & ":" & endrow).Copy
Sheets("Sheet2").Select
Range("A1").Select
ActiveSheet.Paste
Next rownum
End With
End Sub
I attached the codes that almost work, but cannot find my first value.
You can use Find method which looks something like:
Dim s As Range, e As Range
With Sheet1 'or this can be any other sheet where you search
Set r = .Range("A:A").Find("Whatever you want found")
If Not r Is Nothing Then
Set e = .Range("A:A").Find("The other end", r)
If Not e Is Nothing Then
.Range(r, e).EntireRow.Copy Sheet2.Range("A1") 'or to whatever sheet
End If
End If
End With
You can then have this in a loop which replaces the strings you want found. HTH.

VBA loop omitted while executing through F5, but executes on step by step execution using F8

I have some values (derived out of a formula) in a particular row in an excel sheet. I am trying to drag them down till a certain number of rows using .FillDown method of Range looping through all the columns.
Sub RawDataPreparation()
Dim v As Long
Dim LastRow As Long
Dim lastCol As Integer
Dim c As Integer
c = ThisWorkbook.Sheets("Metadata").Range("B10")
Dim ColName() As String
ReDim ColName(c)
Dim strFormulas() As Variant
ReDim strFormulas(c)
Dim lastColumn As Integer
With ThisWorkbook.Sheets("Raw Data")
lastColumn = .Cells(1, .Columns.Count).End(xlToLeft).Column
End With
For i = 0 To c - 1
On Error Resume Next
ColName(i) = ThisWorkbook.Sheets("Metadata").Range("C" & i + 10) 'copy the value corresponding to column C10:C18 in Metadata sheet to the array
ThisWorkbook.Sheets("Raw Data").Cells(1, lastColumn + 1 + i).Value = ColName(i)
Next i
With ThisWorkbook.Sheets("Raw Data")
LastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
For j = 0 To c - 1
On Error Resume Next
strFormulas(j) = ThisWorkbook.Sheets("Metadata").Range("D" & j + 10) 'copy the formulas corresponding to column D10:D18 in Metadata sheet to the array
With ThisWorkbook.Sheets("Raw Data")
.Cells(2, lastColumn + 1 + j).Formula = strFormulas(j)
End With
Next j
'Code to drag down the formula till last row.
For k = 0 To c - 1
ThisWorkbook.Sheets("Raw Data").Range(Cells(2, lastColumn + 1 + k), Cells(LastRow, lastColumn + 1 + k)).FillDown
Next k
End Sub
When I execute using F8 (step by step) the last loop (to drag the formula till last row) is being executed and giving intended result. But upon executing the entire Sub RawDataPreparation using F5, the last loop is getting omitted.
I am not able to understand this behavior. Can anyone suggest why this is happening?
You need to qualify references to Cells, Range, Rows, etc unless you KNOW that your ActiveSheet is the sheet being referred to.
So change your last statement to
ThisWorkbook.Sheets("Raw Data").Range(ThisWorkbook.Sheets("Raw Data").Cells(2, lastColumn + 1 + k), ThisWorkbook.Sheets("Raw Data").Cells(LastRow, lastColumn + 1 + k)).FillDown
Or, to make it a bit easier to read:
With ThisWorkbook.Sheets("Raw Data")
.Range(.Cells(2, lastColumn + 1 + k), .Cells(LastRow, lastColumn + 1 + k)).FillDown
End With
In order to make your code work the last loop should define the range somewhat like this:-
Dim k As Long
For k = 0 To C - 1
With ThisWorkbook.Sheets("Raw Data")
.Range(.Cells(2, lastColumn + 1 + k), .Cells(lastRow, lastColumn + 1 + k)).FillDown
End With
Next k
Observe the period before the Cells defining the range. This period makes the references refer to the "Raw Data" sheet, whereas in your code, in the absence of any spacification, they refer to the ActiveSheet. So you get a different result not depending upon how you trigger the code but which is the active sheet at the time.
Anyway, to prove my point I had to almost rewrite your code. I continued to finish the job, and here it is.
Sub RawDataPreparation()
' 24 Mar 2017
Dim WsMetadata As Worksheet
Dim WsRawData As Worksheet
Dim firstMetaRow As Long, TransferRow As Long
Dim nextCol As Integer
Dim lastRow As Long
Dim R As Long, C As Long
firstMetaRow = 10 ' set this value here instead of in the code
Set WsMetadata = ThisWorkbook.Sheets("Metadata")
TransferRow = CLng(Val(WsMetadata.Range("B10").Value)) - 1
' B10 can't have a value < 1. In your example it should be 9
If TransferRow < 0 Then Exit Sub
Set WsRawData = ThisWorkbook.Sheets("Raw Data")
With WsRawData
nextCol = .Cells(1, .Columns.Count).End(xlToLeft).Column + 1
End With
' transpose columns C and D of WsMetaData to rows 1 and 2 in WsRawData
For R = firstMetaRow To (firstMetaRow + TransferRow)
With WsMetadata
.Cells(R, "C").Copy Destination:=WsRawData.Cells(1, (nextCol + R - firstMetaRow))
.Cells(R, "D").Copy Destination:=WsRawData.Cells(2, (nextCol + R - firstMetaRow))
End With
Next R
With WsRawData
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
.Range(.Cells(2, nextCol), .Cells(lastRow, nextCol + TransferRow)).FillDown
End With
End Sub
It works, but unfortunately it may not do the job you envisioned. The formulas you copy from the Metadata contain references to that sheet which can't be translated 1:1 once the range is transposed. You may have to settle for pasting values or translate the formulas before filling them down.

Remove duplicate values within dynamic ranges identified by text strings

Text “endofdata” in col B identifies the boundaries of multiple ranges on a single sheet. I’m trying to step through each range and remove duplicate values in columns E and F within each range. I also call a routine that deletes blank rows that are generated when duplicates are removed. The bottom row with “endofdata” is always removed when .removeduplicates is executed.
I’ve tried the Do loop but it’s failing. (It works for the first range but fails for the next range) Please suggest how to make this work. What kind of loop should I use? How should I search for “endofdata” string? Thank you very much in advance.
Sub RemoveDupsinRange()
Dim LastRow As Long, i As Long, startRow, EndRow
Call setSheets
LastRow = wsQC.Cells(wsQC.Rows.Count, "A").End(xlUp).Row
Debug.Print LastRow
For i = LastRow To 1 Step -1
Do
If wsQC.Cells(i, 2).Value = "endofdata" Then
startRow = i
End If
i = i - 1
Loop Until wsQC.Cells(i, 2).Value = "endofdata"
EndRow = i
i = i - 1
Range(startRow & ":" & EndRow).Select
Selection.removeduplicates Columns:=Array(5, 6), _
Header:=xlNo
Call DeleteBlanks
Next i
End Sub
I just tested this loop and it worked.
Sub RemoveDupsinRange()
Dim LastRow As Long, i As Long, rStart As Range, rEnd As Range
Call setSheets
LastRow = wsQC.Cells(wsQC.Rows.Count, "A").End(xlUp).Row
Debug.Print LastRow
Set rEnd = wsQC.Cells(LastRow, 2)
For i = LastRow To 2 Step -1
Do
i = i - 1
If wsQC.Cells(i, 2).Value = "endofdata" Then
Set rStart = wsQC.Cells(i, 2)
End If
Loop Until wsQC.Cells(i, 2).Value = "endofdata"
wsQC.Range(rStart.Offset(, -1), rEnd.Offset(, 4)).RemoveDuplicates Columns:=Array(5, 6), Header:=xlNo
Set rEnd = rStart
Call DeleteBlanks
Next i
End Sub

Resources