excel Find copy, paste all values that start with 1Z - excel

I am trying to find a script that finds certain values in sheet1 and paste those values in sheet2 A1.
the range is whole sheet1
needs to search all cells for anything that starts with "1Z" and "W"
paste each cell data that starts with "1Z" and "W" in sheet2 under row A.
Currently have this script:
Sub delete_oldads()
Dim cel As Range, cfind As Range
ActiveSheet.UsedRange.Select
For Each cel In Selection
If cel = "" Then GoTo nextcel
Set cfind = cel.Find(what:="1Z", lookat:=xlPart)
If Not cfind Is Nothing Then
cfind.Copy Cells(cfind.Row, "A")
cfind.Clear
End If
nextcel:
Next cel
End Sub
But this one copy/paste all the matching cells in the same sheet and also if a match is found in the same row, it will copy the last one only.

This does not use FIND() and may be a little slow:
Sub poiuyt()
Dim K As Long, r As Range
Dim sh2 As Worksheet
Set sh2 = Sheets("Sheet2")
K = 1
With Sheets("Sheet1")
For Each r In .UsedRange
v = r.Value
If v <> "" Then
If Left(v, 1) = "W" Or Left(v, 2) = "IZ" Then
r.Copy sh2.Cells(K, 1)
K = K + 1
End If
End If
Next r
End With
End Sub

Related

How to Automate my Manual Selection Process in VBA

