Iterating through row range group data and take an action - vba

I am getting to know Excel VBA. I have a working program that uses an action button on one sheet opens a source workbook and data worksheet, selects data and puts that into a second workbook and destination sheet. I then sort the data as needed and it looks like this
Destination sheet, sorted and annotated duplicates
I am now trying to select the data based on col 2 "B" where the items are duplicated and/or not duplicated then perform an action (send an email to the manager about the staff under their control). I can get an email to work but its selecting the data that I'm having trouble with.
the output data would be col 1 & col 3 to 5 e.g.
Dear Manager1,
you staff member/s listed below have achieved xyz
Person1 22/06/2017 11/08/2017 22/08/2017
Person11 22/06/2017 11/08/2017 22/08/2017
Person15 22/06/2017 11/08/2017 22/08/2017
congratulations....
So what I hope somebody can help me with is a clue how I get to look at the data in col 2
add the Row data required to an array or something then to check the next Row add it to the same something until it is different to the next Row Pause do the action then do the next iteration. Resulting in:
Manager1 .....Person 1,11,15action
Manager10 ..... Person 10action
Manager2 ..... Person 12,16,2,25,28action
Manager3 ..... Person 13,17,26,29,3action
until last line is reached.
I am so confused with arrays / lookups and loops I have lost the plot somewhere along the way.
I have a variable lastTmp which tells me the last line of data in the set, this will vary each month.
The Range is:
Set rng1 = Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).row)
The last piece of my working code is:
Dim lp As Integer
lp = 1
For Each cell In rng1
If 1 < Application.CountIf(rng1, cell.Value) Then
With cell
.Offset(0, 4) = "duplicate : "
.Offset(0, 5) = lp
End With
Else
With cell
.Offset(0, 4) = "NOT duplicate : "
.Offset(0, 5) = 0
End With
End If
Next cell

You will be better placed to confront confusion if you do your indenting more logically. Related For / Next, If / Else / End If and With / End With should always be on the same indent level for easier reading. I rearranged your original code like this:-
For Each Cell In Rng1
If 1 < Application.CountIf(Rng1, Cell.Value) Then
With Cell
.Offset(0, 4) = "duplicate : "
.Offset(0, 5) = lp
End With
Else
With Cell
.Offset(0, 4) = "NOT duplicate : "
.Offset(0, 5) = 0
End With
End If
Next Cell
It now becomes apparent that the With Cell / End With need not be duplicated. I have further presumed that your variable lp actually was intended to hold the count. That made me arrive at the following compression of your code.
Dim Rng1 As Range
Dim Cell As Range
Dim lp As Integer
' the sheet isn't specified: uses the ActiveSheet
Set Rng1 = Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).Row)
For Each Cell In Rng1
With Cell
lp = Application.CountIf(Rng1, .Value)
.Offset(0, 4) = IIf(lp, "", "NOT ") & "duplicate : "
.Offset(0, 5) = lp
End With
Next Cell

Consider using a Dictionary or Collection, whenever, checking for duplicates.
Here I use a Dictionary of Dictionaries to compile lists of Persons by Manager.
Sub ListManagerList1()
Dim cell As Range
Dim manager As String, person As String
Dim key As Variant
Dim dictManagers As Object
Set dictManagers = CreateObject("Scripting.Dictionary")
For Each cell In Range("B5:B" & Cells(Rows.Count, "B").End(xlUp).Row)
manager = cell.Value
person = cell.Offset(0, -1).Value
If Not dictManagers.Exists(manager) Then
dictManagers.Add manager, CreateObject("Scripting.Dictionary")
End If
If Not dictManagers(manager).Exists(person) Then
dictManagers(manager).Add person, vbNullString
End If
Next
For Each key In dictManagers
Debug.Print key & " -> "; Join(dictManagers(key).Keys(), ",")
Next
End Sub
I recommend you wanting Excel VBA Introduction Part 39 - Dictionaries

