Outputting rows into another sheet - vba

I have two sets of data stored in two different sheets. I need to run an analysis which prints out the non-duplicate rows (i.e. row is present in one and not the other) found in the sheets and print them in a new sheet.
I can do the comparison fine - it is relatively simple with ranges and the For Next method. I currently store the non-duplicates in two different collections, each representing the non-duplicates in each sheet. However I am having trouble deciding how to proceed with pasting the duplicate rows on the new sheet.
I thought about storing the entire row into a collection but printing the row out of the collection in the new sheet seems non-trivial: I would have to determine the size of the collection, set the appropriate range and then iterate through the collection and print them out. I would also like to truncate this data which would add another layer of complexity.
The other method I thought was simply storing the row number and using Range.Select.Copy and PasteSpecial. The advantage of this is that I can truncate however much I wish, however this seems incredibly hacky to me (essentially using VBA to simulate user input) and I am not sure on performance hits.
What are the relative merits or is there a better way?

I have been tackling a similar problem at work this week. I have come up with two methods:
First you could simply iterate through each collection one row at a time, and copy the values to the new sheet:
Function PasteRows1(ByRef srcRows As Collection, ByRef dst As Worksheet)
Dim row As Range
Dim curRow As Integer
curRow = 1
For Each row In srcRows
dst.rows(curRow).Value = row.Value
curRow = curRow + 1
Next
End Function
This has the benefit of not using the Range.Copy method and so the user's clipboard is preserved. If you are not copying an entire row then you will have to create a range that starts at the first cell of the row and then resize it using Range.Resize. So the code inside the for loop would roughly be:
Dim firstCellInRow as Range
Set firstCellInRow = dst.Cells(curRow,1)
firstCellInRow.Resize(1,Row.columns.Count).Value = row.Value
curRow = curRow + 1
The second method I thought of uses the Range.Copy. Like so:
Function PasteRows2(ByRef srcRows As Collection, ByRef dst As Worksheet)
Dim row As Range
Dim disjointRange As Range
For Each row In srcRows
If disjointRange is Nothing Then
Set disjointRange = row
Else
Set disjointRange = Union(disjointRange, row)
End If
Next
disjointRange.Copy
dst.Paste
End Function
While this does use the .Copy method it also will allow you to copy all of the rows in one shot which is nice because you will avoid partial copies if excel ever crashes in the middle of your macro.
Let me know if either of these methods satisfy your needs :)

Related

Excel VBA Get Count of Non-Empty Rows in Range

I know this has been asked at least a dozen times, but of all those questions the solutions seemed to be tailored or simply created an error in my code. For some reason I get an error when I try and reference a range from a different worksheet. Below is my code. I have two worksheets (tabs). One has a button to launch my code MENU, the other is the data I am trying to read and write to RAW. All I am trying to do is find out how many rows of data the sheet has so I can loop through the rows and make changes. For some reason I can't wrap my head around it or something.
Private Sub CommandButton21_Click()
Dim wbCurrent As Workbook
Dim wsCurrent As Worksheet
Set wbCurrent = ThisWorkbook
Set wsCurrent = wbCurrent.Worksheets("RAW")
Dim i As Long
Dim siteCol As Long
siteCol = Range("I" & Rows.Count).End(xlUp).Row
For i = 1 To siteCol
wsCurrent.Range("I" & i) = "MARKED"
Next i
Range("I1") = siteCol
End Sub
1- Always use Long for your variables, not Integer unless you have a specific reason. The maximum value of an Integer is 32767, which might be not enough to hold your count of rows.
2- Dont get the number of rows with COUNTA, this has many drawbacks. use this instead:
siteCol = wsCurrent.Range("I" & wsCurrent.Rows.count).End(xlUp).Row
This finds the Row number of the last occupied cell in wsCurrent, assuming that wsCurrent is at the top of the document (it starts on Row 1). Please note that if wsCurrent is completely empty, this will find the row number of the first occupied cell above wsCurrent, or the first row of the document.
3- When you want to assign a whole range to the same value, you can do it at once, which is much faster and simpler, like this (no loop needed):
wsCurrent.Range("I1:I" & siteCol) = "MARKED"
4- No need to Activate a worksheet to work with it. Drop the statement wsCurrent.Activate and try to always work with qualified ranges.