I have a manual selection process that I have tried but failed to automate, so I am reaching out for help. I have attached an image of my Excel sheet as a visual guide when reading my process. Excel Snapshot.
I select cell "L2" and run the code below. It finds the first instance of the value within "A2:J1501" and cuts the whole row. It pastes the row onto the sheet named Lineups. Then it highlights each of the values of the cut row in column "L:L" to let me know that value has been used. I then manually select the next non-highlighted value (in the image example it would be "L2") and run the code again, and again, and again, until every row of L:L is highlighted. This process can take some time depending on the number of rows in L:L so I was hoping I can get some help to automate.
Thank you very much.
Sub ManualSelect()
Dim rng As Range
Set rng = Range("A1:J1501")
Dim ac As Range
Set ac = Application.ActiveCell
rng.Find(what:=ac).Select
Range("A" & ActiveCell.Row).Resize(1, 10).Cut
ActiveWindow.ScrollRow = 1
Sheets("Lineups").Select
nextRow = Cells(Rows.Count, 1).End(xlUp).Row + 1
Cells(nextRow, 1).Select
ActiveSheet.Paste
Sheets("Data").Select
Dim wsData As Worksheet
Dim wsLineups As Worksheet
Dim rngToSearch As Range
Dim rngLineupSet As Range
Dim rngPlayerID As Range
Dim Column As Long
Dim Row As Long
Dim LastRow As Long
Set wsData = Sheets("Data")
Set wsLineups = Sheets("Lineups")
Set rngPlayerID = wsData.Range("L2:K200")
Set rngToSearch = rngPlayerID
LastRow = wsLineups.Cells(Rows.Count, 1).End(xlUp).Row
For Row = 2 To LastRow
For Column = 1 To 10
Set rngLineupSet = rngPlayerID.Find(what:=wsLineups.Cells(Row, Column), LookIn:=xlValues)
If Not rngLineupSet Is Nothing Then rngLineupSet.Interior.Color = 65535
Next Column
Next Row
End Sub
This should be pretty close:
Sub ManualSelect()
Dim wsData As Worksheet, c As Range, dict As Object, v, rw As Range
Dim wsLineups As Worksheet, c2 As Range, f As Range
Set dict = CreateObject("scripting.dictionary") 'for tracking already-seen values
Set wsLineups = ThisWorkbook.Worksheets("Lineups")
Set wsData = ThisWorkbook.Worksheets("Data")
For Each c In wsData.Range("L2", wsData.Cells(Rows.Count, "L").End(xlUp))
v = c.Value
If dict.exists(CStr(v)) Then
c.Interior.Color = vbYellow 'already seen this value in L or a data row
Else
'search for the value in
Set f = wsData.Range("A2:J1501").Find(v, lookat:=xlWhole, LookIn:=xlValues, searchorder:=xlByRows)
If Not f Is Nothing Then
Set rw = f.EntireRow.Columns("A").Resize(1, 10) 'A to J
For Each c2 In rw.Cells 'add all values from this row to the dictionary
dict(CStr(c2)) = True
Next c2
rw.Cut Destination:=wsLineups.Cells(Rows.Count, "A").End(xlUp).Offset(1)
c.Interior.Color = vbYellow
Else
'will there always be a match?
c.Interior.Color = vbRed 'flag no matching row
End If
End If 'haven't already seen this col L value
Next c 'next Col L value
End Sub
I believe this should do it (updated):
Sub AutoSelect()
Dim wsData As Worksheet, wsLineups As Worksheet
Dim rng As Range, listIDs As Range
Set wsData = ActiveWorkbook.Sheets("Data")
Set wsLineups = ActiveWorkbook.Sheets("Lineups")
Set rng = wsData.Range("A2:J1501")
'get last row col L to define list
LastRowL = wsData.Range("L" & Rows.Count).End(xlUp).Row
Set listIDs = wsData.Range("L2:L" & LastRowL)
'loop through all cells in list
For i = 1 To listIDs.Rows.Count
myCell = listIDs.Cells(i)
'retrieve first mach in listID
checkFirst = Application.Match(myCell, listIDs, 0)
'only check first duplicate in list
If checkFirst = i Then
'get new row for target sheet as well (if sheet empty, starting at two)
newrow = wsLineups.Range("A" & Rows.Count).End(xlUp).Row + 1
'check if it is already processed
Set processedAlready = wsLineups.Cells(2, 1).Resize(newrow - 1, rng.Columns.Count).Find(What:=myCell, lookat:=xlWhole, LookIn:=xlValues)
'if so, color yellow, and skip
If Not processedAlready Is Nothing Then
listIDs.Cells(i).Interior.Color = vbYellow
Else
'get fist match for value, if any (n.b. "xlWhole" ensures whole match)
Set foundMatch = rng.Find(What:=myCell, lookat:=xlWhole, LookIn:=xlValues)
'checking for a match
If Not foundMatch Is Nothing Then
'get the row
foundRow = foundMatch.Row - rng.Cells(1).Row + 1
'specify target range and set it equal to vals from correct row in rng
wsLineups.Cells(newrow, 1).Resize(1, rng.Columns.Count).Value2 = rng.Rows(foundRow).Value
'clear contents rng row
rng.Rows(foundRow).ClearContents
'give a color to cells that actually got a match
listIDs.Cells(i).Interior.Color = vbYellow
Else
'no match
listIDs.Cells(i).Interior.Color = vbRed
End If
End If
Else
'duplicate already handled, give same color as first
listIDs.Cells(i).Interior.Color = listIDs.Cells(checkFirst).Interior.Color
End If
Next i
End Sub
Also, I think, slightly faster than the other solution offered (because of the nested loop there?). Update: I got a bit confused about the nested loop in the answer by Tim Williams, but I missed that you also want to "accept" the values in the list that matched on a row that is already gone. I fixed this in the updated version by checking if a value that fails to match on the data range has already been transferred to Lineups. Provided that doing so is permissible, this method avoids the nested loop.
I checked both methods for speed (n = 50) on a list (n = 200) for the full data range, ended up with average of 1.70x faster... But maybe speed is not such a big deal, if you're coming from manual labor :)

How to match first 6 characters of a cell instead of the whole cell

Focusing on:
rows = w2.Columns("A:A").Find(What:=aCell, LookAt:=xlWhole).Row
Instead of looking at the whole cell to match (xlwhole), I'm trying to only match the first 6 characters of the cell. I've looked into xlpart and a few other options but have been unsuccessful.
Sub test()
Dim aCell
Dim A, B As Long, rows As Long
Dim w1, w2 As Worksheet
Dim cell As Range
Set w1 = Workbooks("Book1").Sheets("Sheet1")
Set w2 = Workbooks("Book2").Sheets("Sheet1")
A = w1.Cells(w1.Rows.Count, 1).End(xlUp).Row
B = w2.Cells(w2.Rows.Count, 1).End(xlUp).Row
For Each aCell In w1.Range("A2:A" & A)
On Error Resume Next
rows = w2.Columns("A:A").Find(What:=aCell, LookAt:=xlWhole).Row
On Error GoTo 0
If rows = 0 Then
Else
w2.Range("B" & rows).Value = aCell.Offset(0, 1).Value
End If
rows = 0
Next
End Sub
Find supports wildcards *. Use Left$ to get the first 6 characters of the cell value.
For Each aCell In w1.Range("A2:A" & A)
Dim rng As Range
Set rng = w2.Columns("A:A").Find(What:=Left$(aCell.Value, 6) & "*", LookAt:=xlWhole)
' First test if the find succeeded
If Not rng Is Nothing Then
rng.Offset(,1).Value = aCell.Offset(,1).Value
End If
Next

