Copy cells from specified column, removing duplicates - vba

I'm newbie in VBA, what I need to do is to copy rows from specified column into a column on the other worksheet, but I want to copy just one occurance of each word, for example
Column "F"
dog
dog
cat
dog
In the result I need to have new Worksheet called "Animals" with:
Column "A" Column "B"
1 dog
2 cat

Here is a sub routine that will do exactly what you want: slap a list of unique elements in Sheet1 column F into column A of sheet2 and rename the sheet "animals". You could tweak this so that instead of it changing the name of sheet2 it can create a new sheet if you like.
Sub UniqueList()
Application.ScreenUpdating = False
Dim lastRow As Long
Dim i As Long
Dim dictionary As Object
Set dictionary = CreateObject("scripting.dictionary")
Sheet1.Activate
lastRow = Sheet1.Cells(Rows.count, "F").End(xlUp).row
On Error Resume Next
For i = 1 To lastRow
If Len(cells(i, "F")) <> 0 Then
dictionary.Add cells(i, "F").Value, 1
End If
Next
Sheet2.range("a1").Resize(dictionary.count).Value = _
Application.Transpose(dictionary.keys)
Application.ScreenUpdating = True
MsgBox dictionary.count & " unique cell(s) were found and copied."
End Sub
How it works: I use a dictionary file, which will automatically take out any dupes, then slap the list of entries into sheet2.

Do you need to do this in VBA at all?
If you just want to get a unique copy of your list, select the unsorted, non-unique column contents including the header, then hit the Advanced... button on the Sort and Filter pane of the Data ribbon. You can ask it to copy to another location and tick Unique records only.
Recording this activity and looking at the VBA, this is how it looks:
Range("A1:A4").AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("B1"), Unique:=True

here is a solution:
Option Explicit
Sub copyNoDuplicates()
Dim rLastCell As Range
Dim cell As Range, i As Long
Dim cAnimals As Collection
Set cAnimals = New Collection
With ActiveWorkbook.Worksheets("Sheet1")
'Find last used cell
Set rLastCell = .Range("F65536").End(xlUp)
'Parse every animal and put it in a collection
On Error Resume Next
For Each cell In .Range("F2:F" & rLastCell.Row)
cAnimals.Add cell.Value, CStr(cell.Value)
Next cell
On Error GoTo 0
End With
With ActiveWorkbook.Worksheets("Sheet2")
For i = 1 To cAnimals.Count
.Range("A" & i).Value = i
.Range("B" & i).Value = cAnimals(i)
Next i
End With
End Sub

Related

Find non zero value in column G on sheet 1, return value of Column C in that row to sheet 2 (VBA)