Assuming your data is as in the image
Then following code will give you result as in the image below.
Sub Demo()
Dim srcSht As Worksheet, destSht As Worksheet
Dim lastRow As Long, i As Long
Dim arr1(), arr2()
Dim dict As Object
Set dict = CreateObject("scripting.dictionary")
Set srcSht = ThisWorkbook.Sheets("Sheet2") 'change Sheet2 to your data sheet
Set destSht = ThisWorkbook.Sheets("Sheet1") 'change Sheet1 to your output sheet
arr1 = Application.Index(srcSht.Cells, [row(1:7000)], Array(2, 1)) 'See note below
arr2 = arr1
For i = 1 To UBound(arr1, 1)
If Not dict.exists(LCase$(arr1(i, 1))) Then
dict.Add LCase$(arr1(i, 1)), i
Else
arr2(i, 1) = vbNullString
arr2(dict.Item(LCase$(arr1(i, 1))), 2) = arr2(dict.Item(LCase$(arr1(i, 1))), 2) & "," & arr1(i, 2)
End If
Next
destSht.Range("A1").Resize(UBound(arr1, 1), UBound(arr1, 2)) = arr2 'display result
destSht.Columns("a").SpecialCells(xlBlanks).EntireRow.Delete
End Sub
Note : For details on assigning range to array see this.

Related

Excel VBA - Get a Cells value by using Offset, based on the data from another column

Column K can contain the string 'Item Cost'. When Column K contains 'Item Cost' I would like to offset to Column U and copy the value from that cell within the same ROW as the string 'Item Cost'.
I can get the code to read and find the value in Column K, but am having a problem with the coping portion of the code for Column U.
Dim range1 As Range
Dim Answer4 As Variant
LstRw = Cells(Rows.Count, "K").End(xlUp).Row
Set List = CreateObject("Scripting.Dictionary")
For Each range1 In wbFrom.Sheets("Sheet0").Range("K9:K" & LstRw)
If range1.Offset(0, 0) = "Item Cost " Then
'MsgBox "found"
Answer4 = range1.Offset(0, 10).Value '<---- PROBLEM
End If
Next
'Msgbox Answer4 'returns nothing
wbTo.Sheets("Sheet1").Range("D10").Value = Answer4 'returns nothing
Seems like you will want to make your destination range dynamic (Range("D10")). The code as is will continousely re-write over your value in D10. Do you maybe want the value to be in Col D on the same row as the target range? If so, swap
wbTo.Sheets("Sheet1").Range("D10") = range1.Offset(0, 10)
for
wbTo.Sheets("Sheet1").Range("D" & range1.Row) = range1.Offset(0, 10)
For Each range1 In wbFrom.Sheets("Sheet0").Range("K9:K" & LstRw)
If range1 = "Item Cost " Then
'MsgBox "found"
wbTo.Sheets("Sheet1").Range("D10") = range1.Offset(0, 10)
End If
Next

Excel Macro Copy Range Paste offset based on cell value

I have two sheets "Data" - which has raw data and "Report" - as Report form .
Report sheet first 5 rows has info.
Data Sheet there 6 columns of Data available (SlNo Name Desig Place gender Category)
Report sheet has first 5 columns only (SlNo Name Desig Place gender)
Report sheet range C5 is dropdown box (List from Category column of Data sheet).
So based on this C5 value get details from Data sheet and paste in Report sheet.
I tried the following code but it pastes the whole row when I want to paste only Name,Desig,Place,gender details in offset and loop...
Sub ViewBtn()
Dim SCHL As String
Dim x As Long
x = 2
Do While Cells(x, 1) <> ""
Sheets("Report").Range(Cells(x, 1).Address, Cells(x, 5).Address).ClearContents
x = x + 1
Loop
Dim id As String
id = ActiveSheet.Range("C5").Value
x = 2
Sheets("Data").Select
Category = id
Do While Cells(x, 1) <> ""
If Cells(x, 4) = Category Then
Worksheets("Data").Rows(x).Copy
Worksheets("Report").Activate
erow = Sheets("Report").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
ActiveSheet.Paste Destination:=Worksheets("Report").Rows(erow)
End If
Worksheets("Data").Activate
x = x + 1
Loop
Application.CutCopyMode = False
Worksheets("Report").Activate
End Sub
Here is some sample code to do what I think you are asking for. It is not necessarily the shortest or cleverest way to do it, but everything is done step by step so I hope it is clear enough to follow easily.
Option Explicit
Private Sub viewBtn_Click()
'// Set references to worksheets
Dim wsReport As Worksheet: Set wsReport = Sheets("Report")
Dim wsData As Worksheet: Set wsData = Sheets("Data")
'// Get the category to be reported
Dim sCategory As String
sCategory = wsReport.Range("C5")
'// Reference first line of the report, in row 8
Dim rFirstReportLine As Range
Set rFirstReportLine = wsReport.Range("A8:E8")
'// Reference the line of the report to be written
Dim rReportLine As Range
Set rReportLine = rFirstReportLine
'// Clear the old report area
Do While rReportLine.Cells(1, 1) <> ""
rReportLine.Clear
Set rReportLine = rReportLine.Offset(1, 0)
Loop
'// Reset to first line of the report
Set rReportLine = rFirstReportLine
'// Find the first cell, if any, that matches the category
Dim rMatch As Range
Set rMatch = wsData.Range("F:F").Find(sCategory, , , xlWhole)
'// Get reference to top data row of data sheet, just the cols to be copied
Dim rDataRow As Range: Set rDataRow = wsData.Range("A1:E1")
'// check for at least one match
If Not rMatch Is Nothing Then
'// Save the address of the first match for checking end of loop with FindNext
Dim sFirstMatchAddress As String: sFirstMatchAddress = rMatch.Address
Do
'// 1) .. copy data row to the report line
rDataRow.Offset(rMatch.Row - 1).Copy rReportLine
'// 2) .. move the report line down
Set rReportLine = rReportLine.Offset(1, 0)
'// 3) .. find the next match on category
Set rMatch = wsData.Range("F:F").FindNext(rMatch)
'// 4) .. exit when we have looped around
Loop Until rMatch.Address = sFirstMatchAddress
End If
'// Display the number of entries found at the end of the report
With rReportLine
Dim nEntryCount As Integer: nEntryCount = .Row - rFirstReportLine.Row
.Cells(1, 1) = nEntryCount & IIf(nEntryCount = 1, " Entry", " Entries")
.Font.Italic = True
.Font.Color = vbBlue
End With
'// Make sure the report sheet is displayed
wsReport.Activate
End Sub
With this data
Get this result