Cut and delete an entire row but paste only the value of a row falling under specific column

I'm trying to cut and delete some entire rows having move it in ID column and paste only the value of that ID to another sheet using a macro. What I've written so far can cut and delete the rows but paste the whole row in another sheet instead of only the IDs.
Sheet1 before running the macro:
Sheet1 after running the macro:
Sheet2 where the value should be placed like:
My following attempt can do the second step flawlessly but when it comes to paste only the value of kicked out IDs in another sheet, it pastes the whole row.
Sub CutAndPaste()
Dim cel As Range
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim wsAno As Worksheet: Set wsAno = ThisWorkbook.Sheets("Sheet2")
For Each cel In Range("C2:C10")
If cel(1, 1) = "move it" Then
ws.Range(cel(1, 1).Address).EntireRow.Cut wsAno.Range("A" & wsAno.Rows.count).End(xlUp).Offset(1, 0)
ws.Range(cel(1, 1).Address).EntireRow.Delete
wsAno.Range("B" & wsAno.Rows.count).End(xlUp).Offset(1, 0).Value = "done"
End If
Next cel
End Sub
How can I cut and delete an entire row but paste only the value of a row falling under ID column in another sheet?
Please, try the next code. It should be very fast, using an array for the ranges to be copied (cut if you like it better...) and deletion is done at once:
Sub CutAndPaste_()
Dim cel As range, rngDel As range, arrCut, lastRAn As Long, k As Long
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim wsAno As Worksheet: Set wsAno = ThisWorkbook.Sheets("Sheet2")
ReDim arrCut(1 To 2, 1 To 9) 'redim the array at a maximum able to keep all the necessary elements (10 -1)
For Each cel In ws.range("C2:C10")
If cel(1, 1).Value = "move it" Then
If rngDel Is Nothing Then 'if range to be deleted does not exist, yet:
k = k + 1
arrCut(1, k) = cel: arrCut(2, k) = "done" 'or cel.Offset(0, 1)
Set rngDel = cel
Else
k = k + 1
arrCut(1, k) = cel: arrCut(2, k) = "done" 'or cel.Offset(0, 1)
Set rngDel = Union(rngDel, cel)
End If
End If
Next cel
lastRAn = wsAno.range("A" & rows.count).End(xlUp).row + 1 'last empty row
ReDim Preserve arrCut(1 To 2, 1 To k) 'keep only the existing filled array elements
wsAno.range("A" & lastRAn).Resize(k, 2).Value = WorksheetFunction.Transpose(arrCut) 'drop the array value at once
rngDel.EntireRow.Delete xlUp 'delete the necessary rows at once
End Sub
I would suggest you to also use a variable to determine the last row in C:C and use it to create the range and also preliminary ReDim the array. It is filled with columns instead of rows, because only the last dimension can be ReDim Preserved..

Search for a match, copy entire row, and paste to corresponding