Excel 2013: Use macro to copy and subsequently add data from one sheet to another within same workbook

I have used the various methods pointed out in this forum and none seem to work, so I will try to be more specific.
I have a workbook called LIBRARY.xlsm.
This workbook contains two worksheets: CALCULATOR and CUTS.
The worksheet CALCULATOR contains two tables: INPUT and OUTPUT.
I enter data into INPUT, values are calculated and automatically entered into OUTPUT.
I create a button below OUTPUT with macro to copy data in OUTPUT to worksheet CUTS.
I enter new data into INPUT, which then updates OUTPUT.
Now I want to copy this new data to CUTS without overwriting or deleting previous data.
Since this project is divided into 5 sections, I should end up with five tables in the worksheet CUTS that I can then print out.
The INPUT table encompasses cells A1:M31, which does not matter (I’m not copying this).
The OUTPUT table occupies cells O6:S26. This is the data that needs to be copied.
Placement into worksheet CUTS can start at cell A1 (which means the table will have the range A1:E20). I would like to skip a column and then place the next data set. Thus, the next data set should begin at G1 (G1:K20), then at M1:Q20 and so forth). Maybe only go three tables across and then start next three below (separated by row).
Here is the code tried to use. Problem is, it does not retain the values and it overwrites the previous data.
Sub Create_CUTS ()
Dim sourceSheet As Worksheet
Dim sourceRange As Range
Dim sourceRows As Integer
Set sourceSheet = Worksheets("CALCULATOR")
sourceRows = WorksheetFunction.CountA(sourceSheet.Range("A:A"))
Set sourceRange = sourceSheet.Range("O6:S26" & sourceRows)
Dim targetSheet As Worksheet
Dim targetRange As Range
Dim targetRows As Integer
Set targetSheet = Worksheets("CUTS")
targetRows = WorksheetFunction.CountA(targetSheet.Range("A:A"))
Set targetRange = targetSheet.Range("A" & targetRows + 1 & ":A" & targetRows + 1 + sourceRows)
sourceRange.Copy Destination:=targetRange
End Sub
Thank you, everyone
-Grumps
There are a few ways to do this. The easiest is probably to just reference the usedrange of the target sheet to know where you left off with the last paste.
lastUsedRow = targetSheet.UsedRange.Rows.Count
lastColumnUsed = targetSheet.UsedRange.Columns.Count
Then you just add a column or row and paste the table in the new location. If the column count is 22 or greater, add a row and paste at "A" and the lastUsedRow + 2. There is some potential for this to be wrong if the sheets are saved with cells that are empty, but excel reads them as "used" (somehow people I work with manage to do this all the time, I don't even know how they do it). It sounds like this is something that users won't be manipulating, so I wouldn't think that would be a problem, but if it is a possible problem for you, you can use a loop to find the next empty cell instead of using the built in "usedrange" collection.

VBA IE automation - finding the last row of dynamic table

I am working on some code that would take information from Excel and transfer the information to a table on a website in IE. All has been well until now.
I'm currently stuck at a point and I can't seem to find any information to help.
What happens is there is a dynamic table per employee on the website. After clicking an "Add" button, a blank row is created, which is the one I want to transfer the information to. The problem is the row that is created ends up being the last row in the table. So when adding the row, it could be number 1, number 35, or whatever.
Is there a way I can set a reference to the last row of a dynamic IE table without having to know the number so the code would work for any employee?
I think you are talking about converting the elements in a column into an array. Is that what you want? If so, try one of these two scripts.
Sub MakeArray()
Dim arr As Variant
With ActiveSheet
arr = WorksheetFunction.Transpose(.Range(.[A1], .Cells(Rows.Count, "A").End(xlUp)))
End With
End Sub
Sub MakeString()
Dim s As String
Const DELIMITER = ","
With ActiveSheet
s = Join(WorksheetFunction.Transpose(.Range(.[A1], .Cells(Rows.Count, "A").End(xlUp))), DELIMITER)
End With
End Sub

How to paste part of two dimensional array into worksheet range without using loop?

How to paste part of two dimensional array into worksheet range without using loop ?
Pasting whole array is straightforward :
Sub TestPasteArrayIntoRange()
Dim MyArray As Variant
MyArray = [{1,2;3,4;5,101}]
selection.Resize(UBound(MyArray, 1), UBound(MyArray, 2)) = MyArray
End Sub
But what about pasting only second row (first row we could past in by simple reducing selection size to one row) ? I've seen answer for case of one dimensional array which use Index Excel function, but it doesn't apply here.
My first idea was to copy array into range and then copy row from it, but it seems, that I cannot create range object which is not part of some worksheet object (expecially cannot create virtual range without virtual worksheet).
I think you will just have to create another function like e.g. GetRow() or GetColumn() to return a reduced vector (or array) with the select values you need. There are some functions here that you can use for your purpose: http://www.cpearson.com/excel/vbaarrays.htm

Selecting all data from a default table size VBA Excel

I have a spread sheet with a default table size and layout that is populated by information from another spread sheet. This table will always have the same number of columns, but the number of entries in the rows can vary. I want to select all the data from the table, and paste it into another sheet, without copying any empty rows.
My initial attempt involved the following code:
Set rightcell = Range("B9").End(x1Right)
Set bottomcell = Range(rightcell).End(x1Down)
To define what the bottom right corner should be, so I can reference the entire table like so:
Range("B9", bottomcell).Select
Or copy or whatever. When I run this, it gives me a "user-defined or object-defined error" and I don't know why. I have the code entered as part of a larger sub, and I have defined my variables as both ranges and variants to try and get this to work. I have spent quite a bit of time scouring the internet for a solution, but so far the information I've found has not explicitly related to my problem, and none of the similar solutions work.
Does anyone know what the appropriate coding for this is, or if I am making some minor error that is throwing everything else off? I remember encountering the same issue during a project in college, but for the life of me, I can't recall the solution. It's quite frustrating.
Also, if I am too vague or you need more clarification on the task, don't hesitate to ask. Thanks in advance for the help!
EDIT: An important note that I left out is that the the table I want to extract data from is in the middle of a page with multiple other tables that I am not trying to interact with.
If the table will always be in the same location on the sheet, you can do something like this to copy the entire table:
'Modify this to any cell in your table (like the top left hand cell):
Range("B9").CurrentRegion.Copy Sheets("TheSheetYouWantToPasteTo").Range("A1")
Even if the table's location on the sheet changes, you can still use the above code to copy the table as long as you know one of the cells in the table.
If you want to keep the same method as you're trying, try this instead:
Dim rightcell As Long
Dim bottomcell As Long
'Finds the furthest column to the right:
rightcell = Cells(5, Columns.Count).End(xlToLeft).Column
'Finds the bottom most row in the table (will stop at the first non-blank cell it finds.)
bottomcell = Range("B:B").Find("*", Range("B9"), searchdirection:=xlPrevious).Row
'Reference the variables like this:
Range(Cells(9, 2), Cells(bottomcell, rightcell)).copy _
Sheets("TheSheetYouWantToPasteTo").Range("A1")
this is what I use
Public Function last_row() As Long
Dim i As Integer
Dim l_row As Long
'my sheet has 35 columns change this number to fit your
For i = 1 To 35
If Sheet1.Cells(Rows.Count, i).End(xlUp).Row > l_row Then
l_row = Sheet1.Cells(Rows.Count, i).End(xlUp).Row
End If
Next i
last_row = l_row
End Function
Then Use
Dim l_row As Long
l_row = last_row
'Again since you know the last column change 35 here to your value
'or use the String i.e. "AI"
Range("B9", Cells(l_row,35)).Select
This will look at every column to determine the the last row that contains data