I am needing to follow the protocol below:
I am scanning Sheet1 and for each unique empName on that worksheet selecting the individual empName worksheet.
on the individual empName worksheet capturing the value in the last cell in column O
Storing the value in variable tper (it's a percentage)
Selecting sheet1
Writing a header to column N1
Selecting the 1st empty cell in column N (excluding the header)
write the value of tper to the selected cell in column N
Repeat until all empNames have been processed from Sheet1
My syntax seems to execute as it should up until this line lr1 = Cells(Rows.Count, 13).End(xlUp).Row where it throws an error of
error invalid qualifier
What do I need to re-write in order for this to follow the protocol outlined above?
Function Test()
Dim lr As Long, i As Long, lr1 As Long, i1 As Long
Dim WS As Worksheet, empName As String, tper As Variant
Set WS = ActiveSheet
lr = Cells(Rows.Count, 2).End(xlUp).Row
For i = 2 To lr
empName = WS.Cells(i, 2).Value
Sheets(empName).Select
tper = "=LOOKUP(2,1/(O:O<>""),O:O)"
Sheets("Sheet1").Select
Range("N1").FormulaR1C1 = "Percent"
lr1 = Cells(Rows.Count, 13).End(xlUp).Row
For i1 = 2 To lr1
lr1.Cells.FormulaR1C1 = tper
Next i1
Next i
End Function
I have attempted to modify your code. see if this works. Since you want to loop through all employees in Sheet1, which is being taken care of by the first for loop, I got rid of the second loop.
Sub Test()
Dim empName As String, tper As Variant
Dim WS As Worksheet, empSheet As Worksheet
Set WS = Sheets("Sheet1")
Dim lr As Long
lr = WS.Cells(Rows.Count, 2).End(xlUp).Row
Dim i As Long
For i = 2 To lr
'scanning Sheet1 and for each unique empName
empName = WS.Cells(i, 2).Value
'selecting the individual empName worksheet
'just set to variable. no need to select
Set empSheet = Sheets(empName)
'on the individual empName worksheet capturing the value in the last cell in column O
'Storing the value in variable tper (it's a percentage)
tper = empSheet.Range("O" & Rows.Count).End(xlUp).Value
'Selecting Sheet1
'Writing a header to column N1
WS.Range("N1").FormulaR1C1 = "Percent"
'Selecting the 1st empty cell in column N (excluding the header)
WS.Range("N" & Rows.Count).End(xlUp).Offset(1, 0) = tper
'Repeat until all empNames have been processed from Sheet1
' next i will reapeat for the next employee in sheet 1
Next i
End Sub
You also mentioned
for each unique empName
The code does not check that.
Related
I would like to copy column from excel "Book1" to another excel "Book2" by determined its cell value.
Let's say the header columns in Book1 are Name, Age, Gender, Address and Group. I want to copy the column "Name", "Age" and "Group" to "Book2". Below coding is what I've done to pull column data by cell coordinate.
Is it possible if I can pull the column from its header value?
Sub copyColumns()
Dim lr As Long, r As Long
Set src = ThisWorkbook.Worksheets("Sheet1")
Set tgt = Workbooks("Book2.xlsx").Worksheets("Sheet1")
lr = src.Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To lr
src.Cells(i, 1).copy
r = tgt.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
src.Paste Destination:=tgt.Cells(r, 1)
src.Cells(i, 2).copy
src.Paste Destination:=tgt.Cells(r, 2)
src.Cells(i, 5).copy
src.Paste Destination:=tgt.Cells(r, 3)
Next i
End Sub
There is number of solutions to this problem. For example, declare variables:
Dim rw As Range
Dim cl As Range
Dim sFields As String
Dim V
Dim j As Integer
Chose your column names for copying:
sFields = "Name|Age|Group"
V = Split(sFields, "|")
And then, inside your For i loop, make two another loops:
For Each cl In Intersect(src.Rows(i), src.UsedRange)
For j = 0 To UBound(V)
If cl.EntireColumn.Range("A1") = V(j) Then
tgt.Cells(i, j).Value = src.Cells(i, j).Value
End If
Next j
Next cl
Intersect(src.Rows(i), src.UsedRange) will chose all cells in row in the range that is actually used (that is, it will not loop through all 16 384 columns. All columns names are in one variable sFields, you can easily modify it. It is string separated with pipes, you will probably never use pipes (|) in your field names, so it is safe.
A few tips along the way:
Always declare every variable you want to use and user Option Explicit at the beginning of your module.
Dont copy every single cell, copy a range
See the updated code below:
Sub copyColumns2()
Dim src As Worksheet, tgt As Worksheet
Dim lr As Long, r As Long, I As Long, Col As Long
Dim ColsToCopy, ColToCopy, counter As Integer
ColsToCopy = Array("Name", "Age", "Group")
Set src = ThisWorkbook.Worksheets("Sheet1")
Set tgt = Workbooks("Book2.xlsx").Worksheets("Sheet1")
lr = src.Cells(Rows.Count, 1).End(xlUp).Row
For Col = 1 To 20
For Each ColToCopy In ColsToCopy
If src.Cells(2, Col).Value = ColToCopy Then
counter = counter + 1
src.Range(src.Cells(2, Col), src.Cells(lr, Col)).Copy tgt.Cells(2, counter)
Exit For
End If
Next ColToCopy
Next Col
End Sub
If you would like to copy the column by column header, you can use this function to get the letter of your header:
Function Letter(oSheet As Worksheet, name As String, Optional num As Integer)
If num = 0 Then num = 1
Letter = Application.Match(name, oSheet.Rows(num), 0)
Letter = Split(Cells(, Letter).Address, "$")(1)
End Function
Implementation:
Sub copycolum()
Dim ws As Worksheet, ws2 As Worksheet
Set ws = Sheets("Sheet1"): Set ws2 = Sheets("Sheet2"):
ws.Range(Letter(ws, "Gender") & "2:" & Letter(ws, "Gender") & ws.Range(Letter(ws, "Gender") & "1000000").End(xlUp).Row).Copy Destination:=ws2.Range(Letter(ws2, "Gender") & "2")
End Sub
Note that the function is set to default at row 1, in other words your header is in column 1, if you like you can change this to whatever row your data is in.
I'm trying to write a loop that searches every filled cell in column "J" in sheet2, and hides the corresponding row in sheet1. For example, if cell J1 was "30", row 30 in sheet1 would be hidden. Here's what I have so far:
Sub test()
Dim myrng As Range
Dim ws As Worksheet
Dim i As Integer
Dim lrow As Long, value As Long
Dim cell As Variant
Set ws = Sheet2
lrow = ws.Range("J" & Rows.Count).End(xlUp).Row
For i = 1 To lrow
value = ws.Cells(i, 10).value
cell = "A" & value
Sheet1.[cell].EntireRow.Hidden = True
Next i
End Sub
Everything is working except for the line "Sheet1.[value].entirerow.hidden=true". I'm probably using incorrect syntax. Any thoughts/help would be much appreciated!
Thanks,
Kim
What I want to do: Iterate over values on one page to check for match on another page and if a match is found take a value from 2nd page same row but different column.
I've been trying now for quite some time. I'm new to VBA-scripting / Excel and might be approaching the problem incorrectly, hence why I'm asking here!
My code so far:
Sub InsertData()
ScreenUpdating = False
Dim wks As Worksheet
Dim subSheet As Worksheet
Set subSheet = Sheets("Sheet4")
Dim rowRangeSub As Range
Dim LastRowSub As Long
LastRowSub = subSheet.Cells(subSheet.Rows.Count, "C").End(xlUp).Row
Set rowRangeSub = subSheet.Range("C2:C" & LastRowSub)
Dim subGroupList As ListObject
Dim rowRange As Range
Dim colRange As Range
Dim LastCol As Long
Dim LastRow As Long
Dim Found As Range
'START OF SHEET1'
Set wks = Sheets("SHEET1")
LastRow = wks.Cells(wks.Rows.Count, "B").End(xlUp).Row
Set rowRange = wks.Range("B2:B" & LastRow)
'Loop through each row in B column (Names)'
For Each rrow In rowRange
If Not IsEmpty(rrow) Then
With Sheets("Sheet4").Range("C2:C" & LastRowSub)
Set Found = .Find(What:=rrow, _
After:=.Cells(1), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Found Is Nothing Then
'Debug.Print "Found"'
wks.Cells(rrow.Row, "K").Value = "Found"
Else
wks.Cells(rrow.Row, "K").Value = "Not Found"
'Debug.Print "Not Found"'
End If
End With
End If
Next rrow
'END OF SHEET1'
'START OF SHEET2'
Set wks = Sheets("SHEET2")
LastRow = wks.Cells(wks.Rows.Count, "B").End(xlUp).Row
Set rowRange = wks.Range("B2:B" & LastRow)
'END OF SHEET2'
'START OF SHEET3'
Set wks = Sheets("SHEET3")
LastRow = wks.Cells(wks.Rows.Count, "B").End(xlUp).Row
Set rowRange = wks.Range("B2:B" & LastRow)
'END OF SHEET3'
ScreenUpdating = True
End Sub
The setup in the Excel file is as such:
The three sheets, Sheet1, Sheet2, Sheet3 contains a lot of data in its 10 first columns (A-J) and the 11th column (K) is where the data is to be inserted if it is found. Pertinent data, names, is found in column B where B:1 is just "Name" as a title. There is also some empty cells in the column to take into consideration.
The 4th sheet, Sheet4 contains some data in its 5 first columns. The names which are to be matched can be found in column C, and if a match is found it is supposed to collect data from the Cells(Found.Row, "E") where "E" is column E.
This problem has been screwing with my head quite a lot since .Find()-function seems to not work as I expect it to, as in it finds the opposites sometimes.
My main question is: How do I assign the correct value to the row?
wks.Cells(rrow.Row, "K").Value = rowRangeSub.Cells(Found.Row, "E").Value
I feel like I've tested at least 10 different ways to assign, but I keep on getting error after error. Most of the time it's a missmatch error.
Any help is appreciated!
EDIT since reading comments:
Ok, here it goes :
All columns are formatted as text.
Column A: Personal numbers: not relevant
Column B: Names: Form is: Lastname, Firstname. This is to be used when searching for a match.
Column C to J not relevant with various information about a person.
Column K: This columns cell starts out empty. This is to be filled by the macro.
I have three different books within the Excel file that have data that looks like what I've explained, just different data in each book.
The 4th book is as such:
Column A and B is not relevant with info not needed at all.
Column C: Is the names in form Lastname, Firstname. This is what should be the column cells to compare with column B's cells in the other books.
Column D: Not relevant
Column E: This is the important part of Sheet4. For every person there is a "group number" that can be found in this column for every row.
What I want to do is compare each cell in column B in Sheet1-3 for a match in column C in Sheet4. If a match is found (not all are assigned a group, so matches might not be found) then take cell information from Sheet4 on the row which a match was found and column "E", put this information in the row in Sheet1-3 and column "K".
Example data (is there a way to submit tables?):
Sheet1:
COLUMN B
Tablesson, Pen
Paper, Ink
Eraser, Screen
COLUMN K is at this moment empty
Sheet4:
COLUMN C
Paper, Ink
Eraser, Screen
COLUMN E
55
77
RUNS THE MACRO, Sheet1 after macro:
COLUMN B
Tablesson, Pen
Paper, Ink
Eraser, Screen
COLUMN K
[First entry is empty since no match was found]
55
77
Hopefully this is understandable!
I simplified the process by using a Scripting Dictionary.
Sub InsertData()
Dim lastRow As Long, x As Long
Dim dicNames, k As String, v As Variant
Set dicNames = CreateObject("scripting.dictionary")
'Create list of Names to compare against and values to update
With Worksheets("Sheet4")
lastRow = .Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To lastRow
k = .Cells(x, 3).Value 'Name from Column C
v = .Cells(x, 5).Value 'Value From Column E
'Add Key Value pairs to Dictionary
If Not dicNames.Exists(k) Then dicNames.Add k, v
Next
End With
ProcessWorksheet Worksheets("Sheet1"), dicNames
ProcessWorksheet Worksheets("Sheet2"), dicNames
ProcessWorksheet Worksheets("Sheet3"), dicNames
End Sub
Sub ProcessWorksheet(ws As Worksheet, ByRef dicNames)
Dim k As String, v As Range
Dim lastRow As Long, x As Long
With ws
lastRow = .Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To lastRow
k = .Cells(x, 2) 'If Name from Column B
If dicNames.Exists(k) Then
.Cells(x, 11) = dicNames(k) 'Then Column K = Value from Sheet4
End If
Next
End With
End Sub
Sub InsertData()
Dim lastRow As Long, x As Long
Dim dicNames, k As String, v As Variant
Set dicNames = CreateObject("scripting.dictionary")
'Create list of Names to compare against and values to update
With Worksheets("Sheet4")
lastRow = .Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To lastRow
k = .Cells(x, 3).Value 'Name from Column C
v = .Cells(x, 5).Value 'Value From Column E
'Add Key Value pairs to Dictionary
If Not dicNames.Exists(k) Then dicNames.Add k, v
Next
End With
ProcessWorksheet Worksheets("Sheet1"), dicNames
ProcessWorksheet Worksheets("Sheet2"), dicNames
ProcessWorksheet Worksheets("Sheet3"), dicNames
End Sub
Sub ProcessWorksheet(ws As Worksheet, ByRef dicNames)
Dim k As String, v As Range
Dim lastRow As Long, x As Long
With ws
lastRow = .Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To lastRow
k = .Cells(x, 2) 'If Name from Column B
If dicNames.Exists(k) Then
.Cells(x, 11) = dicNames(k) 'Then Column K = Value from Sheet4
End If
Next
End With
End Sub
Basically used the code provided by Thomas Inzina with minor changes:
If dicNames.Exists(k) Then
newV = IIf(dicNames(k) = v, v, dicNames(k) & "," & v)
dicNames.Remove (k)
dicNames.Add k, newV
Else
dicNames.Add k, v
End If
This takes duplicates into consideration.
I also used this cleaning function since I couldn't find the built-in one in VBA. Used them as such:
k = CleanTrim(.Cells(X, 3).Value) 'Name from Column C
k = CleanTrim(.Cells(X, 2).Value) 'If Name from Column B
I have the following data set in Sheet1 with headings as you see below:
I want to split the big data set into different sheets by every empty row. Every data set is separated by an empty row, and every data set have values in all cells in columns A and E but their columns B, C, D might have some empty cells randomly. So the defining element to split is the empty rows in column E.
Q1: I want to copy the headings A1:D1 to the new sheets and only copy the columns A:D and not the column E.
Q2: I want to rename new sheets to take the cell value in column E as their name.
So the *results are the following:
Sheet ID1:
Sheet ID2:
Sheet ID3:
I have tried the following code, it works, but it only copies the first table, without renaming the sheet to take the cell value in column E, and it should copy the column E so it should copy only A:D, and it doesn't loop through all tables.
Sub Split_Sheets_by_row()
Dim lLoop As Long, lLoopStop As Long
Dim rMove As Range, wsNew As Worksheet
Set rMove = ActiveSheet.UsedRange.Columns("A:E")
lLoopStop = WorksheetFunction.CountIf(rMove, "Heading5")
For lLoop = 1 To lLoopStop
Set wsNew = Sheets.Add
rMove.Find("Heading5", rMove.Cells(1, 1), xlValues, _
xlPart, , xlNext, False).CurrentRegion.Copy _
Destination:=wsNew.Cells(1, 1)
Next lLoop
End Sub
Your help is very much appreciated.
I've taken a slightly different approach but I have achieved the results you are looking for.
Sub Split_Sheets_by_row()
Dim hdr As Range, rng As Range, ws As Worksheet, wsn As Worksheet
Dim rw As Long, lr As Long, b As Long, blks As Long
Set ws = ActiveSheet
With ws
Set hdr = .Cells(1, 1).Resize(1, 4)
lr = .Cells(Rows.Count, 5).End(xlUp).Row
rw = 2
blks = Application.CountBlank(.Range(.Cells(rw, 1), .Cells(lr, 1))) + 1
For b = 1 To blks
Set rng = .Cells(rw, 1).CurrentRegion
Set rng = rng.Offset(-CBool(b = 1), 0).Resize(rng.Rows.Count + CBool(b = 1), 4)
Set wsn = Worksheets.Add(after:=Sheets(Sheets.Count))
With wsn
.Name = rng.Offset(0, 4).Cells(1, 1).Value
hdr.Copy Destination:=.Cells(1, 1)
rng.Copy Destination:=.Cells(2, 1)
End With
rw = rw + rng.Rows.Count + 1
Set rng = Nothing
Set wsn = Nothing
If rw > lr Then Exit For
Next b
End With
Set rng = Nothing
Set ws = Nothing
End Sub
The header is stored for repeated use and the number of blocks of data are counted by counting the separating blank rows and adding 1. The value from column E is used to rename the worksheet but is not carried across in the data transfer to the new worksheet.
I'm not sure how you would want to handle a worksheet with the same name already existing but they could be deleted before a new worksheet is renamed.
This is my first time to code in VBA.
I have several worksheets in a file and they are in order by dates.
So what I am trying to do is to collect data sets in a worksheet if they have the same period of time.
date1 value1
date2 value2
date3 value3
Since they are in order I just compare the first date values and if they are different it moves on to the next worksheet. If they are the same then copy the value and do the same process until it reaches the last worksheet.
However it copies one worksheet fine but after that Excel freezes.
I would be appreciated if you find any errors or give me other suggestions to do it.
Following is my code:
Sub matchingStock()
Dim sh1 As Worksheet, sh2 As Worksheet
' create short references to sheets
' inside the Sheets() use either the tab number or name
Set sh1 = Sheets("combined")
Dim col As Long
'since first column is for Tbill it stock price should place from the third column
col = 3
Dim k As Long
'go through all the stock worksheets
For k = Sheets("WLT").Index To Sheets("ARNA").Index
Set sh2 = Sheets(k)
' Create iterators
Dim i As Long, j As Long
' Create last rows values for the columns you will be comparing
Dim lr1 As Long, lr2 As Long
' create a reference variable to the next available row
Dim nxtRow As Long
' Create ranges to easily reference data
Dim rng1 As Range, rng2 As Range
' Assign values to variables
lr1 = sh1.Range("A" & Rows.Count).End(xlUp).Row
lr2 = sh2.Range("A" & Rows.Count).End(xlUp).Row
If sh1.Range("A3").Value = sh2.Range("A3").Value Then
Application.ScreenUpdating = False
' Loop through column A on sheet1
For i = 2 To lr1
Set rng1 = sh1.Range("A" & i)
' Loop through column A on sheet1
For j = 2 To lr2
Set rng2 = sh2.Range("A" & j)
' compare the words in column a on sheet1 with the words in column on sheet2
'Dim date1 As Date
'Dim date2 As Date
'date1 = TimeValue(sh1.Range("A3"))
'date2 = TimeValue(sh2.Range("A3"))
sh1.Cells(1, col).Value = sh2.Range("A1").Value
' find next empty row
nxtRow = sh1.Cells(Rows.Count, col).End(xlUp).Row + 1
' copy the word in column A on sheet2 to the next available row in sheet1
' copy the value ( offset(0,1) Column B ) to the next available row in sheet1
sh1.Cells(nxtRow, col).Value = rng2.Offset(0, 6).Value
'when the date is different skip to the next worksheet
Set rng2 = Nothing
Next j
Set rng1 = Nothing
Next i
'sh3.Rows("1:1").Delete
Else
GoTo Skip
End If
Skip:
col = col + 1
Next k
End Sub
I cannot identify a specific error so this is a list of suggestions that may help you identify the error and may help improve your code.
Suggestion 1
Do you think the Else block of If-Then-Else-End-If is compulsory?
If sh1.Range("A3").Value = sh2.Range("A3").Value Then
:
Else
GoTo Skip
End If
Skip:
is the same as:
If sh1.Range("A3").Value = sh2.Range("A3").Value Then
:
End If
Suggestion 2
I do not like:
For k = Sheets("WLT").Index To Sheets("ARNA").Index
The value of property Index for a worksheet may not what you think it is. This may not give you the set or sequence of worksheets you expect. Do you want every worksheet except "Combined"? The following should be more reliable:
For k = 1 To Worksheets.Count
If Worksheets(k).Name <> sh1.Name Then
:
End If
Next
Suggestion 3
You use:
.Range("A" & Rows.Count)
.Range("A3")
.Cells(1, col).Value
.Cells(Rows.Count, col)
rng2.Offset(0, 6)
All these methods of identifying a cell or a range have their purposes. However, I find it confusing to use more than one at a time. I find .Cells(row, column) and .Range(.Cells(row1, column1), .Cells(row2, column2)) to be the most versatile and use them unless there is a powerful reason to use one of the other methods.
Suggestion 4
I cannot decypher what this code is attempting to achieve.
You say: "I have several worksheets in a file and they are in order by dates. So what I am trying to do is to collect data sets in a worksheet if they have the same period of time."
If you have set Worksheet("combined").Range("A3").Value to a particular date and you want to collect data from all those sheets with the same value in cell A3 then the outer For-Loop and the If give this effect. But if so, if does not matter how the worksheets are ordered. Also you start checking cell values from row 2 which suggests row 3 is a regular data row.
The outer loop is for each worksheet, the next loop is for each row in "combined" and the inner loop is for each row in the worksheet selected by the outer loop. The middle loop does not appear to do anything but set rng1 which is not used.
Perhaps you can add an explanation of what you are trying to achieve.
Suggestion 5
Are you trying to add an entire column of values from the source worksheets to "Combined". The macro below:
Identifies the next free row in column A of "Combined"
Identifies the last used row in column A of "Sheet2"
Assumes the first interesting row of "Sheet2" is 2.
Adds the entire used range of column A of "Sheet2" (complete with formatting) to the end of "Combined"'s column A in a single statement.
This may demonstrate a better way of achieving the effect you seek.
Sub Test()
Dim RngSrc As Range
Dim RngDest As Range
Dim RowCombNext As Long
Dim RowSrcFirst As Long
Dim RowSrcLast As Long
With Worksheets("Combined")
RowCombNext = .Cells(Rows.Count, "A").End(xlUp).Row + 1
Set RngDest = .Cells(RowCombNext, "A")
End With
With Worksheets("Sheet2")
RowSrcFirst = 2
RowSrcLast = .Cells(Rows.Count, "A").End(xlUp).Row
Set RngSrc = .Range(.Cells(RowSrcFirst, "A"), .Cells(RowSrcLast, "A"))
End With
RngSrc.Copy Destination:=RngDest
End Sub