Col B on "Sheet2" contains 370 rows of data.
Starting with "Sheet2" Cell B1, I want to search for a matching value in Col B on "Sheet1" (it could be located anywhere in the first 300 rows of "Sheet1" Col B).
If a match is found, copy the entire row from "Sheet1" and paste to Row1 on "Sheet2". Then, move to "Sheet2" Cell B2 and repeat the search, this time pasting the entire row from "Sheet1" to Row2 on "Sheet2". Continue moving thru the entire column of data on "Sheet2", searching for each cell's value on "Sheet1". If a search doesn't return a match, then do not paste anything to that row on "Sheet2" and just proceed to search for the next cell on "Sheet2". (For example, if Sheet1 Col B doesn't contain a match for Sheet2 Cell B3, then nothing gets pasted in Sheet2 Row3.)
I have found the following example, which starts to help me, but it specifies the search value and doesn't loop thru the entire column of values like I am attempting to do.
Sub CopyYes()
Dim c As Range
Dim j As Integer
Dim Source As Worksheet
Dim Target As Worksheet
' Change worksheet designations as needed
Set Source = ActiveWorkbook.Worksheets("Sheet1")
Set Target = ActiveWorkbook.Worksheets("Sheet2")
J = 1 ' Start copying to row 1 in target sheet
For Each c In Source.Range("E1:E1000") ' Do 1000 rows
If c = "yes" Then
Source.Rows(c.Row).Copy Target.Rows(j)
j = j + 1
End If
Next c
End Sub
This should do the trick, and do it fast:
Option Explicit
Sub CopyYes()
'You need Microsoft Scripting Runtime library under Tools-References for this
Dim arrPaste As Variant: arrPaste = Sheet2.UsedRange.Value
Dim arrCopy As Variant: arrCopy = Sheet1.UsedRange.Value
Dim MyMatches As New Dictionary: Set MyMatches = CreateDictionary(arrCopy)
Dim i As Long
For i = 1 To UBound(arrPaste)
If arrPaste(i, 2) = vbNullString Then Exit For
If MyMatches.Exists(arrPaste(i, 2)) Then PasteData arrPaste, arrCopy, i, MyMatches(arrPaste(i, 2))
Next i
Sheet2.UsedRange.Value = arrPaste
Erase arrCopy
Erase arrPaste
End Sub
Private Function CreateDictionary(arr As Variant) As Dictionary
Dim i As Long
Set CreateDictionary = New Dictionary
For i = 1 To 300
CreateDictionary.Add arr(i, 2), i
Next i
End Function
Private Sub PasteData(arrPaste As Variant, arrCopy As Variant, i As Long, MyMatch As Long)
Dim j As Long
For j = 1 To UBound(arrCopy, 2)
If arrCopy(MyMatch, j) = vbNullString Then Exit For
arrPaste(i, j) = arrCopy(MyMatch, j)
Next j
End Sub
Use Range.Find to search for your matching cell
Use a Union to create a collection of the rows that are found
Once your loop is finished, copy your range all at once if the Union is not empty
Sub Shelter_In_Place()
Dim Source As Worksheet: Set Source = ThisWorkbook.Sheets("Sheet1")
Dim Target As Worksheet: Set Target = ThisWorkbook.Sheets("Sheet2")
Dim Found As Range, lr As Long
Dim CopyMe As Range
lr = Target.Range("B" & Target.Rows.Count).End(xlUp).Row
For i = 1 To lr
Set Found = Source.Range("B:B").Find(Target.Range("B" & i), LookIn:=xlWhole)
If Not Found Is Nothing Then
If Not CopyMe Is Nothing Then
Set CopyMe = Union(CopyMe, Target.Range("B" & i))
Else
Set CopyMe = Target.Range("B" & i)
End If
End If
Set Fouund = Nothing
Next i
If Not CopyMe Is Nothing Then
CopyMe.EntireRow.Copy
Source.Range("A1").PasteSpecial xlPasteValues
End If
End Sub

Calculation of values based on the color of cells in Excel VBA

The code shows a simple average calculation based on the values in the defined cells. Those cells are highlighted in three colors. The aim is to take the values into the calcuation which cell color is e.g. green. I know the "if" command is needed, I just dont know where excatly to put it in:
Dim wb As Workbook, wq As Object
Dim ws As Worksheet, datdatum
Dim cell As Range, cell2 As Range, col As Long
ws.Range("H104:U104").Formula = "= Average(H34,H39,H68,H71,H83)"
I'll assume that entire rows are not green and that each column needs to be looked at independently.
Loop through each column from H to U. Loop through each cell in each column. Build a union of the cells that are green and average the union. Move on to the next column.
There is no point in building a formula for each column since any change would require rerunning the sub procedure. These will work on both manually set and conditional formatted cell colors.
.DisplayFormat does not work within a User Defined Function.
dim c as long, r as long, rng as range
with worksheets("sheet1")
for c =8 to 21
for r=2 to 103
if .cells(r, c).displayformat.interior.color = vbgreen then
if rng is nothing then
set rng = .cells(r, c)
else
set rng = union(rng, .cells(r, c))
end if
end if
next r
if not rng is nothing then _
.cells(104, c) = application.average(rng)
'alternate
'if not rng is nothing then _
'.cells(104, c).formula = "=average(" & rng.address(0,0) & ")"
next c
end with
Alternate,
dim c as long
with worksheets("sheet1")
if .autofiltermode then .autofiltermode = false
for c =8 to 21
with .range(.cells(1, c), .cells(103, c))
.AutoFilter Field:=1, Criteria1:=vbgreen, Operator:=xlFilterCellColor
.cells(104, c) = application.subtotal(101, .cells)
.AutoFilter
end with
next c
end with

Resources