My first sheet is set up like this:
I want to find the non zero values in column G. Then I want to read the corresponding name in column C. I, then, want to return the value of the name to a cell on Sheet 2.
At this point, it doesn't matter what cell it returns to in sheet 2. It sounds like a VLOOKUP or INDEXMATCH but my VBA isn't good enough to figure out the formatting of it. This is some code that I tried and I can get it to return the name. But I don't know how to do it for all non zeros or how to have it print to sheet 2. Need a loop or need to figure out look ups!
code:
For Each c In Range("G6").Cells
If c.Value > 0 Then
PlayerName = Range(Cells(Selection.Row, 3).Address).Value
End If
Exit For
Next c
The following code will find the first row which has a number greater than 0 in column G (starting at row 6), and write the value in column C of that row to cell X5 of Sheet2.
With Worksheets("Sheet1")
For Each c In .Range("G6", .Cells(.Rows.Count, "G").End(xlUp)).Cells
If c.Value > 0 Then
Worksheets("Sheet2").Cells(5, "X").Value = c.Offset(0, -4).Value
Exit For ' Moved this inside the `If`, otherwise it will exit as soon as
' the first cell in the range is processed, irrespective of whether
' it was greater than 0 or not
End If
Next c
End With
Iterative version:
Dim s2Row as Long
s2Row = 5
With Worksheets("Sheet1")
For Each c In .Range("G6", .Cells(.Rows.Count, "G").End(xlUp)).Cells
If c.Value > 0 Then
Worksheets("Sheet2").Cells(s2Row, "X").Value = c.Offset(0, -4).Value
s2Row = s2Row + 1
End If
Next c
End With
Here is the logic you'll need. Will you be able to build the macro with this logic? It will help you understand how to maneuver rows that are greater than zero. Then you copy the column on that row y9ou need and paste it to the other sheet.
Sub macro1()
Dim myRng As Range, lastRow As Long
lastRow = ActiveSheet.Range("G65536").End(xlUp).Row
Set myRng = Sheet1.Range("G1:G" & lastRow)
For Each Rng In myRng
If IsNumeric(Rng.Value) And Rng.Value > 0 Then
Debug.Print "Cell " & Rng.Address & " has the number " & Rng.Value & " in row " & Rng.Row
End If
Next Rng
End Sub
Yes, except "G" is a column, not a row. Replace the debug.print line with WorkSheets("sheet name to copy from here").Rows(rng.row).Copy Destination:=WorkSheets("sheet name to copy to here").Range("A" & rowCounterVariable). Of course, change the sheet names to your actual sheet names.
Here I set the first row at 2 on the page to copy to. If you need to set it to the first available row then you need to research how to find the last used row on that page. Put these exact terms into Google "VBA EXCEL HOW TO FIND LAST USED ROW". I have an example of finding the last used row for the activesheet inside the code. We could give you fish today, and teach you how to fish. But you need to catch your own. We're not here to write code for you.
Sub macro2()
Dim myRng As Range, lastRow As Long, rowCounterVariable as long
rowCounterVariable = 2
lastRow = ActiveSheet.Range("G65536").End(xlUp).Row
Set myRng = Sheet1.Range("G1:G" & lastRow)
For Each Rng In myRng
If IsNumeric(Rng.Value) And Rng.Value > 0 Then
WorkSheets("sheet name to copy from here").Rows(rng.row).Copy Destination:=WorkSheets("sheet name to copy to here").Range("A" & rowCounterVariable)
rowCounterVariable = rowCounterVariable + 1
End If
Next Rng
End Sub

Copy paste if contains certain value between three column

