I have a file in excel. For each line, I have to create a unique identifier. It must be created permanently. The problem is for example: I have the same files on computers A and B. I'm filling same lines in each file. But the identifiers of those lines should be different. Please help me. If you know how to do that please give me an example of macro or formula
Try this.
=CONCATENATE(DEC2HEX(RANDBETWEEN(0,4294967295),8),"-",DEC2HEX(RANDBETWEEN(0,65535),4),"-",DEC2HEX(RANDBETWEEN(16384,20479),4),"-",DEC2HEX(RANDBETWEEN(32768,49151),4),"-",DEC2HEX(RANDBETWEEN(0,65535),4),DEC2HEX(RANDBETWEEN(0,4294967295),8))
This a copy of How can I generate GUIDs in Excel? with the semi colons replaced with commas. What you are looking for is a Global Unique Identifier or GUID. If you search for excel and GUID you can get a lot more options.
If you want to be able to get a reference of the row and/or columns out of this so you can compare the two files, you can append
& "-" & ROW()&COLUMN()
to the end of the above. This will effectively give you and address of the row and column being referenced.
Not the most elegant way of doing things, but I believe this somewhat answers your question.
If you can make sure that both files are Macro enabled, the user doesn't need to know how to prefix them, simply use something like below to add the ID to whatever column you want as new data is entered in column B in my example below:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
'declare and set your worksheet, amend as required
LastRow = ws.Cells(ws.Rows.Count, "B").End(xlUp).Row
'get the last row with data on Column A
If Target.Address = "$B$" & LastRow Then
For i = 2 To LastRow
ws.Cells(i, 1).Value = "A" & Format(i, "000")
Next i
End If
End Sub
Do the same in the other file, just change the "A" to "B"
Related
My problem is how to populate the cmbSelProp ComboBox with propIDs from a workbook scoped Named Range called PropLocs. The columns of interest Column "A" which contains the list of propIDs and Column "K" which contains the ActiveStatus of each propID. I also have Named Ranges for both columns of interest, propIDs and actStatus respectively. The actStatus, Column "K", portion of the range are set to True/False when the properties are added and subsequently initialized and annual worksheets are automatically generated.
I only want cmbRptPrpID populated with propIDs having an actStatus = True.
I have spent many hours over the past few weeks going blind doing Web Searches, looking at links, and trying suggestions from the various links without success. I,m as "Lost as a blind man in a snow storm!"
There are broader issues associated with the problem I am dealing with and understanding how to solve, and understand, the issue at hand will help me in the future development of my application.
UPDATE
the 2 mentioned additional ranges are in columns "A" and "K"
Update 2
Business Logic
The application I am developing utilizes a multipage object and there are pairs of dynamic comboboxes, cmbSelProp and cmbYears for example, used to select the active worksheet and enter Monthly expenses, view/generate Reports, etc. cbmSelPropselects the property ID, and the cbmSplProp_Change() event configures cmbYears list values based on the variable wsStartYr in column "K" and wbCurYear which is the Current Calendar Year. the annual worksheets have a Worksheet TabName of propId_Year and are selected using a variable wsA = pID & "_" & wsYr. I do not want propIDs with an Inactive status to appear as a part of the cmbSelProp list values.
This is a link to My First Question here on StakOverflow which is related to this question.
I really need some help figuring this out!
Thanks in advance for your assistance.
here is the code I have been trying to work with.
selectedRow = cmbSelProp.ListIndex + 3
For Each cPart In wsCntrl.Range(Range("propIDs"), Range("A" & Rows.Count).End(xlUp))
pAct = wsCntrl.Cells(selectedRow, "K").Value
With Me.cmbSelProp
If pAct = "True" And cPart.Value <> "" Then cmbSelProp.AddItem cPart.Value
End With
Next cPart
There are a number of issues in your code. Rather than breaking down all the errors/issues, I'll show you how I would do it.
From your previous Q, I take it that your Combo Box is on a Userform.
I've created a Userform with a combo box and a button to load it. You can change how you call the load function to suit your needs.
The UserForm code
Option Explicit
Private Sub btnTest_Click()
Load_cmbSelProp
End Sub
Private Sub Load_cmbSelProp()
Dim propIDs As Range
Dim actStatus As Range
Dim rw As Long
Set propIDs = ThisWorkbook.Names("propIDs").RefersToRange
Set actStatus = ThisWorkbook.Names("actStatus").RefersToRange
Me.cmbSelProp.Clear
For rw = 1 To propIDs.Count
If propIDs.Cells(rw, 1).Value2 <> vbNullString Then
If actStatus.Cells(rw, 1).Value2 = True Then
Me.cmbSelProp.AddItem propIDs.Cells(rw, 1).Value2
End If
End If
Next
End Sub
The Named ranges
The result
At my job I have automated a very manual task and to the point they have wanted to expand it to the other department where they work a little differently. So the goal of this is I want to be able to filter column "A" and then filter another column based off of phrases that I already have in place as well. The data in column "A" I would have a source in another sheet, but it would have around potentially 200-400 possibilities to look for. After it filters column A I then want it go to column "AG" and then do another loop filtering based off of provided key phrases that the analyst would select based on a data validation. Once it filters those two criteria I then have the codes in place to generate the spreadsheets for the analyst. The code below is an example of the first block, I have 4 other codes that are pretty much the same they just generate different templates, I had to do multiple codes cause I didn't know how to do a loop based off of a source.
Sub Generate_Consolidated_Update_Reports()
Dim EDI As String
EDI = Environ("USERPROFILE") & "\desktop\foldername\foldername2\foldername3\filename " &
Format(Now(), "MM.DD.YY")
Workbooks("Master(where the filtering happens)").Activate
'The next line of code I am just doing the second filtering which is based off of a data validation
'I created, essentially the analyst would just select a key phrase and then that would
'prompt the code to generate the template, I haven't figured out how to do it based off of
'the source that's why I just have the specific name in the key range
'The first step to the code would need to be looking for anything in column "A", but like
'I said that could be anywhere from 200-400 possibilities. I have access to it, though and have
'it listed in "Sheet2" along with the phrases in column "AG" as well.
ActiveWorkbook.Worksheets("Master").Sort.SortFields.Add Key:=Range("AG:AG" _
), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
With Selection
.autofilter Field:=33, Criteria1:="Send to EDI Team - Account Level"
.Offset(1, 0).Select
End With
'The next line of code will be seeing if it is empty, I realized that it would work once,
'but if there ever was data the next line of looking for empty would always
'just filter anyways and keep creating the template even though there was no data
'so I did this route where if it saw it was empty I called another code, would be awesome
'if I could figure that out too!
'This then goes on repeat down the line, it's 5 different codes, so it isn't clean. :/
'so I only did one so you wouldn't see a ton of fluff!
Dim Lr As Long
Lr = Range("A" & Rows.Count).End(xlUp).Row
If Lr > 1 Then
Workbooks.Add
ActiveWorkbook.SaveAs EDI & ".XLS"
Sheets("Sheet1").Select
Sheets("Sheet1").Name = "EDI Account Update"
'Redacted code, just fluff on creating the template for the analyst
'Next line is just doing the code to show all the data again
'Then within that if statement to then call another sub that is essentially the same process
'If the code doesn't find it it goes to else where it then just calls the other sub
If ActiveSheet.AutoFilterMode Then ActiveSheet.ShowAllData
Range("A1").Select
Call Create_EDI_Update_GroupLevel
Else
ActiveSheet.ShowAllData
Range("A1").Select
Call Create_EDI_Update_GroupLevel
End If
End Sub
The reason I have to also call other subs is because each criteria they select will generate a completely different template based on company policies and such.
Really sorry if this isn't clear, I am still learning coding and just having a hard time trying to explain exactly what I needed. Thank you so much for your time.
I wanted to add a comment but not enough reputation yet.
One question I have is: Are you filtering on one phrase among 200-400 or can it be multiple?
I can't be sure, however an Advanced Filter might help you in this case. It is not possible for me to go into all the details. There is a very good tutorial on Youtube about this (where I learned myself): VBA Advanced Filter - The FASTEST way to Copy and Filter Data
You can also use "Worksheet_Change" event to fill in the filter. Couple it with:
If Not Application.Intersect(Target,rngRange) is Nothing Then
' Your code here
' Where rngRange is a Range object that you want the change event to catch
End If
Another note is, you can use (considering data starts at "A1") Range("A1").CurrentRegion if you don't have any completely empty rows within your data instead of .End(xlUp). Actually you can use CurrentRegion in any cell that is inside the data range. Check out the behaviour by pressing CTRL + * after selecting a cell.
Dim rngData as Range
Set rngData = ws.Range("A1").CurrentRegion
' ws can be a worksheet object you can set alike range above or worksheet codename
' or Worksheet("SheetName")
' Then rngData.Row will give the top row of rngData
' rngData.Column will give you you the leftmost column
' rngData.Rows.Count will give the number of rows
' rngData.Columns.Count will give the number of columns
' rngData.resize() and rngData.Offset() helps a lot too, can be used in same line
' i.e., rngData.offset(2,0).resize(1,1) : move the range 2 rows down, resize to 1 row,1 column
' Do whatever with rngData
' After you are done clean up, doesn't matter most of the time in VBA but this is better
' from what I have read/learned from others
Set rngData = Nothing
This may not be the exact answer you are looking for, but may get you started. The video is 20 minutes long, see if it can be used in your case.
Edit 1: To further clarify
rngData part is more a general approach. You haven't said anything about being able to use Advanced Filtering but here how I would do it.
Assume, we have 3 sheets. wsData, wsFilter, wsReport.
wsData would be the sheet where data is entered.
wsFilter would only hold the filter criteria.
wsReport would be the sheet that you will export.
wsData, as an example assume row 1 is the header row.
wsFilter would be only 2 columns (with headers corresponding to A and AG), corresponding to Column A and AG.
On wsReport, you only have to clear contents and enter the column headers from wsData that you would want to appear on the customized report.
You will need 3 ranges, 1 for each worksheet. i.e., rngData, rngFilter, rngReport
The code to creating the report is easy as:
rngData.AdvancedFilter xlFilterCopy, rngFilter, rngReport
(I won't get into how to decide on the ranges as the video does it better than I would be able to).
Then next step should be: How to enter the filter criteria?
I'm using VBA code to write to cells in excel. For eg.
Range("C3") = code
Or
Cells(3,3) = code
If a row is inserted in the sheet, the code does then not update accordingly and would still write to Range("C3") etc. So the code then writes to the incorrect cell.
Is there a better way I can structure my code so it will update accordingly? Perhaps using a table instead of cells?
One solution is to used Named Ranges. You can define a Named Range in Formula Tab by clicking on Name Manager.
Then you will write.
Range("Name of the Range") = code
My believe is that named ranges update automatically when a row or column is inserted, so your code will print the value in the correct cell.
Thanks, good idea. I ended up defining the column filled with values as a range, then use a for loop to search for the defined string. That way it doesnt matter what row it is in aslong as the name and string doesnt change (Using a Named Range will probably be better for that exact reason).
Worksheets("Sheet1").Select
Set WS = ActiveSheet
With WS
LastRow = .Cells(.Rows.Count, 2).End(xlUp).Row 'Determine the last row number with data in it for column B
For Each acell In .Range("B1:B" & LastRow) 'Defines the search range from B1 to last row
If acell.Value = "String Searched For" Then
'Do stuff based on found cell location
End If
If acell.Value = "String Searched For#2" Then
'Do stuff based on found cell location#2
End If
Next
End With
I have a column number , say columnNumber = 4 . I need the used range of this column. I know how to find the last used row, and I could convert the column number to a column number like so
ColumnLetter = Split(Cells(1, ColumnNumber).Address, "$")(1)
LastRow = sht.Cells(sht.Rows.Count, ColumnLetter).End(xlUp).Row
and then build an address like so
rngaddy = ColumnLetter & "1:" & ColumnLetter & LastRow
and finally do
Range(rngaddy)
But is there an easier way to find the complete used range of a column given it's number ?
Dim rngaddy As Range
With Sheet1
Set rngaddy = .Range(.Cells(1, 4), .Cells(.Rows.Count, 4).End(xlUp))
End With
and if, for some reason, you want to see the address in A1 notation, merely:
debug.print rngaddy.address
Note that in doing it this way, rngaddy is, itself, the range object and not a string. So no need to do Range(rngaddy)
You could return the last populated cell is in columns # col with this:
MsgBox Cells(sht.Rows.Count,col).End(xlUp).Address
If you want to return the first populated cell as well, you could use:
MsgBox IIf(IsEmpty(Cells(1,col)),Cells(1,col).End(xlDown),Cells(1,col)).Address
Therefore this would return only the "used" range of Column #4 (D):
Sub Example_GetUsedRangeOfColumn()
Const col = 4
Dim sht As Worksheet
Set sht = Sheets("Sheet1")
MsgBox Range(IIf(IsEmpty(Cells(1, col)), Cells(1, col).End(xlDown), _
Cells(1, col)), Cells(sht.Rows.Count, col).End(xlUp)).Address
End Sub
So with this example:
...the above procedure would return: .
My preferred method is to use ListObjects aka Excel Tables to hold any input data whenever I possibly can. ListObjects are named ranges that Excel automatically maintains on your behalf, and because they grow automatically when new data is added, they give you a very robust way of referencing ranges in Excel from VBA, that is more immune to users doing things that might otherwise break code reliant on the .End(xlUp) approach.
? Range("MyTable").ListObject.ListColumns("Column 1").DataBodyRange.Address
$A$3:$A$7
Often I'll give the column concerned a named range of its own, in case the user (or a developer) later wants to change the Table column name, and use that name in my code instead.
? Range("FirstColumn").Address
$A$3:$A$7
If somebody (perhaps me) adds rows/columns above/left of the range of interest or shuffles the order of Table columns around, or changes the name of a column, the code still references the intended range and doesn't need to be changed.
? Range("FirstColumn").Address
$C$4:$C$8
? Range(Range("FirstColumn").Address & ":" & Range("FirstColumn").EntireColumn.cells(1).address).Address
$C$1:$C$8
Granted, that method of getting the range from the top cell (which may be above the ListObject) to the bottom of the column concerned is kinda long, but once you start using ListObjects more in your code you normally don't care what is above or below them...you just want the goods held inside.
I haven't used .End(xlUp) in years, other than to find where my data ends should I be in the process of turning it into a ListObject. But I'm a ListObject evangelist...your mileage may vary :-)
to get the real UsedRange of a columns you could use:
With Columns(columnNumber).SpecialCells(xlCellTypeConstants)
Set rngaddy = .Parent.Range(.Areas(1), .Areas(.Areas.Count))
End With
where rngaddy is a Range object
of course what above would fail if the column has no "constant" cells, then you may want to add some error trapping or entry check (e.g. If WorksheetFunction.CountA(Columns(columnNumber)) = 0 Then Exit Sub
Or
Option Explicit
Public Sub test()
Const columnNumber As Long = 4
Dim rngaddy As Range
Set rngaddy = Intersect(Columns(2), ActiveSheet.UsedRange): Debug.Print rngaddy.Address
End Sub
Is it possible to include the number of rows in an Excel worksheet (that contain data) in the worksheet name? Currently the names look like "Widgets", I would like them to look like: "Widgets (n)" The first row is a header, so the total number of rows would be n-1. Thank you.
Perhaps:
Sub range_reporter()
Dim r As Range
Dim nLastRow As Long
ActiveSheet.UsedRange
Set r = ActiveSheet.UsedRange
nLastRow = r.Rows.Count + r.Row - 1
ActiveSheet.Name = ActiveSheet.Name & "(" & nLastRow - 1 & ")"
End Sub
However the count will include rows that may be empty within non-empty rows.
The only way I could suggest would be to save through a Macro. That way you can get the row count (there is a variety of ways no doubt discussed here) and create a save string that appends the row count.
The only downside to this would be that it would create a new file each time (as the file name would change.) You could move the old ones using the same macro to an archive folder or delete them - but this would be something to bear in mind.
Hope that this helps