So I have this Excel workbook which basically is supposed to be a data sheet for work clothes for each employee. What I want is to have one sheet for each person (first name and last name), then one sheet for each clothing available.
Then one sheet to merge it all together. But, this would involve a repeat of each name as many times as there are unique cloth types. Hard to explain, but I've got a workbook available for download here:
http://www.mediafire.com/?dxurbdjq340su6j
Sheet2(Users) contains a simple list of each unique user. Sheet3(Articles) contains a list of each unique clothing article. And then finally we have the first Sheet(Summary) which contains a merged list of it all. Based on this Summary sheet, I'm gonna create a pivot table later. But first I need the functionality here to be dynamic. Whenever a new user or clothing is added or deleted, I only want to do this in Sheet2 or Sheet3. Whatever is in Sheet1 should collect the data dynamically.
Any idea? Would I have to create a macro to do this?
Edit: Ok, so I've created a module with two public Dictionaries. Here's the code I've got so far:
ThisWorkbook
Private Sub Workbook_Open()
' Create dictionaries
Set dictName = New Dictionary
Set dictClothItems = New Dictionary
' Collect data
collectDictName
collectDictClothItems
End Sub
Module1:
Public dictName As Dictionary
Public dictClothItems As Dictionary
Public Sub collectDictName()
Dim v As Variant
Dim rngName As Range
' Add values
With ThisWorkbook.Sheets(2)
Set rngName = .Range("A2", .Range("A" & Rows.Count).End(xlUp))
For Each strName In rngName
dictName.Add strName, ""
Next
End With
End Sub
Public Sub collectDictClothItems()
Dim v As Variant
Dim rngName As Range
' Add values
With ThisWorkbook.Sheets(3)
Set rngName = .Range("A2", .Range("A" & Rows.Count).End(xlUp))
For Each strClothName In rngName
dictClothItems.Add strClothName, ""
Next
End With
End Sub
So, here I collect the data and store these in an array. I wasn't sure how to collect the data from the range, so I used For Each strName In rngName. Looks like it collects other data that I really don't need as well. Can probably fix that up by creating a string variable and assigning the value I'm after to that. And then adding that string to the Dictionary. Anyways, now that the data's collected, I need to compare and add it to the actual Summary sheet now. Any idea on where to begin?
Question rephrasing
So basically, what you want is:
Each time you add a new value on either Sheet2 (Users) or Sheet3 (Clothes), you want Excel to dynamically add on Sheet1 (Summary) a new row for each value on the other Sheet (Users or Clothes depending on the one that was changed)
Btw, where does the amount come from?
Answer
Yet, to answer your question, the easiest way is to do it with VBA so it will be dynamic.
Here is what you can do:
add en event Worksheet_Change() on both Sheet2 and Sheet3 (see on Chip Pearson website how to do it)
this procedure will watch every change on these sheets. Every time a user add a value, you should loop over the other Sheet get the value from the other sheet and populate an array (Clothes or User depending on the value)
IMHO, the easiest way to consolidate the data is to create loop over your created array (or use a dictionary) and append every new row to your array
eventually, you can add the array in your Summary sheet
Don't hesitate to ask for some more information if you need or ask a new question with what you've tried if you are still stuck.
[EDIT 2] Here is a full working solution for articles (easily adaptable for users)
Code on Sheet3 (Articles)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim aSum() As String
Dim sArticle As String, sPrice As Long
Dim i As Integer, iLastrow As Integer
If Intersect(Range("A:B"), Target) Is Nothing Then Exit Sub
'if no price, exit sub
If Not Intersect(Range("A:A"), Target) Is Nothing Then
If Target.Offset(0, 1).Value = Empty Then Exit Sub
sArticle = Target.Value
sPrice = CLng(Target.Offset(0, 1).Value)
End If
'if no name, exit sub
If Not Intersect(Range("B:B"), Target) Is Nothing Then
If Target.Offset(0, -1).Value = Empty Then Exit Sub
sArticle = Target.Offset(0, -1).Value
sPrice = CLng(Target.Value)
End If
'get the names in an array
collectNames
'store the data in a new array so to map these values in the end
ReDim aSum(UBound(vNames) - 1, 3)
For i = 0 To UBound(vNames) - 1
aSum(i, 0) = vNames(i + 1, 1)
aSum(i, 1) = sArticle
aSum(i, 3) = sPrice
Next
'store the data back to the Summary sheet
With ThisWorkbook.Sheets("Summary")
iLastrow = .Range("A" & Rows.Count).End(xlUp).Row + 1
.Range("A" & iLastrow & ":D" & iLastrow + UBound(vNames) - 1).Value = aSum
End With
End Sub
Code in a module
Option Explicit
Public vNames As Variant
Public vClothes As Variant
Public Sub collectNames()
With ThisWorkbook.Sheets(2)
vNames = .Range("A2", .Range("A" & Rows.Count).End(xlUp))
End With
End Sub
Public Sub collectClothItems()
With ThisWorkbook.Sheets(3)
vClothes = .Range("A2", .Range("B" & Rows.Count).End(xlUp))
End With
End Sub
I didn't use dictionaries (maybe #Issun may have an idea here, he is a dict-expert) but arrays because it was easier to map to the sheet ranges.
Related
I have programmed a manual macro in Excel VBA that displays 2 or in the future multiple tables to show the history of certain data in a sheet called "evaluation". The data i reference to is in the table "checklist".(Look below) The problem is that the data in "checklist" changes every day or more often. Every time the sheet changes the macro should insert a new row with a new date into the LastRow of the table in "evaluation". I would like to display a history of the data in "evaluation". So the values in the row of the last change should stay stable. So for example row 1 in "evaluation": 2020-01-17 value is 1 (this should stay 1, because i want to see the progress) Now the sheet changes and row 2 gets inserted: row 2: 2020-01-18 value is now 2 (copied from checklist) and i want the value in row 1 to stay at 1 (because it was 1 before the last change).
This part works perfectly with my 1st code: (see below), but if I want to record the data of the second table too (code 2) nothing happens... Do I have to just make an adjustment to my first code or how is it done? Right now it looks like this:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal target As Range)
If Sh.Name = "checklist" Then
'Monitoring from A3:E100, if different change this
If Not Intersect(target, Range("A3:E3")) Is Nothing Then
'if any monitoring here, please you add here
Test target 'Here procedure to insert
End If
End If
End Sub
Private Sub Test(target As Range)
Dim LastRow As Long
LastRow = Range("evaluation!A" & Sheets("evaluation").Rows.Count).End(xlUp).Row
If Range("evaluation!A1").Value <> "" Then
LastRow = LastRow + 1
End If
'every change A3:E in checklist will insert row to this evaluation
'but if different please you decide here
Range("evaluation!A" & LastRow).Value = Format(Now, "dd.mm.yyyy hh:mm") 'you can change this
Range("evaluation!B" & LastRow & ":F" & LastRow).Value = Range("checklist!A" & target.Row & ":E" & target.Row).Value
End Sub
the first codes are for the first table and the one below is for the second table:
Private Sub Workbook_SheetChange2(ByVal Sh As Object, ByVal target As Range)
If Sh.Name = "checklist" Then
'Monitoring from A3:E100, if different change this
If Not Intersect(target, Range("G3:K3")) Is Nothing Then
'if any monitoring here, please you add here
Test target 'Here procedure to insert
End If
End If
End Sub
Private Sub Test2(target As Range)
Dim LastRow As Long
LastRow = Range("evaluation!H" & Sheets("evaluation").Rows.Count).End(xlUp).Row
If Range("evaluation!H1").Value <> "" Then
LastRow = LastRow + 1
End If
'every change A3:E in checklist will insert row to this evaluation
'but if different please you decide here
Range("evaluation!H" & LastRow).Value = Format(Now, "dd.mm.yyyy hh:mm") 'you can change this
Range("evaluation!I" & LastRow & ":M" & LastRow).Value = Range("checklist!G" & target.Row & ":K" & target.Row).Value
End Sub
Do you have any ideas how to connect these codes? Sorry I am not really a VBA expert. I made a google sheet to show what I actually mean, but I need this in excel VBA, the google sheet is just to visualize what I mean: https://docs.google.com/spreadsheets/d/1OU_95Lhf6p0ju2TLlz8xmTegHpzTYu4DW0_X57mObBc/edit#gid=0
I think you just forgot to add a "2". For your second code, it still calls Test instead of calling Test2.
I'll be happy to dig in, if that isn't the error. But since the first one works for you, the second should work too. Lets hope.
Edit after OPs comment:
I meant you called the sub "Test" twice and never actually called Test2 (also I didnt see the 2 on your second sheetchange).
Just merge the two SheetChanges and correctly call the TestX subs.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal target As Range)
If Sh.Name = "checklist" Then
'Monitoring from A3:E100, if different change this
If Not Intersect(target, Range("A3:E3")) Is Nothing Then
'if any monitoring here, please you add here
Test target 'Here procedure to insert
End If
If Not Intersect(target, Range("G3:K3")) Is Nothing Then
'if any monitoring here, please you add here
Test2 target 'Here procedure to insert
End If
End If
End Sub
This is my approach
Convert ranges to Excel Tables
Put the code behind checklist sheet
Checklist sheet
Table name in that sheet: TableCheckList
Evaluation sheet
Table names in that sheet TableHistory01 and TableHistory02
Code:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim checkListTable As ListObject
Dim checkListRow As ListRow
Set checkListTable = Range("TableCheckList").ListObject
If Intersect(Target, checkListTable.DataBodyRange) Is Nothing Then Exit Sub
Set checkListRow = checkListTable.ListRows(Target.Row - checkListTable.HeaderRowRange.Row)
AddHistory Target, "TableHistory01", checkListRow
AddHistory Target, "TableHistory02", checkListRow
End Sub
Private Sub AddHistory(ByVal Target As Range, ByVal HistoryTableName As String, ByVal checkListRow As ListRow)
Dim historyTable As ListObject
Dim newRow As ListRow
Set historyTable = ThisWorkbook.Worksheets("Evaluation").ListObjects(HistoryTableName)
' Add a row to that table
Set newRow = historyTable.ListRows.Add(alwaysInsert:=True)
' Fill the row with source values
With newRow
.Range.Cells(1).Value = Format(Now, "dd.mm.yyyy hh:mm")
.Range.Cells(2).Value = checkListRow.Range.Cells(1)
.Range.Cells(3).Value = checkListRow.Range.Cells(2)
.Range.Cells(4).Value = checkListRow.Range.Cells(3)
.Range.Cells(5).Value = checkListRow.Range.Cells(4)
.Range.Cells(6).Value = checkListRow.Range.Cells(5)
End With
End Sub
Some remarks:
Your code is adding rows everytime a cell is changed. Is that the intended purpose? Maybe when a row in checklist is changed/added?
You mentioned to record days, but your code is adding the time too
Here is a link to the sample
Some reference to about to listobjects (Excel tables)
Let me know if it works
i'm new to excal macros/vba, and i am encountering a problem which i do not know how to approach.
I have a workbook that includes several sheets. There is 1 file which is more or less a master list, and 3 files which are sort of a packing list.
I have put in a command button with a macro in the 3 packing list respectively that tells me if a certain item in the packing list exist in the master, and if it does it tells me which row it appears in. This is working fine, however my problem is that if a particular items appears several times in the master list(due to different purchase date), the macro only gives the first result.
I would like to know if there are any ways such that all possible results appears instead of just the first.
below is a sample of the code i used
Private Sub CommandButton1_Click()
Dim k As Integer
For k = 3 To 1000
Cells(k, 24).Value = Application.Match(Cells(k, 2), Sheets("master").Range("B2:B1000"), 0)
Next k
End Sub
if your "master" sheet data is a list of contiguous not empty cells from B2 down to last not empty one, then here's a different approach playing around a bit with
Option Explicit
Private Sub CommandButton1_Click()
Dim cell As Range
With Worksheets("master") ' reference your "master" sheet
With .Range("B2", .Cells(.Rows.Count, "B").End(xlUp)) ' reference referenced sheet column B range from row 2 down to last not empty one
For Each cell In Range("B3", Cells(Rows.Count, "B").End(xlUp)) ' loop through packinglist sheet (i.e. where button resides) column B cells from row 3 down to last not empty one
If Not .Find(what:=cell.Value2, LookIn:=xlValues, lookat:=xlWhole) Is Nothing Then ' if current packinglist item is in "master"
.Replace what:=cell.Value2, replacement:=vbNullString, lookat:=xlWhole ' temporarily replace master item with a blank
cell.Offset(, 22).Value2 = Replace(.SpecialCells(xlCellTypeBlanks).Address(False, False), "B", "") ' write master list blanks rows in packinglist sheet current item row and column "X"
.SpecialCells(xlCellTypeBlanks).Value = cell.Value2 ' restore master list current packinglist item value
End If
Next
End With
End With
End Sub
I would use a dictionary to store every item in the master sheet, and everytime you find it duplicate, add another number with its row like this:
Option Explicit
Private Sub CommandButton1_Click()
Dim MasterKeys As Object
MasterKeys = FillDictionary(MasterKeys)
With ThisWorkbook.Sheets("MySheet") 'change MySheet for your actual sheet name
Dim arr As Variant
arr = .UsedRange.Value 'drop your data inside an array
Dim i As Long
For i = 3 To UBound(arr) 'loop through all the rows in your data
If MasterKeys.Exists(arr(i, 2)) Then arr(i, 24) = MasterKeys(arr(i, 2))
Next i
.UsedRange.Value = arr 'drop back your data
End With
End Sub
Function FillDictionary(MasterKeys As Object) As Object
Set MasterKeys = CreateObject("Scripting.Dictionary")
With Workbooks("MasterWorkbook.xlsx").Sheets("master") 'change MasterWorkbook for the actual filename of your master workbook
Dim LastRow As Long
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row 'find the last row on column B
Dim C As Range
For Each C In .Range("B2:B" & LastRow) 'loop through the range
If Not MasterKeys.Exists(C.Value) Then
MasterKeys.Add C.Value, C.Row
Else
MasterKeys(C.Value) = MasterKeys(C.Value) & "," & C.Row
End If
Next C
End With
End Function
This question already has answers here:
Aggregate, Collate and Transpose rows into columns
(3 answers)
Closed 6 years ago.
I'm pretty new to stack overflow but I've been on here as a lurker before.
So I'm having trouble reorganizing this excel output. The original output is below. I've modified the output to preserve the confidentiality of the dataset and also in the interest of time as the dataset has over 10k cells, but the ideas should be clear.
Before
As you can see, there's a lot of duplicates and useless stuff and in general annoying bits. Basically I need to reorganize the data into column headers and repopulate the spreadsheet so that the data stays with the proper code number. The current column headers of supercatagory and subcategory are worthless. I've attached what I think would be the ideal here. After
I've tried using pivot tables and that kind of serves as a half measure but that would still require me to go through the output and copy and paste by hand for over 2 hours. I've also tried using transpose in excel and while that is good for the first part of the problem, making new column headers, but it doesn't solve the problem of repopulating the spreadsheet and keeping everything straight.
Thank you so much.
Without knowing more, the below code works for me in testing with the data provided in your images. The big question of course is where the column headers in the After data came from. It appeared to come from column B of the Before data. I assumed these would be duplicated for each unique value from column A. As such, in the below code, only the first set of values is used to set the headers of the newly created sheet.
Option Explicit
Sub TransposeWithUniques()
Dim SourceSheet As Worksheet
Dim TargetSheet As Worksheet
Dim Uniques As Collection
Dim Unique As Variant
Dim UniqueData() As Variant
Dim FormulaColumn As Range
Dim CriteriaColumn As Range
Dim DataRange As Range
Dim FoundRange As Range
Dim ValueIndex As Long
Dim LastRow As Long
Dim LastColumn As Long
Dim NewRow As Long
Dim ErrorFound As Boolean
Set SourceSheet = ActiveSheet '!!! This will need to be the currently active sheet housing your data
' If sheet is protected, exit
If SourceSheet.ProtectContents Then
MsgBox "Please unprotect the worksheet first.", vbExclamation, "Transpose with Uniques"
Exit Sub
End If
' Get last row/column
LastRow = SourceSheet.Cells(SourceSheet.Rows.Count, 1).End(xlUp).Row
LastColumn = SourceSheet.Cells(1, SourceSheet.Columns.Count).End(xlToLeft).Column
Set DataRange = SourceSheet.Range("A1", SourceSheet.Cells(LastRow, LastColumn))
NewRow = 1
' Get unique UniqueData from column A
UniqueData = SourceSheet.Range("A2:A" & LastRow).Value2
Set Uniques = New Collection
For ValueIndex = LBound(UniqueData, 1) To UBound(UniqueData, 1)
If InCollection(Uniques, CStr(UniqueData(ValueIndex, 1))) = False Then
Uniques.Add UniqueData(ValueIndex, 1), CStr(UniqueData(ValueIndex, 1))
End If
Next ValueIndex
' Set application properties for better code running experience
Application.DisplayAlerts = False
Application.EnableEvents = False
Application.ScreenUpdating = False
' Add helper columns
On Error GoTo TransposeWithUniques_Error
SourceSheet.Cells(1, LastColumn).Offset(0, 1).Resize(LastRow, 2).Insert
Set CriteriaColumn = SourceSheet.Cells(1, LastColumn).Offset(0, 1).Resize(LastRow, 1)
Set FormulaColumn = SourceSheet.Cells(1, LastColumn).Offset(0, 2).Resize(LastRow, 1)
FormulaColumn(1, 1).Value = "FORMULA"
CriteriaColumn(1, 1).Value = "CRITERIA"
FormulaColumn(2, 1).Resize(LastRow - 1, 1).Formula = "=ROW(A1)"
FormulaColumn(2, 1).Resize(LastRow - 1, 1).Value = FormulaColumn(2, 1).Resize(LastRow - 1, 1).Value
' Loop through all uniques, get data and move it
For Each Unique In Uniques
CriteriaColumn(2, 1).Resize(LastRow - 1, 1).Formula = "=1/(A2=" & Chr(34) & Unique & Chr(34) & ")"
CriteriaColumn(2, 1).Resize(LastRow - 1, 1).Value = CriteriaColumn(2, 1).Resize(LastRow - 1, 1).Value
DataRange.Resize(, DataRange.Columns.Count + 2).Sort Key1:=CriteriaColumn(1, 1), Order1:=xlAscending, Key2:=SourceSheet.Range("B1"), Order2:=xlAscending, Header:=xlYes
On Error Resume Next
Set FoundRange = CriteriaColumn.SpecialCells(xlCellTypeConstants, xlNumbers)
On Error GoTo 0
If Not FoundRange Is Nothing Then
If TargetSheet Is Nothing Then
Set TargetSheet = ActiveWorkbook.Worksheets.Add(After:=SourceSheet)
TargetSheet.Range("A1").Value = SourceSheet.Range("A1").Value
TargetSheet.Range("B1").Resize(1, FoundRange.Cells.Count).Value = Application.Transpose(Intersect(SourceSheet.Range("B:B"), FoundRange.EntireRow).Value)
End If
NewRow = NewRow + 1
TargetSheet.Cells(NewRow, 1).Value = Unique
TargetSheet.Cells(NewRow, 2).Resize(1, FoundRange.Cells.Count).Value = Application.Transpose(Intersect(SourceSheet.Range("C:C"), FoundRange.EntireRow).Value)
Set FoundRange = Nothing
End If
Next Unique
' Reset data to original state
DataRange.Resize(, DataRange.Columns.Count + 2).Sort Key1:=FormulaColumn(1, 1), Order1:=xlAscending, Header:=xlYes
FormulaColumn.Delete xlToLeft
CriteriaColumn.Delete xlToLeft
TransposeWithUniques_Exit:
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
If Not ErrorFound Then
MsgBox "Process completed successfully.", vbInformation, "Transpose with Uniques"
End If
Exit Sub
TransposeWithUniques_Error:
ErrorFound = True
MsgBox "Something went wrong.", vbExclamation, "Transpose with Uniques"
GoTo TransposeWithUniques_Exit
End Sub
Public Function InCollection(CheckCollection As Collection, CheckKey As String) As Boolean
'
' Returns True if the specified key is found in the specified collection.
'
' Syntax: InCollection(CheckCollection,CheckKey)
'
' Parameters: CheckCollection. Collection. Required. The collection to search in.
' CheckKey. String. Required. The string key to search in collection for.
'
On Error Resume Next
InCollection = CBool(Not IsEmpty(CheckCollection(CheckKey)))
On Error GoTo 0
End Function
To use the above code, in your file you want to run this on, press ALT+F11 to open the Visual Basic Editor (VBE). Press CTRL+R to show the Project Explorer (PE), generally this shows by default. Find your project in the PE and right-click it, select Insert, Module. Double click the newly inserted module (should be named Module1). Copy/paste the above code into this module. Click anywhere inside the top routine (for example, click on the text near the top "TransposeWithUniques" so your cursor is on that line, or just below it). Press F5 to run the routine.
CAUTION: Make sure you save a backup copy of your file prior to running this. It resets the data to its original state, but this is always good practice. Check the newly created sheet to ensure it's what you're looking for. If this isn't what you're looking for, please be as specific as possible in explaining the input versus output.
Regards,
Zack Barresse
I'm trying to loop through several worksheets that contain some source data that has to be copied to one main sheet, called "PriorityList" here.
First of all, the sub is not working and I think the error is somewhere in the "find"-method. Second, the sub takes quite long to run, and I think this is maybe because the "find"-method searches through the whole sheet instead of only the relevant range?
Thank you very much for your answers!
Patrick
Sub PriorityCheck()
'Sub module to actualise the PriorityList
Dim CurrWS As Long, StartWS As Long, EndWS As Long, ScheduleWS As Long
StartWS = Sheets("H_HS").Index
EndWS = Sheets("E_2").Index
Dim SourceCell As Range, Destcell As Range
For CurrWS = StartWS To EndWS
For Each SourceCell In Worksheets(CurrWS).Range("G4:G73")
On Error Resume Next
'Use of the find method
Set Destcell = Worksheets(CurrWS).Cells.Find(What:=SourceCell.Value, After:=Worksheets("PriorityList").Range("A1"), LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False)
'Copying relevant data from source sheet to main sheet
If Destcell <> Nothing Then
Destcell.Offset(0, 2).Value = SourceCell.Offset(0, 5).Value + Destcell.Offset(0, 2).Value
If SourceCell.Offset(0, 3).Value = "x" Then Destcell.Offset(0, 3).Value = "x"
End If
End If
On Error GoTo 0
Next SourceCell
Next CurrWS
End Sub
here short sample how to use 'Find' method to find the first occurrence of the source.Value in the priorityList.
Source cell is one of the cells from the range "G4:G73" and priorityList is used range on "PriorityList" sheet. Hope this helps.
Public Sub PriorityCheck()
Dim source As Range
Dim priorityList As Range
Dim result As Range
Set priorityList = Worksheets("PriorityList").UsedRange
Dim i As Long
For i = Worksheets("H_HS").Index To Worksheets("E_2").Index
For Each source In Worksheets(i).Range("G4:G73")
Set result = priorityList.Find(What:=source.Value)
If (Not result Is Nothing) Then
' do stuff with result here ...
Debug.Print result.Worksheet.Name & ", " & result.Address
End If
Next source
Next i
End Sub
Here is an approach using arrays. You save each range into an array, then iterate through array to satisfy your if-else condition. BTW IF you want to find the exact line with code error, then you must comment On Error Resume Next line.. :) Further, you can simply store the values into a new array, dump everything else into the main sheet later after iterating through all the sheets instead of going back and forth to sheets, code, sheets..code..
Dim sourceArray as Variant, priorityArray as Variant
'-- specify the correct priority List range here
'-- if multi-column then use following method
priorityArray = Worksheets(CurrWS).Range("A1:B10").Value
'-- if single column use this method
' priorityArray = WorkSheetFunction.Transpose(Worksheets(CurrWS).Range("A1:A10").Value)
For CurrWS = StartWS To EndWS
On Error Resume Next
sourceArray = Worksheets(CurrWS).Range("G4:J73").Value
For i = Lbound(sourceArray,1) to UBound(sourceArray,1)
For j = Lbound(priorityArray,1) to UBound(priorityArray,1)
If Not IsEmpty(vArr(i,1)) Then '-- use first column
'-- do your validations here..
'-- offset(0,3) refers to J column from G column, that means
'---- sourceArray(i,3)...
'-- you can either choose to update priority List sheet here or
'---- you may copy data into a new array which is same size as priorityArray
'------ as you deem..
End If
Next j
Next i
Next CurrWS
PS: Not front of a MS Excel installed machine to try this out. So treat above as a code un-tested. For the same reason I couldn't run your find method. But it seems odd. Don't forget when using match or find it's important to do proper error handling. Try checking out [find based solutions provided here.
VBA in find function runtime error 91
Excel 2007 VBA find function. Trying to find data between two sheets and put it in a third sheet
I have edited the initial code to include the main logic using two array. Since you need to refer to values in J column of source sheets, you will need to adjust source array into a two-dimensional array. So you can do the validations using first column and then retrieve data as you desire.
For everyone maybe interested, this is the code version that I finally used (pretty similar to the version suggested by Daniel Dusek):
Sub PriorityCheck()
Dim Source As Range
Dim PriorityList As Range
Dim Dest As Range
Set PriorityList = Worksheets("PriorityList").UsedRange
Dim i As Long
For i = Worksheets("H_HS").Index To Worksheets("S_14").Index
For Each Source In Worksheets(i).Range("G4:G73")
If Source <> "" Then
Set Dest = PriorityList.Find(What:=Source.Value)
If Not Dest Is Nothing Then
If Dest <> "" Then
Dest.Offset(0, 2).ClearContents
Dest.Offset(0, 2).Value = Source.Offset(0, 5).Value + Dest.Offset(0, 2).Value
End If
If Source.Offset(0, 3).Value = "x" Then Dest.Offset(0, 3).Value = "x"
Debug.Print Dest.Worksheet.Name & ", " & Dest.Address
End If
End If
Next Source
Next i
MsgBox "Update Priority List completed!"
End Sub
I have a quandary, and I don't know if it will work better using excel VBA or not. Thinking about it I believe VBA will work best, but I don't know how to make it work.
I have two pages in a workbook, one is the form, the other is the database, I want the pulldown menu from the form to populate the rest of the form. It does... what I want then is to be able to change the value of the form press submit, and the new data will overwrite the old data.
Is this possible?
Here is the link to the sheet I'm talking about.
http://dl.dropbox.com/u/3327208/Excel/Change.xlsx
Here is the script I am working with now...it takes the sheet, copies everything to a row takes that row, moves it to the NCMR Data tab and then clears the data on the new row from the original sheet.
This code technically could work, but what I need to do is make it use the same concept, but instead of creating a new row at the end of the sheet find the original line and replace the data from B to U in whatever row it was originally in.
I know it's possible, I just don't know how.
'Copy Ranges Variable
Dim c As Variant
'Paste Ranges Variable
Dim p As Range
'Setting Sheet
Set wsInt = Sheets("Form")
Set wsNDA = Sheets("Data")
Set p = wsInt.Range("A14")
With wsInt
c = Array(.Range("B11"))
End With
For i = LBound(c) To UBound(c)
p(i + 1).Value = c(i).Value
Next
With wsNDA
Dim Lastrow As Long
Lastrow = .Range("B" & Rows.Count).End(xlUp).Row + 1
wsInt.Rows("14").Copy
With .Rows(Lastrow)
.PasteSpecial Paste:=xlPasteFormats
.PasteSpecial Paste:=xlPasteValues
.Interior.Pattern = xlNone
End With
With .Range("A" & Lastrow)
If Lastrow = 3 Then
.Value = 1
Else
.Value = Val(wsNDA.Range("A" & Lastrow - 1).Value) + 1
End If
.NumberFormat = "0#######"
End With
End With
End Sub
I found this code:
Sub CopyTest()
Dim selrow As Range, rngToCopy As Range
With Worksheets("PD DB")
Set selrow = .Range("B:B").Find(.Range("BA1").Value)
'find the cell containing the value
Set rngToCopy = Union(selrow.Offset(0, 9), selrow.Offset(0, 12))
'use offset to define the ranges to be copied
rngToCopy.Copy Destination:=Worksheets("Edit Sheet").Range("B50")
'copy and paste (without Select)
End With
End Sub
As far as I can tell this will do what I want mostly, but I can't seem to figure out where to break it up to add it where I need to to make it work the way I want it to.
What I can tell is this, it will copy and paste, but I want to make sure it will paste the data into row it finds, and not overwrite the number of said row.
Can someone help make that possible with the two scripts I have here?
Not tested, but should get you started. I added a 3rd sheet (shtMap) to hold the mmapping between the cell addresses on your form and the column numbers on the "Data" sheet. Useful to name your sheets directly in the VB editor: select the sheet and set the name in the property grid.
*EDIT:*If you want to trigger the transfer on selecting a record id from a list in Range AG3 then place this code in the code module for that worksheet:
Private Sub Worksheet_Change(ByVal Target As Range)
Static bProcessing As Boolean
Dim rng As Range
If bProcessing Then Exit Sub
Set rng = Target.Cells(1)
If Not Application.Intersect(rng, Me.Range("AG3")) Is Nothing Then
bProcessing = True
'this is where you call your macro to transfer the record
bProcessing = False
End If
End Sub
You could use something like this for the transfer:
Public Enum XferDirection
ToForm = 1
ToDataSheet = 2
End Enum
Sub FetchRecord()
TransferData XferDirection.ToForm
End Sub
Sub SaveRecord()
TransferData XferDirection.ToDataSheet
End Sub
Sub TransferData(Direction As XferDirection)
Dim rngMap As Range, rw As Range, f As Range, dataCell As Range
Dim formCell As Range, dataCol As Long, dataRow As Long
Dim sId As String
sId = shtForm.Range("AG3").Value
Set f = shtData.Columns(1).Find(sId, LookIn:=xlValues, lookat:=xlWhole)
If Not f Is Nothing Then
dataRow = f.Row
Else
'what do you want to do here?
' record doesn't exist on data sheet
MsgBox "Record '" & sId & "' not found on '" & shtForm.Name & "' !"
Exit Sub
End If
Set rngMap = shtMap.Range("A2:B10")
For Each rw In rngMap.Rows
'the cell on the edit form
Set formCell = shtForm.Range(rw.Cells(1).Value)
'column # on datasheet
Set dataCell = shtData.Cells(dataRow, rw.Cells(2).Value)
If Direction = XferDirection.ToDataSheet Then
dataCell.Value = formCell.Value
Else
formCell.Value = dataCell.Value
End If
Next rw
End Sub
Matt, there are two approaches I would take. The first is use find(), which returns a range object, then append ".row" so that you'll be able to modify the row on Sheet2 (wsNDA, I think). You may want to test that find() doesn't return Nothing.
Dim foundRow as Long
Dim foundRng as Range
set foundRng = wsNDA.find(wsInt.Range("B11").Value, ...)
If Not foundRng is Nothing Then
foundRow = foundRng.row
End If
'method without check: foundRow = wsNDA.find(wsInt.Range("B11").Value, ...).Row
The other is to use a Dictionary object. I'm not sure what you'd want for the key, but the item could be the row on the data sheet. When you make the change to what's on the form, check against the key and grab its item (the corresponding row) to determine where you need to replace the values.