I know, I know. There are a ton of suggestions how to solve that particular problem, but somehow they all tend to fail with me.
What I have (simplified): A (Mac) Excel-Sheet "Output" with:
Name Time Date
Mike 08:00 01.01.2016
The second row is yielding the data based on some input on yet another sheet.
What I need:
Whenever I will change the second row of "Output" (i.e. changing the input), I can click a button to add the entire second row onto a new worksheet "Log" (that will feature a header as well). Essentially logging the data upon clicking the button. The data can only be added once, multiple entries of the same data are deleted. After logging the data in "Log", the second row of "Output" does not need to be cleared, however I should not be able to add the same data again.
Any thoughts?
*EDIT
I modified the code from here: http://goo.gl/48jjDo.
Sub Submit()
Application.ScreenUpdating = False
Dim refTable As Variant, trans As Variant
refTable = Array("A = A2", "B = B2", "C=C2")
Dim Row As Long
Row = Worksheets("Log").UsedRange.Rows.Count + 1
For Each trans In refTable
Dim Dest As String, Field As String
Dest = Trim(Left(trans, InStr(1, trans, "=") - 1)) & Row
Field = Trim(Right(trans, Len(trans) - InStr(1, trans, "=")))
Worksheets("Log").Range(Dest).value = Worksheets("Output").Range(Field).value
Next
Application.ScreenUpdating = True
End Sub
*Edit2
Ok this got me further:
Sub CopyRangeFromSheet1toSheet2()
Dim lastRow As Long
lastRow = Sheets("Sheet2").Range("A100000").End(xlUp).Row + 1 ' then next free row in sheet2
Sheets("Sheet1").Range("A2:C2").Copy Destination:=Sheets("Sheet2").Range("A" & lastRow)
End Sub
However, how do I check now for multiple data? And I will need to paste only the values.
So far this works:
Sub CopyFormulas()
Dim sht1 As Worksheet, sht2 As Worksheet
Set sht1 = Sheets("Output")
Set sht2 = Sheets("Log")
sht1.Range("A2:C2").Copy
sht2.Cells(Rows.Count, "A").End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
Application.CutCopyMode = False
response = MsgBox("data was added")
End Sub
Not it is only the check for multiple entries that is missing
Related
I am working with data where the only consistency is the layout and the bold headings to distinguish between a new date.
I am trying to find the cells in between these cells in bold, find the value "Individual" (in column A) in the selected rows, then sum the values of the given rows in column D (as there can be more then 1 row with "Individual"), and copy this new value to a different cell.
Since the cells between the bold is one date, if the value is not there, the output cell needs to shift down one without filling in anything.
Here is what I have so far:
Sub SelectBetween()
Dim findrow As Long, findrow2 As Long
findrow = range("A:A").Find("test1", range("A1")).Row
findrow2 = range("A:A").Find("test2", range("A" & findrow)).Row
range("A" & findrow + 1 & ":A" & findrow2 - 1).Select
Selection.Find("Individual").Activate
range("D" & (ActiveCell.Row)).Select
Selection.copy
sheets("Mix of Business").Select
range("C4").Select
ActiveSheet.Paste
Exit Sub
errhandler:
MsgBox "No Cells containing specified text found"
End Sub
How can I loop through the data and each time it loops through a range, no matter if it finds the value (e.g. individual) or not, shifts down one row on the output cell? Also, how can I change the findrow to be a format (Bold) rather then a value?
Here is some data for reference:
This is what I am trying to get it to look like:
So you have a good start to trying to work through your data. I have a few tips to share that can hopefully help get you closer. (And please come back and ask more questions as you work through it!)
First and foremost, try to avoid using Select or Activate in your code. When you look at a recorded macro, I know that's all you see. BUT that is a recording of your keystrokes and mouseclicks (selecting and activating). You can access the data in a cell or a range without it (see my example below).
In order to approach your data, your first issue is to figure out where your data set starts (which row) and where it ends. Generally, your data is between cells with BOLD data. The exception is the last data set, which just has a many blank rows (until the end of the column). So I've created a function that starts at a given row and checks each row below it to find either a BOLD cell or the end of the data.
Private Function EndRowOfDataSet(ByRef ws As Worksheet, _
ByVal startRow As Long, _
Optional maxRowsInDataSet As Long = 50) As Long
'--- checks each row below the starting row for either a BOLD cell
' or, if no BOLD cells are detected, returns the last row of data
Dim checkCell As Range
Set checkCell = ws.Cells(startRow, 1) 'assumes column "A"
Dim i As Long
For i = startRow To maxRowsInDataSet
If ws.Cells(startRow, 1).Font.Bold Then
EndRowOfDataSet = i - 1
Exit Function
End If
Next i
'--- if we make it here, we haven't found a BOLD cell, so
' find the last row of data
EndRowOfDataSet = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
End Function
To show you how to use that with your specific data, I've created a test subroutine indicating how to loop through all the different data sets:
Option Explicit
Public Sub DataBetween()
Dim thisWB As Workbook
Dim dataWS As Worksheet
Set thisWB = ThisWorkbook
Set dataWS = thisWB.Sheets("YourNameOfSheetWithData")
'--- find the first bold cell...
'Dim nextBoldCell As Range
'Set nextBoldCell = FindNextBoldInColumn(dataWS.Range("A1"))
'--- now note the start of the data and find the next bold cell
Dim startOfDataRow As Long
Dim endOfDataRow As Long
Dim lastRowOfAllData As Long
startOfDataRow = 3
lastRowOfAllData = dataWS.Cells(ws.Rows.Count, "A").End(xlUp).Row
'--- this loop is for all the data sets...
Loop
endOfDataRow = EndRowOfDataSet(dataWS, startOfDataRow)
'--- this loop is to work through one data set
For i = startOfDataRow To endOfDataRow
'--- work through each of the data rows and copy your
' data over to the other sheet here
Next i
startOfDataRow = endOfDataRow + 1
Do While endOfDataRow < lastRowOfAllData
End Sub
Use both of those together and see if that can get you closer to a full solution.
EDIT: I should have deleted that section of code. It was from an earlier concept I had that didn't completely work. I commented out those lines (for the sake of later clarity in reading the comments). Below, I'll include the function and why it didn't completely work for this situation.
So here's the function in question:
Public Function FindNextBoldInColumn(ByRef startCell As Range, _
Optional columnNumber As Long = 1) As Range
'--- beginning at the startCell row, this function check each
' lower row in the same column and stops when it encounters
' a BOLD font setting
Dim checkCell As Range
Set checkCell = startCell
Do While Not checkCell.Font.Bold
Set checkCell = checkCell.Offset(1, 0)
If checkCell.Row = checkCell.Parent.Rows.Count Then
'--- we've reached the end of the column, so
' return nothing
Set FindNextBoldInColumn = Nothing
Exit Function
End If
Loop
Set FindNextBoldInColumn = checkCell
End Function
Now, while this function works perfectly well, the situation is DOES NOT account for is the end of the last data set. In other words, a situation like this:
The function FindNextBoldInColumn will return nothing in this case and not the end of the data. So I (should have completely) deleted that function and replaced it with EndRowOfDataSet which does exactly what you need. Sorry about that.
Hellow,
I have a problem with copy past code
I can't identify the last cell in the row "where I would like to past" !!?
Here in the next code, I wrote "Shet.Cells(Rows.Count, "N").End(xlUp).row + 1", and it works well just in case there are no hidden rows, except that last row's value always replace itself !!
So, what should I do to update last row's value every time I execute Sub Copy_Past() ???
Sub Copy_Past()
Dim Shet As Worksheet
Set Shet = ThisWorkbook.Sheets(1)
Dim LRow As Long
'To get the latest cell in the column "N", where I would like to paste my data.
LRow = Shet.Cells(Rows.Count, "N").End(xlUp).row + 1
'To make a copy form where I selected
Selection.SpecialCells(xlCellTypeVisible).Copy ActiveSheet.Cells(LRow, "M")
'To delete the range of data that I selected and after coping them
Selection.SpecialCells(xlCellTypeVisible).Delete (xlShiftUp)
End Sub
I found a solution for this :)
I brought this method from here
https://answers.microsoft.com/en-us/msoffice/forum/msoffice_excel-msoffice_custom-mso_2007/finding-last-row-including-hidden-rows/af0d7d7c-84f1-44bf-b36a-5abc98a93fa6
Sub xlCellTypeLastCell_Example_Column()
For LastRow = Columns("N").SpecialCells(xlCellTypeLastCell).row To 1 Step -1
If Len(Cells(LastRow, "N").Formula) Then Exit For
Next
MsgBox LastRow
End Sub
I'm trying to create a yearly summary for some of our transfers. Essentially, I have 12 sheets, one for each month of the year, and each entry is given one of four specific "Transfer Rationales" in column L. I need to be able to create a worksheet that gives me a running year-to-date summary based on each transfer rationale.
So say, for example, the transfer rationale I'm looking at is called "Incorrectly Assigned" - I think need to have the summary page show columns G-K of each row where column L is "Incorrectly Assigned" from all twelve month sheets.
I've been looking at VBA code and trying to tweak some to work, but I could use some help!
EDIT:
Obviously it's not working as I need or I wouldn't be here, but I don't have much knowledge about VBA. I have something here where the code is grabbing the entries where column L met the criteria, but it was
a) copying all the columns, and I only need G-K to paste, and
b) was putting the copied rows all in one row in the summary tab, so I could see the data for a split second, and then it would overwrite with the next line and so on until it finally settled on the last entry found.
SECOND EDIT:
So I have a code that now (mostly) works, I've pasted it below and deleted the old code above.
Private Sub CommandButton1_Click()
Dim WkSht As Worksheet
Dim r As Integer
Dim i As Integer
i = 1
For Each WkSht In ThisWorkbook.Worksheets
i = i + 1
If WkSht.Name <> "Incorrectly Assigned" Then
For r = 1 To 1000
If WkSht.Range("L" & r).Value = Sheets("Incorrectly Assigned").Range("A1").Value Then
WkSht.Range("E:L").Rows(r & ":" & r).Copy
Sheets("Incorrectly Assigned").Range("E:L").End(xlUp).Offset(i, 0).PasteSpecial Paste:=xlPasteValues
End If
Next r
End If
Next WkSht
End Sub
The problem now is that it is only grabbing the last match from each worksheet - so say January has four matching entries, it's only pasting the fourth entry, then the next row down it'll paste the last entry from February etc. and then if there's an entry in say November that matches, it'll be pasted in the 11th row from the beginning, rather than each entry being pasted one after another.
Better to create a sub-routine that you call from your "CommandButton1". Then you can call the procedure from more than one location. You can also generalize it by using an input parameter 'transferID' which defines the summary you want.
Private Sub CommandButton1_Click()
Call PrintSummary("Incorrectly Assigned")
End Sub
It will likely need some tweaking to get it how you want, but this should give you some ideas to get you started:
Sub PrintSummary(transferID As String)
Dim ws As Excel.Worksheet
Dim wso As Excel.Worksheet
Dim lrow As Long
Dim rng As Excel.Range
Dim rngo As Excel.Range
Dim cell As Excel.Range
Dim colH As Variant
Dim i As Integer
'// Define columns for output
colH = Array("G", "H", "I", "J", "K")
'// Check for summary sheet (for output)
On Error Resume Next
Set wso = ThisWorkbook.Worksheets("Summary")
On Error GoTo 0
If wso Is Nothing Then
'// Summary worksheet does not exist :/
Exit Sub
Else '// format worksheet for output
'// for example...
wso.Cells.Delete Shift:=xlUp
Set rngo = wso.Range("A1") '// define output start
Set wso = Nothing
End If
'// Loop through worksheets
For Each ws In ThisWorkbook.Worksheets
'// Check for valid worksheet name
Select Case VBA.UCase(ws.Name)
Case "JAN", "FEB" '// and so forth...
Set rng = ws.Range("L1")
Set rng = ws.Range(rng, ws.Cells(Rows.Count, rng.Column).End(xlUp))
For Each cell In rng
If (VBA.UCase(cell.Text) = VBA.UCase(transferID)) Then
'// Print meta data
rngo.Offset(lrow, 0).Value = ws.Name
rngo.Offset(lrow, 1).Value = transferID
'// Print values
For i = 0 To UBound(colH)
rngo.Offset(lrow, i + 2).Value = ws.Cells(cell.Row, VBA.CStr(colH(i))).Value
Next i
'// Update counter
lrow = lrow + 1
End If
Next cell
Case Else
'// Not a month? do nothing
End Select
Next ws
End Sub
You do not need VBA - just refence the cell in the other tab:
SheetName!CellAddress
Precede the cell address with the worksheet name, and follow it with an exclamation point.
If you need VBA, then I have understood your question incorrectly.
EDIT:
Lets start with problem B:
was putting the copied rows all in one row in the summary tab
Lets look at the code you use to paste values:
Sheets("Summary").Range("A65536").End(xlUp).Offset(1).PasteSpecial Paste:=xlPasteValues
Here you always paste everyting in the same place, in cell A65536 which you offset by one. On every iteration of your loop, the values will be at the same place. Change the Offset(1) to
Offset(0, r)
Now on every iteration you will paste on a different row, because r will be 1, 2, ..., 1000. See MSDN for documentation on Offset. Select a values that accomplished a paste the way you need.
Lets go to the next question:
a) it was copying all the columns
I will edit once the first part works as it should for you.
What I am trying to do is copy variable data ranges, but identical headers, from all sheets and paste into the Master sheet one after the other. The original code (CODE 1 below) renewed the data in the master whenever I clicked on another sheet and back onto the master. The problem now is that there are other sheets in the Workbook that I do not want included in the copy process.
I have edited the code I received below (CODE 2 below) to try and define start and end sheets for running a "loopindex" and also removing the "copy headers" line of code as the headers for each worksheet are appearing throughout the mastersheet. Obviously it does not work and I was wondering if someone could help.
Could you please help me correct the combined code or provide a more elegant solution? Thanks.
Original question here - Excel Forum post
Secondary code from here - Stack post LoopIndex
Original CODE 1
Private Sub Worksheet_Activate()
Dim ws As Worksheet
Application.ScreenUpdating = False
Me.UsedRange.Clear
For Each ws In ThisWorkbook.Worksheets
If ws.Name <> Me.Name Then
If Range("A1") = "" Then ws.Range("A1").EntireRow.Copy Me.Range("A1")'copy in the headers
ws.UsedRange.Offset(1).Copy Me.Range("A" & Rows.Count).End(xlUp).Offset(1)'copy data
End If
Next ws
Application.ScreenUpdating = True
End Sub
Edited CODE 2
Private Sub Worksheet_Activate()
Dim ws As Worksheet
Application.ScreenUpdating = False
Me.UsedRange.Clear
Dim StartIndex, EndIndex, LoopIndex As Integer
StartIndex = Sheets("Master sheet").Index + 1
EndIndex = Sheets("End").Index - 1
For LoopIndex = StartIndex To EndIndex
If Range("A1") = "" Then ws.Range("A1").Offset(1).Copy Me.Range("A" &Rows.Count).End(xlUp).Offset(1) 'copy data
Next LoopIndex
Application.ScreenUpdating = True
End Sub
I can just about understand why you had this as a Worksheet Activate event routine against worksheet "Master list" when there was only one source worksheet. I am having more difficulty in seeing this as convenient when you have multiple source worksheets. I am not asking you to justify your decision since I do not have a full understanding of workbook but you might like to reconsider your approach. I have coded the routine below as an normal macro but you can change this easily if you wish.
I do not like the approach of assuming the worksheets to be loaded are from Sheets("Master sheet").Index + 1 to Sheets("End").Index - 1. I would have thought that was unstable although I have never tried this approach.
I have created a hidden worksheet "Load List":
This lists the worksheets to be loaded in the sequence to be loaded.
I have filled worksheet "Sheet1" with data:
Not very imaginative data but it makes it easy to check that "Master list" is loaded with the correct data. Worksheets "Sheet2" to "Sheet5" have similar data except that the number of data rows vary and "S1" is replaced by "S2", "S3", "S4" and "S5".
After the macro has run, the top of "Master list" contains:
You can see I have loaded all rows from the first worksheet then data rows only from subsequent worksheets.
I do not say a great deal about the VBA I have used. Once you know a statement exists it is normally easy to look it up. Ask if necessary. I hope I have provided an adequate explanation of what the code does. Again ask if necessary.
Option Explicit
Sub CombinedSelected()
Dim ColSrcMax As Long
Dim LoadList As Variant
Dim RowListCrnt As Long
Dim RowListMax As Long
Dim RowMasterNext As Long
Dim RowSrcMax As Long
With Worksheets("Load List")
RowListMax = .Cells(Rows.Count, "A").End(xlUp).Row
' Load the values from column A of worksheet "Load List" to LoadList.
' The statement converts LoadList to a 2 dimensional array. It is the
' equivalent of Redim LoadList(1 To RowListMax, 1 to 1)
LoadList = .Range(.Cells(1, "A"), .Cells(RowListMax, "A")).Value
End With
RowMasterNext = 1
With Worksheets("Master sheet")
.Cells.EntireRow.Delete ' Delete existing contents
End With
For RowListCrnt = 2 To RowListMax
With Worksheets(LoadList(RowListCrnt, 1))
' Find last used row and column containing a value.
' Warning. These statements do not allow for any of the source worksheets being empty
RowSrcMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious).Row
ColSrcMax = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious).Column
If RowListCrnt = 2 Then
' For first source worksheet only include header row
.Range(.Cells(1, 1), .Cells(RowSrcMax, ColSrcMax)).Copy _
Destination:=Worksheets("Master sheet").Cells(RowMasterNext, 1)
RowMasterNext = RowMasterNext + RowSrcMax
Else
' Data rows only to be copied
.Range(.Cells(2, 1), .Cells(RowSrcMax, ColSrcMax)).Copy _
Destination:=Worksheets("Master sheet").Cells(RowMasterNext, 1)
RowMasterNext = RowMasterNext + RowSrcMax - 1
End If
End With
Next
End Sub
I am working on a macro that will consolidate two different sources of order data. The first source will contain old orders as well as some new, the second source will contain only the old orders and will have additional data in columns that were updated manually.
My idea for this is to take the order totals from the second source, paste them in a sheet after the order totals from the first source, and then search all the order numbers from the new file against the order numbers from the existing tracker. I have a for loop that is supposed to find the order numbers from the new file that are not already in the tracker and then insert a row with that order detail. I am receiving a Type mismatch error on the if statement that checks if the string exists in the array. Please take a look at this code:
Dim r As Integer
For r = 1 To 1000
Dim NewOrd As String
NewOrd = Range(Cells(r, 1), Cells(r, 1)).Value
Dim ExistArray As Variant
ExistArray = Range("a1", Range("a1").End(xlUp))
Sheets("Sheet2").Select
If IsEmpty(NewOrd) Then
Exit For
End If
If Not UBound(Filter(ExistArray, NewOrd)) >= 0 And NewOrd <> "" Then
Rows(r).Select
Selection.Copy
Sheets("Sheet3").Select
Rows(r).Select
Selection.Insert Shift:=xlDown
Application.CutCopyMode = False
End If
r = r + 1
Next r
I have tried a few different ways of setting the array, tried adding option explicit, and tried nesting for loops (not my brightest efficiency moment). Would greatly appreciate another set of eyes!
Thanks!
Assigning a Range object to an array always results in a two-dimensional array, which is causing the error.
Do this:
ExistArray = Application.Transpose(Range("a1", Range("a1").End(xlUp)))
I think that should resolve it for you.
Updates
You may need to:
Dim ExistArray() As Variant
Your range object is also problematic, being a single cell:
ExistArray = Application.Transpose(Array(Range("A1")))
Change the sheet names from "Sheet1" and "Sheet2" as necessary:
Sub tgr()
Dim wsNew As Worksheet
Dim wsTracker As Worksheet
Dim rIndex As Long
'This is the sheet that contains the new data that needs to be added
Set wsNew = Sheets("Sheet1")
'This sheet contains the old data
Set wsTracker = Sheets("Sheet2")
'Go through each row in the new data
For rIndex = 1 To wsNew.Cells(Rows.Count, "A").End(xlUp).Row
'Verify that the row isn't blank and that it doesn't already exist in wsTracker
If Len(wsNew.Cells(rIndex, "A").Value) > 0 And WorksheetFunction.CountIf(wsTracker.Columns("A"), wsNew.Cells(rIndex, "A").Value) = 0 Then
'This is a new item that needs to be added
'Copy the row to the next available row in wsTracker
wsNew.Rows(rIndex).Copy wsTracker.Cells(Rows.Count, "A").End(xlUp).Offset(1)
End If
Next rIndex
Set wsNew = Nothing
Set wsTracker = Nothing
End Sub