Cannot figure out why VBA code is not entering the If statement

I am trying to sort through accounts in two separate worksheets ("Participaciones Bond" and "Participaciones VAL") and copy the customers that are in both sheets into one column in worksheet "resumen" and the customers that are in one, but not the other, into another column in "resumen".
The part that copies those customers in both sheets works well, but I cannot figure out why the second if statement does not work.
'Patribond= i, patriVal= j
i = 5
j = 5
Do While Worksheets("Participaciones Bond ").Cells(i, "A") <> ""
j = 5
Do While Worksheets("Participaciones VAL ").Cells(j, "A") <> ""
If Worksheets("Participaciones Bond ").Cells(i, 1).Value = Worksheets("Participaciones VAL ").Cells(j, 1).Value Then
Worksheets("Participaciones Bond ").Activate
Sheets("Participaciones Bond ").Select
Worksheets("Participaciones Bond ").Rows(i).Copy Sheets("Resumen").Range("A1048576").End(xlUp).Offset(1, 0)
Exit Do
End If
j = j + 1
'personas en patribond que no aparecen en patrival'
If Worksheets("Participaciones VAL ").Cells(j, 1) = "" Then
Worksheets("Resumen").Activate
'Cells(3, "H").Value = "We got into the second IF"'
Worksheets("Participaciones Bond ").Activate
Range(Cells(i, "A"), Cells(i, "E")).Copy
Worksheets("Resumen").Activate
Range(Cells(i, "G"), Cells(i, "X")).Select
Worksheets("Resumen").Paste
End If
Loop
i = i + 1
Loop
I don't know which data you have for testing, but I think that your code is working just fine, that is: it is entering both If conditions. But the way you check it is by writing into a cell ("H3") that is probabily overwritten when you copy an entire row with the instruction
with Worksheets("Participaciones Bond ").Rows(i).Copy Sheets("Resumen").Range("A1048576").End(xlUp).Offset(1, 0)
This is how I would do it.
You don't need to change selection nor to activate worksheets in order to copy from / to them.
Also, I would copy in the same way both times, for records in both sheets and for records only in the first sheet, not copying the entire row, but limiting the source to the range containing the data. This way you won't accidentally overwrite the columns to the right of the sheet. And your records will get copied at the top of the "Resumen" sheet, too.
In order to do so, I changed
Rows(i).Copy.
to
Range("A" & i, "E" & i).Copy.
I also added references to the three sheets though it isn't necessary.
Dim wBond As Worksheet
Dim wVal As Worksheet
Dim wRes As Worksheet
Set wBond = Worksheets("Participaciones Bond ")
Set wVal = Worksheets("Participaciones Val ")
Set wRes = Worksheets("Resumen")
i = 5
Do While Not IsEmpty(wBond.Cells(i, "A"))
j = 5
Do While Not IsEmpty(wVal.Cells(j, "A"))
If wBond.Cells(i, 1).Value = wVal.Cells(j, 1).Value Then
' La persona está en ambas hojas: copiar en la columna correspondiente
wBond.Range("A" & i, "E" & i).Copy wRes.Cells(Rows.Count, "A").End(xlUp).Offset(1, 0)
Exit Do
End If
j = j + 1
If IsEmpty(wVal.Cells(j, 1)) Then
wBond.Range("A" & i, "E" & i).Copy wRes.Cells(Rows.Count, "G").End(xlUp).Offset(1, 0)
End If
Loop
i = i + 1
Loop
I started writing this code before dinner and got stuck. Now I am well fed but the thread may have progressed beyond what I was doing. Basically, I went by your description and wrote my own code. It's a different approach from the one you took, but then I got stuck on the meaning of your second IF and couldn't figure it out. Please run my code on your data and tell me if its worth continuing.
The code runs through all your names on the Bond sheet and copies data to the Resumen sheet. If a duplicate is found in the Val sheet it copies 10 columns of the Val data (I wonder, is this a logical error) to column A, else it copies 10 columns of data from the Bond sheet (I think both are the same and therefore I would prefer to copy all from the Bond sheet) to column K. The code is simpler than yours and therefore easier to tweak. Take a look. Test it on your data and see what you get.
Sub CopyCustomers()
' 06 Apr 2017
Dim WsBond As Worksheet
Dim WsVal As Worksheet
Dim WsRes As Worksheet
Dim Rl As Long ' WsBond last row
Dim R As Long ' WsBond row
Dim Rv As Long ' found row in WsVal
Dim Rr As Long ' next row in WsRes
Dim Cr As Long ' column in WsRes
Dim Cust As String ' customer name from WsBond
Dim Rng As Range ' range to be copied to WsRes
Set WsBond = Sheets("Participaciones Bond ")
Set WsVal = Sheets("Participaciones VAL ")
Set WsRes = Sheets("Resumen")
Rr = 5
Application.EnableEvents = False
With WsBond
Rl = .Cells(.Rows.Count, 1).End(xlUp).Row
For R = 5 To Rl
Cust = .Cells(R, 1).Value
Rv = 0
On Error Resume Next
Rv = WorksheetFunction.Match(Cust, WsVal.Columns(1), 0)
' no need to copy the entire row of 140K cells (takes too much time)
' in each of the following rows 10 stands for 10 columns being copied
If Err = 0 Then
Set Rng = WsVal.Range(WsVal.Cells(Rv, 1), WsVal.Cells(Rv, 10))
Cr = 1 ' paste to column A
Else
Set Rng = .Range(.Cells(R, 1), .Cells(R, 10))
Cr = 11 ' paste to column K
End If
Rng.Copy Destination:=WsRes.Cells(Rr, Cr).Resize(1, 10)
Rr = Rr + 1
Err.Clear
Next R
End With
Application.EnableEvents = True
End Sub
There might be names in the Val sheet which are not in the Bond sheet. They would be easy to add but that would take another loop, not another IF. You might also not like the arrangement of rows in the Resumen sheet. Easy to tweak. I suppose you might be able to do it yourself. You don't want 10 column, you don't want column A, you don't agree with column K - all easy to adjust. I will be glad to help if you need help.
assuming your data have headers in row 4, you could exploit Autofilter() and go like follows
Option Explicit
Sub main()
Dim commonRng As Range, uniqueBondRng As Range, uniqueValRng As Range
GetCommonAndUniqueData "Participaciones Bond", "Participaciones VAL", commonRng, uniqueBondRng
GetCommonAndUniqueData "Participaciones VAL", "Participaciones Bond", commonRng, uniqueValRng
If Not commonRng Is Nothing Then commonRng.Copy Worksheets("Resumen").Range("a1")
If Not uniqueBondRng Is Nothing Then uniqueBondRng.Copy Worksheets("Resumen").Range("B1")
If Not uniqueValRng Is Nothing Then uniqueValRng.Copy Worksheets("Resumen").Range("C1")
End Sub
Sub GetCommonAndUniqueData(sht1Name As String, sht2Name As String, commonRng As Range, uniqueRng As Range)
Dim cell As Range
With Worksheets(sht1Name)
With .Range("A4", .Cells(.Rows.Count, 1).End(xlUp))
.AutoFilter Field:=1, Criteria1:=GetValues(sht2Name), Operator:=xlFilterValues
With .Offset(1).Resize(.Rows.Count - 1)
If Application.WorksheetFunction.Subtotal(103, .Cells) > 0 Then Set commonRng = .SpecialCells(xlCellTypeVisible)
.Parent.AutoFilterMode = False
If commonRng Is Nothing Then
Set uniqueRng = .Cells
Else
Set uniqueRng = .Cells(.Rows.Count + 1, 1).Resize(1)
For Each cell In .Cells
If Intersect(commonRng, cell) Is Nothing Then Set uniqueRng = Union(uniqueRng, cell)
Next
Set uniqueRng = Intersect(uniqueRng, .Cells)
End If
End With
End With
End With
End Sub
Function GetValues(shtName As String) As Variant
With Worksheets(shtName)
GetValues = Application.Transpose(.Range("A5", .Cells(.Rows.Count, 1).End(xlUp)).Value)
End With
End Function