I'm wondering about how to use "worksheet-change" the best way possible. Right now I use it to copy from one column to another column, in two different sheets. Whenever the column in Sheet1 is updated, column in Sheet2 will as well be updated. Using two columns is no problem and the code works fine!
My issue is whenever I want to use three columns. I want it to loop through column A and whenever it finds the word "Orange" in it, it should copy column B to coulmn A in sheet2.See my sheet for more detailed information.
If it finds Orange, it should only copy and updated the values "1,3,6" in Column B to column A in sheet2.
A code I tried with but didn't work, it copied everything to column B. If it is possible to use VLOOKUP, how do I do that? Because I tried that but it didnt updated whenever a cell was changed.
Dim x As Range
With Sheets("Sheet1")
Set x = .Columns(1).Find("Orange", LookIn:=xlValues, lookat:=xlWhole)
If Not x Is Nothing Then
.Columns(2).Copy Sheets("Sheet2").[B1]
End If
Set x = Nothing
End With
Example:
Workbook 1:
Column A
Orange
apple
Orange
Pear
Berry
Orange
Column B:
1
2
3
4
5
6
Should populate into a new sheet where only "1,3,6" is pasted in column B sheet2
I think you're better served by using the Workbook_SheetDeactivate event. By using this event (only when the user selects a sheet away from your source sheet), you're only performing the copy once. As an alternate and/or addition, you could perform the same copy from the Workbook_BeforeSave event (just in case the user saves and exits the workbook without changing sheets).
Option Explicit
Private Sub Workbook_SheetDeactivate(ByVal Sh As Object)
Dim activeRange As Range
Dim lastRow As Long
Dim c As Range
If Sh.Name = "Sheet1" Then
lastRow = Sh.Range("A" & Rows.Count).End(xlUp).Row
Set activeRange = Sh.Range("A1:A" & lastRow)
For Each c In activeRange
If c.Value = "Orange" Then
Sheets("Sheet2").Range(c.Offset(0, 1).Address) = c.Offset(0, 1).Value
End If
Next c
Debug.Print "done"
End If
End Sub
Try this:
Sub Fruity()
Application.ScreenUpdating = False
Dim LastRow As Integer
'Search code
LastRow = Cells.Find(What:="*", After:=[A1], SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
Dim i As Long
For i = 1 To LastRow
If ThisWorkbook.Sheets("Sheet1").Range("A" & i) = "Orange" Then
Set NextCell = ThisWorkbook.Sheets("Sheet2").Cells(Rows.Count, "B").End(xlUp)
If NextCell = "" Then
NextCell = ThisWorkbook.Sheets("Sheet1").Range("B" & i)
Else
NextCell.Offset(1) = ThisWorkbook.Sheets("Sheet1").Range("B" & i)
End If
End If
Next i
Application.ScreenUpdating = True
End Sub
I am not sure what your target is for Worksheet_Change, so you'll have to clarify your question or add that on your own.
*edit: now puts the column B values from Sheet1 into column B in Sheet2 beginning at B1 instead of putting them in the row that corresponds with the found "Orange."

Get sum from other sheet based on name in the same row

Basically what I need to do is get the value from a specific cell from a different worksheet. The tricky part is that the name of the sheet that it has to access is displayed in a column in the same sheet.
So the status column has to get the number from the other sheet. And the other sheet has to be the name from the "sheetname" column in the same row. If there is no sheet yet it should just stay empty.
Current code I have is this. All this does is create/open the other sheet so far.
Sub CreateNewSheet()
sheet_name_to_create = Range("A" & (ActiveCell.Row)).Value
' Check if filename exists, if false create new else make it active
For rep = 1 To (Worksheets.Count)
If LCase(Sheets(rep).Name) = LCase(sheet_name_to_create) Then
ActiveWorkbook.Sheets(sheet_name_to_create).Activate
Exit Sub
End If
Next
Sheets("TEMPLATE").Copy after:=Sheets(Sheets.Count)
Sheets(ActiveSheet.Name).Name = sheet_name_to_create
End Sub
Its a dutch project so ignore the words at the top btw :p
I am not sure if I get you correct but below example will use Column A data as worksheet name and return cell A1 value in that target sheet to Column B same row.
Sub findNOW()
Dim lastRow As Long
Dim WS As Worksheet
Set WS = ActiveWorkbook.Sheets("Main1")
lastRow = WS.Range("A" & Rows.Count).End(xlUp).Row
For i = 2 To lastRow
On Error Resume Next
WS.Range("B" & i).Value = ThisWorkbook.Sheets(WS.Range("A" & i).Value).Range("A1").Value
Next
End Sub
From Rory's comment it looks like if you put this formula in cell B1, you would just need to know the cell in the other worksheet (A1 in the example).
=INDIRECT(A1 & "!A1")

Copy/Paste multiple rows in VBA

I am attempting to do a simple copy row, paste row within a workbook. I've searched threads and tried changing my code multiple times to no avail.
The one that comes closest to working is this but it only copies a single instance of matching criteria.
I am trying to create a loop that will copy all of the rows that has a match in one of the columns.
So, if 8 columns, each row with matching value in column 7 should copy to a named sheet.
Sub test()
Set MR = Sheets("Main").Range("H1:H1000")
Dim WOLastRow As Long, Iter As Long
For Each cell In MR
If cell.Value = "X" Then
cell.EntireRow.Copy
Sheets("X").Range("A" & Rows.Count).End(xlUp).PasteSpecial
End If
If cell.Value = "Y" Then
cell.EntireRow.Copy
Sheets("Y").Range("A" & Rows.Count).End(xlUp).PasteSpecial
End If
If cell.Value = "Z" Then
cell.EntireRow.Copy
Sheets("Z").Range("A" & Rows.Count).End(xlUp).PasteSpecial
End If
If cell.Value = "AB" Then
cell.EntireRow.Copy
Sheets("AB").Range("A" & Rows.Count).End(xlUp).PasteSpecial
End If
Application.CutCopyMode = False
Next
End Sub
I like this because I need to target multiple destination sheets with different criteria but I need all rows that match criteria to copy over.
EDITED CODE IN RESPONSE TO NEW REQUEST:
The code below will copy all of the rows in Sheet Main and paste them into the corresponding worksheets based on the value in Column 7.
Do note: If there is a value in Column 7 that does NOT match to an existing sheet name, the code will throw an error. Modify the code to handle that exception.
Let me know of any additional needed help.
Sub CopyStuff()
Dim wsMain As Worksheet
Dim wsPaste As Worksheet
Dim rngCopy As Range
Dim nLastRow As Long
Dim nPasteRow As Long
Dim rngCell As Range
Dim ws As Worksheet
Const COLUMN_TO_LOOP As Integer = 7
Application.ScreenUpdating = False
Set wsMain = Worksheets("Main")
nLastRow = wsMain.Cells(Rows.Count, 1).End(xlUp).Row
Set rngCopy = wsMain.Range("A2:H" & nLastRow)
For Each ws In ActiveWorkbook.Worksheets
If UCase(ws.Name) = "MAIN" Then
'Do Nothing for now
Else
Intersect(ws.UsedRange, ws.Columns("A:H")).ClearContents
End If
Next ws
For Each rngCell In Intersect(rngCopy, Columns(COLUMN_TO_LOOP))
On Error Resume Next
Set wsPaste = Worksheets(rngCell.Value)
On Error GoTo 0
If wsPaste Is Nothing Then
MsgBox ("Sheet name: " & rngCell.Value & " does not exist")
Else
nPasteRow = wsPaste.Cells(Rows.Count, 1).End(xlUp).Row + 1
wsMain.Range("A" & rngCell.Row).Resize(, 8).Copy wsPaste.Cells(nPasteRow, 1)
End If
Set wsPaste = Nothing
Next rngCell
Application.ScreenUpdating = True
End Sub
Your current code is pasting to the same row in each sheet over and over, to the last row with a value in column A. Range("A" & Rows.Count).End(xlUp) says, roughly "go to the very bottom of the spreadsheet in column A, and then jump up from there to the next lowest cell in column A with contents," which gets you back to the same cell each time.
Instead, you could use lines of the pattern:
Sheets("X").Range("A" & Sheets("X").UsedRange.Rows.Count + 1).PasteSpecial
Where UsedRange is a range containing all of the cells on the sheet with data in them. The + 1 puts you on the following row.
You could make this a little prettier using With:
With Sheets("X")
.Range("A" & .UsedRange.Rows.Count + 1).PasteSpecial
End With

Best way to get the last non-empty worksheet

I'm trying to write a vba macro for a group tha
has one workbook where they daily create new worksheets, and also have
Sheet 1, Sheet 2 and Sheet 3 at the end of their long list of sheets.
I need to create a external cell reference in a new column in a different workbook where this information is being summarized.
So I need to know how to get the last non-empty sheet so I can grab this data and place it appropriately in the summary.
This function works through the sheets from right to left until it finds a non-blank sheet, and returns its name
Function GetLastNonEmptySheetName() As String
Dim i As Long
For i = Worksheets.Count To 1 Step -1
If Sheets(i).UsedRange.Cells.Count > 1 Then
GetLastNonEmptySheetName = Sheets(i).Name
Exit Function
End If
Next i
End Function
The method above will ignore a sheet with a single cell entry, while that may seem to be a quibble, a Find looking for a non-blank cell will give more certainty.
The xlFormulas argument in the Find method will find hidden cells (but not filtered cells) whereas xlValues won't.
Sub FindLastSht()
Dim lngCnt As Long
Dim rng1 As Range
Dim strSht As String
With ActiveWorkbook
For lngCnt = .Worksheets.Count To 1 Step -1
Set rng1 = .Sheets(lngCnt).Cells.Find("*", , xlFormulas)
If Not rng1 Is Nothing Then
strSht = .Sheets(lngCnt).Name
Exit For
End If
Next lngCnt
If Len(strSht) > 0 Then
MsgBox "Last used sheet in " & .Name & " is " & strSht
Else
MsgBox "No data is contained in " & .Name
End If
End With
End Sub