VBA: adding up irregular ranges

I need some help to create a macro which adds all the values on the column E between the rows with the "avg" word. the result should be displayed on the cells where the "Sum here" label is displayed. Both texts "avg" and "sum here" is just for illustrate the example, "avg" could be replaced by any other word and "sum here" should actually be the aggregation of the values above it.
The real challenge is that the number of ranges on column E is variable, so i would like to find a macro which is able to deal with "n" number of ranges on column E.
Finally, the values on column D are only the example of the expected value on the "sum here" cells.
This is what I have tried to far:
Sub Macro1()
'
' Macro1 Macro
'
Dim sumhere As Range
Dim startingpoint As Range
Dim endingpoint As Range
'
Range("C17").Select
Selection.End(xlDown).Select
If ActiveCell = "avg" Then
ActiveCell.Offset(rowoffset:=0, columnoffset:=2).Select
Set sumhere = ActiveCell
Set startingpoint = ActiveCell.Offset(rowoffset:=-1, columnoffset:=0)
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=sum(range(startingpoint:endingpoint)"
Else
Selection.End(xlUp).Select
If (ActiveCell.Value) = "Sum here" Then
Set endingpoint = ActiveCell.Offset(rowoffset:=1, columnoffset:=0)
sumhere.Formula = "=Sum(Range(startingpoint.adress:endingpoint.adress))"
Else: End If
End If
End If
End Sub
Additionally, as you can see, I do not know, how to define a range using variables. My original idea was to combine this code with some kind of "do while" or/and "for i= 1 to x" and "next i". But I can't see how to combine it.
Using formula only, and providing that column A only has avg (or any text) on each subtotal row.
I've given two versions of the formula - the volatile version (updates everytime you change anything on the spreadsheet), and the non-volatile version (only updates if it needs to).
The formula should be entered on row 6 - change the $E6 to which ever row you need.
(volatile)
=SUM(OFFSET($E6,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)-ROW()+1,,ROW()-1-IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)))
(non volatile):
=SUM(INDEX($E:$E,IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)+1):INDEX($E:$E,ROW()-1))
or if you don't mind using a helper column:
In cell B6:
=IFERROR(LOOKUP(2,1/($A$1:INDEX($A:$A,ROW()-1)<>""),ROW($A$1:INDEX($A:$A,ROW()-1))),0)
In E6: (volatile)
=SUM(OFFSET($E6,$B6-ROW()+1,,ROW()-1-$B6))
or (non volatile):
=SUM(INDEX($E:$E,$B6):INDEX($E:$E,ROW()-1))
Edit:
Thought I'd add a UDF to calculate it to if you're after VBA.
Use the function =AddSubTotal() in the rows you want the sub total to be shown in, or use =AddSubTotal("pop",6) to sum everything in column F (col 6) using "pop" rather than "avg".
Public Function AddSubTotal(Optional Delim As String = "avg", Optional ColNumber = 5) As Double
Dim rCaller As Range
Dim rPrevious As Range
Dim rSumRange As Range
Set rCaller = Application.Caller
With rCaller.Parent
Set rPrevious = .Range(.Cells(1, 1), .Cells(rCaller.Row - 1, 1)).Find(Delim, , , , , xlPrevious)
If Not rPrevious Is Nothing Then
Set rSumRange = rPrevious.Offset(1, ColNumber - 1).Resize(rCaller.Row - rPrevious.Row - 1)
Else
Set rSumRange = .Range(.Cells(1, ColNumber), .Cells(rCaller.Row - 1, ColNumber))
End If
End With
AddSubTotal = WorksheetFunction.Sum(rSumRange)
End Function
The following VBA routine assumes that
your data is in Columns C:E
Nothing else relevant (nothing numeric) in that range
Your "key word" where you want to show the sum is avg
avg (the key word) is hard-coded in the macro
You could easily modify this routine to also perform an average of those values, and put those results, for example, in Column D
Any of the above are easily modified
Option Explicit
Sub TotalSubRanges()
Dim vSrc As Variant, rSrc As Range
Dim dAdd As Double
Dim I As Long
Const sKey As String = "avg"
Set rSrc = Range(Cells(1, "C"), Cells(Rows.Count, "C").End(xlUp)).Resize(columnsize:=3)
vSrc = rSrc
'Do the "work" in a VBA array, as this will
' execute much faster than working directly
' on the worksheet
For I = 1 To UBound(vSrc, 1)
If vSrc(I, 1) = sKey Then
vSrc(I, 3) = dAdd
dAdd = 0
Else
If IsNumeric(vSrc(I, 3)) Then dAdd = dAdd + vSrc(I, 3)
End If
Next I
'write the results back to the worksheet
' and conditionally format the "sum" cells
With rSrc
.EntireColumn.Clear
.Value = vSrc
.Columns(3).AutoFit
.EntireColumn.ColumnWidth = .Columns(3).ColumnWidth
.FormatConditions.Delete
.FormatConditions.Add _
Type:=xlExpression, _
Formula1:="=" & .Item(1, 1).Address(False, True) & "=""" & sKey & """"
With .FormatConditions(1)
.Interior.ColorIndex = 6
End With
End With
End Sub
Surely you just need something like:
Sub sums()
Dim i As Integer, j As Integer, k As Integer
j = Range("C1048576").End(xlUp).Row
k = 1
For i = 1 To j
If Range("C" & i).Value <> "" Then
Range("E" & i).Value = "=Sum(E" & k & ":E" & i - 1 & ")"
k = i + 1
End If
Next i
End Sub
Change:
Dim startingpoint As Range
Dim endingpoint As Range
To:
Dim startingpoint As Variant
Dim endingpoint As Variant
As the startingpoint and endingpoint is used in a formula, you cant define them as a Range.

Copying specific data from a column to a new sheet for reporting

I'm very new to VBA, and I'm trying to move particular items within a column to another sheet for a report.
This is my Macro:
Sub DoIHaveaPRDesignation()
Dim rng As Range
Dim i, Lastrow
Dim splitValues() As String
Lastrow = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
Sheets("Sheet2").Range("A1:I500").ClearContents
Sheets("Sheet2").Cells(1, 1).Value = "Pair"
Sheets("Sheet2").Cells(1, 2).Value = "Commit"
Sheets("Sheet2").Cells(1, 3).Value = "CKID"
Sheets("Sheet2").Cells(1, 4).Value = "Status"
Sheets("Sheet2").Cells(1, 5).Value = "Terminal"
Sheets("Sheet2").Cells(1, 6).Value = "Address"
Set rng = ActiveCell
Dim moveValue As String
Do While rng.Value <> Empty
If InStr(rng.Value, " pr") = 0 Then
MsgBox "Haven't found Pair "
Else
MsgBox rng.Value
End If
Set rng = rng.Offset(1)
rng.Select
Loop
MsgBox "Done!"
End Sub
This is one instance of the data (Column A, Rows 1 - 6):
pr 1 stat RCT commit stat P
sys: type 73RMD no 1 slot: 1 lt: field stat DZ7K co stat NREQ
ckid NONE lp stat RCT 11-30-13 bp/clr 601 tea 1975 W SOUTHPORT RD
type FIXED tec IPLPINPL fld side capr 1975W:279
dist tea 7250 WINSLET BLVD type FIXED addr: 7250 WINSLET BLVD
UNIT 2D serv tea 7250 WINSLET BLVD type FIXED
The code finds the occurance of "pr", but I cannot seem to fidgure out how to pick it up and move it. I need to repeat this for the 6 columns I formatted on sheet 2, but if I get help with the first I can figure out the rest.
Thanks!
This answer discusses features of your existing code that are not recommended and introduces techniques that I believe are relevant to your requirement.
Issue 1
Dim i, Lastrow
The above declares i and Lastrow as variants which can hold anything. For example, the following code is valid:
i = "A"
i = 5
Variants can be very useful but they are slower to access than properly typed variables. I recommend:
Dim i As Long, Lastrow As Long
Issue 2
Sheets("Sheet2").Range("A1:I500").ClearContents
I assume Range("A1:I500") is intended to be larger than the area that was used on a previous run of the macro.
I would write Sheets("Sheet2").Cells.ClearContents and let Excel worry about the range used last time.
Note that ClearContents, as the name implies, only clears the contents. Clear will also clear any formatting. Sheets("Sheet2").Cells.EntireRow.Delete will delete contents and formatting and restore the column widths to their default. However, ClearContents may be adequate for your needs.
Issue 3
Sheets("Sheet2").Range("A1:I500").ClearContents
Sheets("Sheet2").Cells(1, 1).Value = "Pair"
Sheets("Sheet2").Cells(1, 2).Value = "Commit"
Sheets("Sheet2").Cells(1, 3).Value = "CKID"
Sheets("Sheet2").Cells(1, 4).Value = "Status"
Sheets("Sheet2").Cells(1, 5).Value = "Terminal"
Sheets("Sheet2").Cells(1, 6).Value = "Address"
Use of the With statement generally makes your code clearer and faster:
With Sheets("Sheet2")
.Range("A1:I500").ClearContents
.Cells(1, 1).Value = "Pair"
.Cells(1, 2).Value = "Commit"
With .Cells(1, 3)
.Value = "CKID"
.Interior.Color = RGB(0, 240, 240)
End With
.Cells(1, 4).Value = "Status"
.Cells(1, 5).Value = "Terminal"
.Cells(1, 6).Value = "Address"
End With
I have coloured cell C1 to show that With statements can be nested.
Issue 4
Set rng = ActiveCell
As I understand it, the source data is in worksheet Sheet1 and starts at cell A1. The above means your code will start at whatever cell in whatever worksheet the user has positioned the cursor. If there is a fixed starting point then set that in your code. If you do want the user to be able to control the starting point consider:
If ActiveCell.Worksheet.Name <> "Sheet1" Then
Call MsgBox("Please position the cursor to the desired starting " & _
"point in worksheet ""Sheet1""", vbOKOnly)
Exit Sub
End If
Issue 5
Set rng = ActiveCell
:
Set rng = rng.Offset(1)
rng.Select
Accessing a selected cell is much slower than accessing the cell using VBA addressing. I have also seen programmers get hopeless confused about the current location of the cursor when using Offset. You have used VBA addressing to set the header row and I have used it in my sample code below.
Issue 6
Lastrow = Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row
Do While rng.Value <> Empty
You set Lastrow to the number of the last row with a value but your loop moves down the column until it hits an empty cell. If there are no empty rows within the body of the data, this will give the same result. However I suggest you decide which approach is appropriate.
I would avoid the use of Empty. See What is the difference between =Empty and IsEmpty() in VBA (Excel)?.
Sample code
The following code includes the parts relevant to your question. I move the contents of cells containing " pr" to column 1 of worksheet "Sheet2" which is what you seem to be asking. However, if you wanted to split cells containing " pr" and copy selected parts to Sheet2, I would have handled your requirement in a different way. I can add a further section to this answer if you clarify what you seek.
Option Explicit
Sub MovePRRows()
Dim Rng As Range
Dim RowSheet1Crnt As Long
Dim RowSheet1Last As Long
Dim RowSheet2Crnt As Long
Dim WSht2 As Worksheet
Set WSht2 = Worksheets("Sheet2")
WSht2.Cells.EntireRow.Delete
RowSheet2Crnt = 2
With Worksheets("Sheet1")
RowSheet1Last = .Cells(Rows.Count, "A").End(xlUp).Row
For RowSheet1Crnt = 1 To RowSheet1Last
Set Rng = .Cells(RowSheet1Crnt, 1)
If Rng.Value <> "" Then
If InStr(1, Rng.Value, " pr") <> 0 Then
Rng.Copy Destination:=WSht2.Cells(RowSheet2Crnt, 1)
RowSheet2Crnt = RowSheet2Crnt + 1
End If
End If
Next
End With
End Sub