I'm kinda new here, but here is what I'm trying to do.
I have a book lets pretend its a warehouse book for inventory, and we have different divisions in our enterprise, I have master sheet with all the goods and some sheets covering those divisions for distribution of goods between them.
My idea is to have a drop down list for each item type in book for separate divisions so i need macro to assign/reassign named range for each item.
I have 2 columns first with stock number and second with serial number , i need to put all the same serial number in the named range of one of stock numbers. if i have two or more serial numbers i need to put an array of serial numbers in named range of one stock number.
Stock numbers are in C column and serial numbers are in D column
I've tried this code
Private Sub CommandButton2_Click()
Dim LastRow As Long
Dim r As Range
LastRow = Cells(Rows.Count, "C").End(xlUp).Row
For Each r In Range("C2:C" & LastRow)
Range(r.Offset(0, 1), r.Offset(0, 1)).Name = r.Value
Next r
End Sub
But thats not realy working, and assigns only one serial number per one named range of stock numbers
================================================================
So i ran this code you proposed in your updated version and struck some problems
Private Sub CommandButton2_Click()
Dim this As Worksheet: Set this = Sheets("ALFA")'renamed this for my book'
Dim that As Worksheet: Set that = Sheets("STORAGE")'renamed that for my book'
serialNumbers = that.Range(that.Columns(3), that.Columns(4))'Could not find method Unique(and there is no mentions about'
'it in MS documentation) for Application object so i changed it to just Range'
For r = 2 To this.UsedRange.Rows.Count
buffer = ""
comma = ""
stockNumber = this.Cells(r, 3)
For x = 2 To UBound(serialNumbers)
If serialNumbers(x, 1) = stockNumber Then
buffer = buffer & comma & serialNumbers(x, 2)
comma = ","
End If
Next
this.Cells(r, 4).Validation.Delete
this.Cells(r, 4).Validation.Add _'After doing everything it strucks with Run time error 1004
Type:=xlValidateList, _ '/Application-defined or object-defined error in this
AlertStyle:=xlValidAlertStop, _'hole'
Formula1:=buffer 'block'
Next
End Sub
And sometimes code just hangs my excel application for atleast 3 mins, i think it's because there is no limit for cells to look up to and eventualy it tries to give all the cells in D:D a validation check
So if you want to set the validation, it is possible to set dynamic ranges BUT the validation won't accept a text list, for instance "one, two, three". The validation is looking for an array of values, and unfortunately it is tricky to pass a dynamic array using formulas only. You can set it up to do a dynamic range, but that would have to point to a range of cells that contain the needed values one per cell.
However, before you set all that up it's probably just easier to set the validation entirely in code. See this google sheet, which just contains the layout for reference. I have the complete list of items in Column 1 & 2 of the sheet (Item, Stock Number) and the complete list of serial numbers in columns 5 & 6 (Stock Number, Serial Number).
Then I run this code:
Sub setValidation()
Dim this As Worksheet: Set this = Sheets("demo")
Dim that As Worksheet: Set that = Sheets("lookups")
serialNumbers = Application.Unique(that.Range(that.Columns(5), that.Columns(6)))
For r = 2 To this.UsedRange.Rows.Count
buffer = ""
comma = ""
stockNumber = this.Cells(r, 3)
For x = 2 To UBound(serialNumbers)
If serialNumbers(x, 1) = stockNumber Then
buffer = buffer & comma & serialNumbers(x, 2)
comma = ","
End If
Next
this.Cells(r, 4).Validation.Delete
this.Cells(r, 4).Validation.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Formula1:=buffer
Next
End Sub
We assign some worksheet variables to make it easier to reference them, and then put the stock number/serial number combos into an array (with UNIQUE so I don't have to check for duplicates).
Then I run through the range that needs the validations (demo column 4), getting the stock number from column 3 and then using that stock number to find all serial numbers that match, concatenating them into a string and then using that string to set the validation.
Use Validation.Delete before setting the validation to avoid stacking rules.
Assuming that your version of Excel doesn't have UNIQUE, you can use INTERSECT to control the size of the serialNumbers array, like this:
Sub setValidation()
Dim this As Worksheet: Set this = Sheets("demo")
Dim that As Worksheet: Set that = Sheets("lookups")
serialNumbers = Intersect( _
that.Range(that.Columns(5), that.Columns(6)), _
that.UsedRange _
)
For r = 2 To this.UsedRange.Rows.Count
buffer = ""
comma = ""
stockNumber = this.Cells(r, 3)
For x = 2 To UBound(serialNumbers)
If serialNumbers(x, 1) = stockNumber Then
buffer = buffer & comma & serialNumbers(x, 2)
comma = ","
End If
Next
this.Cells(r, 4).Validation.Delete
this.Cells(r, 4).Validation.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Formula1:=buffer
Next
End Sub
Assuming you do have UNIQUE and FILTER in your Excel version, there is another way to do it, using the EVALUATE function to access the Excel function engine. In this case we will just write out a formula just like we would in a cell, and then evaluate it from VBA. Unless specified, evaluate occurs in the context of the active sheet, so that's what I use that.evaluate in this code:
Sub setValidation()
Dim expr As String
Dim this As Worksheet: Set this = Sheets("demo")
Dim that As Worksheet: Set that = Sheets("lookups")
For r = 2 To this.UsedRange.Rows.Count
stockNumber = this.Cells(r, 3)
expr = "Textjoin("","", true, Unique(Filter(F:F, E:E=""" & stockNumber & """)))"
serialNumbers = that.Evaluate(expr)
this.Cells(r, 4).Validation.Delete
this.Cells(r, 4).Validation.Add _
Type:=xlValidateList, _
AlertStyle:=xlValidAlertStop, _
Formula1:=serialNumbers
Next
End Sub
In this case, we use FILTER to return ONLY the serial numbers that match a stock number, UNIQUE to make sure there are no duplicates, and then TEXTJOIN to create a list from that, and then we can just pass that result straight to the validation.
===================================================
Original answer, shows how to get a list of all serial numbers for a specific stock number using only excel formulas, but it became clear that this wouldn't be sufficient, since the lists were going to be used to set validation. Left here for completeness.
So you have two columns, C and D, and you need to get a list of all values in D that match the entries in C. This is actually simple enough to not need code, but you may have more requirements. I'm going to start an answer with just a very basic set of formulas. See this google sheet.
To get a unique list of the stock numbers, I have used UNIQUE(C:C) in G1. This will produce the list in column G.
Then in column H, I have used TEXTJOIN+UNIQUE+FILTER to produce a comma separated list of serial numbers. FILTER takes one input array (in this case Col D) and filters it with another array (Col C) and a condition (the serial number) to return a list of matches, and wrapping that in UNIQUE makes sure that the result array contains only unique values. Wrapping that in TEXTJOIN converts the result array into text.
What I'm not entirely clear on is your need for a named range, or what you will do with the multiple rows in a sheet. For instance, STORAGE rows 35 & 36 are both for DDG_33:
DDG_33 0BV1111
DDG_33 0AV0951
and if you ran some code to produce a list of values and put it in D35 you would have:
DDG_33 0BV1111, 0AV0951
DDG_33 0AV0951
but you would still have two entries for DDG_33. If you ran the code again, you would have
DDG_33 0BV1111, 0AV0951, 0AV0951
DDG_33 0AV0951
and so forth in an infinite loop. It seems like you would need to take the list of unique stock numbers and put them on a different sheet, along with the list of matching serial numbers, but just clarify what you want to do and I can update my answer.
Related
For instance, I would like these 6 numbers.
Currently when I use the sort method it puts 6.6.1.1.13 first and 6.6.1.1.2 later.
Before Sort
6.6.1.1
6.6.1.1.1
6.6.1.1.13
6.6.11.14
► 6.6.1.1.2
What I Want It To Look Like After Sort
6.6.1.1
6.6.1.1.1
► 6.6.1.1.2
6.6.1.1.13
6.6.11.14
Unfortunately the only fully general way to sort outline numbers (or more formally, "path-indexes") is with a custom comparison function. Unfortunately, the Excel sorting operations and functions do not support such a feature (not even from VBA). The Excel preferred way is with custom lists, but these are not workable for path-indexes.
This leaves two choices:
Do your sorting entirely in VBA: This works (I've done it) but is pretty involved and messy. Or,
Use a Helper Column with a VBA function: This works but is not fully general, because you have to know ahead of time what the maximum index values will be.
of these, #2 above is by far the simpler option, but it does have limitations (explained below).
Basically what we want is a VBA function that can take a string like "6.6.11.14" and make it always sortable in path index order. The problem with this string is that in text order two digit indexes like ".11" and ".14" come before ".2" rather than after it.
The obvious way to fix this is to fix this is to convert all indexes into 2-digit numbers with leading zeroes. So, 6.6.11.14 would become 06.06.11.14 and crucially 6.6.2.1 would become 06.06.02.01. Now these two path-index values will sort correctly use text sorting.
The catch, however, is that this is only true if each individual index number is never greater than two digits (99). Thus, 06.07.99 sorts correctly, but 06.07.110 does not under this scheme. This is easily fixable by simply raising it from two digits to three digits, but again, the catch is that you have to know this ahead of time.
So assuming that we do know ahead of time what the maximum size/(number of digits) will be for any single index number, we can use the following VBA function to reformat your outline numbers for a helper column:
Public Function OutlineSortingFormat(OutlineNumber As String, Digits As Integer) As String
Dim PathIndexes() As String
Dim Zeroes As String
Dim i As Integer
Zeroes = "0000000000"
PathIndexes = Split(OutlineNumber, ".")
For i = 0 To UBound(PathIndexes)
PathIndexes(i) = Right(Zeroes & PathIndexes(i), Digits)
Next i
OutlineSortingFormat = Join(PathIndexes, ".")
End Function
This just splits the outline number into individual numeric strings, prefixes the correct amount of zeroes and then concatenates them back into a sortable outline number.
You then apply this by making a helper column and then using the function like so:
=OutlineSortingFormat(M3,2)
Where M is the column that has your unformatted outline indexes and the second parameter (, 2)) indicates that your want all index numbers filled (and truncated) to 2 digits. Then instead of sorting on your original outline numbers, your sort on the "helper column" containing the reformatted values.
Manual method
Use the Text to Column function and separate out your headers using "." as a delimiter.
When you are done select all the data as follows:
Perform a sort on the selected data.
Note: My data has headers has been selected and column 6 and 7 come up a A to Z as they are currently empty and it defaults to alphabetical sort as a result. The alphabetical sort can be added by adding a dummy row of data at the start or end of your data to be sorted. This is done by either adding all 0's or a number larger than any number in your list to all columns.
After selecting ok your "Combined" data will be sorted numerically based on the outline numbers to the right.
Here is some code for multiple purposes.
The first function is a UDF which if wanted could be called from the worksheet to be used as a helper function. Therefore it's easy enough to alter the pad length if required for sorting.
The second code is a little more involved but inserts a column next to the table, adds the helper function, sorts then deletes the helper column to leave the sheet structure as before.
SortColumn should be defined as the column index where the indices are. i.e. if in the first column of the designated table then it would be set to '1'
Public Function PadIndices(Cell As Range, PadLength As Long, Optional Delimiter As String) As String
If Cell.Count > 1 Then Exit Function
If Delimiter = "" Then Delimiter = "."
Dim Arr As Variant: Arr = Split(Cell.Value, Delimiter)
Dim i As Long: For i = LBound(Arr) To UBound(Arr)
If Len(Arr(i)) < PadLength Then Arr(i) = WorksheetFunction.Rept("0", PadLength - Len(Arr(i))) & Arr(i)
Next i
PadIndices = Join(Arr, Delimiter)
End Function
Sub SortByIndices()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("Sheet1")
Dim Table As Range: Set Table = ws.Range("H7:I11")
Dim PadLength As Long: PadLength = 2
Dim SortColumn As Long: SortColumn = 1
Table.Columns(1).Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Dim SortRange As Range: Set SortRange = Table.Columns(1).Offset(0, -1)
SortRange.Formula2R1C1 = "=PadIndices(RC[" & SortColumn & "], " & PadLength & ")"
With ws.Sort.SortFields
.Clear
.Add2 Key:=SortRange, SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
End With
With ws.Sort
.SetRange Application.Union(Table, SortRange)
.Header = xlGuess
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
SortRange.Delete Shift:=xlToLeft
End Sub
You could make a helper column in which you remove the points and sort that helper column.
=NUMBERVALUE(SUBSTITUTE(E4;".";))
A) User defined function without the need of a help column
In order to be able to sort outline numbers, you have to bring the individual numerical values
to a well defined uniform number format (like e.g. "00" if numbers don't exceed 99 as assumed default; note the flexible String() function in section b)).
This dynamic array approach allows argument inputs of any range definitions (plus optional digit maxima) like
=Outline(A5:A10) to sort one column (with a 2-digits default maximum) or even
=Outline(A2:E4, 3) over a multicolumn range (with an explicit 3-digits maximum)
Note: tested with the newer dynamic features of Office 2019+/MS365;
for backward compatibility you would have to change the TextJoin() function and possibly enter =Outline(...) as array formula using CSE (Ctrl+Shift+Enter).
Function Outline(rng As Range, Optional ByVal digits As Long = 2)
'Date: 2022-01-09
'Auth: https://stackoverflow.com/users/6460297/t-m
'a) create unordered 1-dim array from any contiguous range
Dim myFormula As String
myFormula = "TextJoin("","",True," & rng.Address(False, False) & ")"
Dim codes
codes = Split(rng.Parent.Evaluate(myFormula), ",")
'b) add leading zeros via number format
Dim i As Long
For i = LBound(codes) To UBound(codes)
Dim tmp: tmp = Split(codes(i), ".")
Dim ii As Long
For ii = LBound(tmp) To UBound(tmp)
tmp(ii) = Format(CInt(tmp(ii)), String(digits, "0"))
Next ii
codes(i) = Join(tmp, ".") ' join to entire string element
Debug.Print i, codes(i)
Next i
'c) sort
BubbleSort codes ' << help proc BubbleSort
'd) remove leading zeros again
For i = LBound(codes) To UBound(codes)
For ii = 1 To digits - 1 ' repeat (digits - 1) times
codes(i) = Replace(codes(i), ".0", ".")
If Left(codes(i), 1) = "0" Then codes(i) = Mid(codes(i), 2)
Next
Next
'e) return function result
Outline = Application.Transpose(codes)
End Function
Help procedure BubbleSort
Sub BubbleSort(arr)
'Date: 2022-01-09
'Auth: https://stackoverflow.com/users/6460297/t-m
Dim cnt As Long, nxt As Long, temp
For cnt = LBound(arr) To UBound(arr) - 1
For nxt = cnt + 1 To UBound(arr)
If arr(cnt) > arr(nxt) Then
temp = arr(cnt)
arr(cnt) = arr(nxt)
arr(nxt) = temp
End If
Next nxt
Next cnt
End Sub
B) Just for fun: alternative single-formula approach (with restricted number range)
Instead of extending the digit formats, I played with the idea to restrict the numeric display
by executing a temporary hexadecimal replacement.
Note that this approach based on a single-formula evaluation
allows outline sub-numbers only within a numeric range from 1 to 15 (as numbers 10 to 15 get replaced by characters A to F), but might be sufficient for low hierarchy depths! Furthermore it includes a tabular Sort() function available only in Excel version MS365!
Function Outline(rng As Range)
'Site: https://stackoverflow.com/questions/70565436/how-to-sort-outline-numbers-in-numerical-order
'Date: 2022-01-09
'Auth: https://stackoverflow.com/users/6460297/t-m
'Meth: hex replacements + sort; assuming chapters from (0)1 to 15 (10=A,11=B..15=F)
'Note: allows outline sub-numbers only up to 15! Needs Excel version MS365.
Dim pattern
pattern = String(6, "X") & "Sort(" & String(6, "X") & "$,15,""F""),14,""E""),13,""D""),12,""C""),11,""B""),10,""A"")),""A"",10),""B"",11),""C"",12),""D"",13),""E"",14),""F"",15)"
pattern = Replace(Replace(pattern, "$", rng.Address(False, False)), "X", "Substitute(")
Outline = rng.Parent.Evaluate(pattern)
End Function
The thing is not always the amount of values (IDs) will be the same within each cell (at least 1, max=several) that's why the fixed version of using concatenated vlookup+left/mid/right will not work for me due to that will solution will only work up to 3 values. The only fixed size is the size of the values to lookup (IDs - in green), 8 characters (letters+numbers).
I'm not sure but, is it possible to setup a loop within excel formulas/functions ?
Below is a table containing an example of the issue I'm trying to resolve and the expected values (tables are in different tab). Hope you can help.
Thanks.
example-tables
If you have windows Excel O365 with the TEXTJOIN and FILTERXML functions, you can use a formula:
=TEXTJOIN(",",TRUE,IFERROR(XLOOKUP(FILTERXML("<t><s>" & SUBSTITUTE(#[IDs],",","</s><s>") & "</s></t>","//s"),Table2[IDs],Table2[IDv2]),"""--"""))
Note that, in your data, there are two ID's in A4 that do not match any ID's in Table 2. Although that may be a typo, I left them as is to demonstrate the error handling.
Table1
Table2
Here is a UDF that will do what you describe. Paste the code into a standard code module (not one already existing in the workbook but one that you create and that would have a name like Module1 before you change it to what you like best. You can also rename the function to give it a more suitable name.
Function ID_v2(Cell As Range) As String
' 035
Dim Fun As String ' function return value
Dim Sp() As String ' array of CSVs of CellVal
Dim VLRng As Range ' the lookup range
Dim VL As Variant ' result of VLookup
Dim i As Integer ' loop counter
' this is a range similar to your sample A10:D19
Set VLRng = ThisWorkbook.Names("Table2").RefersToRange
Sp = Split(Cell.Cells(1).Value, ",")
If UBound(Sp) >= 0 Then
For i = 0 To UBound(Sp)
On Error Resume Next
VL = Application.VLookup(Trim(Sp(i)), VLRng, 3, False)
If Err Then VL = "[ERROR]"
Fun = Fun & VL & ","
Next i
ID_v2 = Left(Fun, Len(Fun) - 1) ' remove final comma
End If
End Function
Call the function with syntax like built-in functions. For example,
= ID_v2(A3)
This can be copied down like any other function. But remember to save the workbook as macro-enabled.
Try this:
Option Explicit
Sub Cell2List()
Dim wF As WorksheetFunction: Set wF = Application.WorksheetFunction 'To user Transpose
Dim i As Range
Dim j As Range
Dim s As String: s = "," 'The separator of the list
'Ask the user for the cell where are the list with the commas
'Just need to select the cell
Set i = Application.InputBox("Select just one cell where the values are", "01. Selecte the values", , , , , , 8)
'Ask the for the separator. If you are completely sure the comma will never change just delete this line
s = Application.InputBox("Tell me, what is the character separator, just one character! (optional)", "02. Separator (comma semicolon colon or any other char)", , , , , , 2)
If s = "" Then s = "," 'Verifying...........
'Ask the user where want to put the list
'You need to get ready the cells to receive the list.
'If there any data will be lost, the macro will overwrite anything in the cells
Set j = Application.InputBox("Select just one cell where the values will go as a list, just one cell!", "03. Selecte the cell", , , , , , 8)
Dim myArr: myArr = (Split(i.Value, s)) 'Split the list into a Array
Range(Cells(j.Row, j.Column), Cells(j.Row + UBound(myArr), j.Column)).Value = wF.Transpose(myArr)
'j.Row is the row of the cell the user selected to put the cell
'j.Column the same, but the column
'j.Row + UBound(myArr) = UBound(myArr) is the total count of elements in the list
' +j.Row
' _______________
' the last cell of the new list!
'wF.Transpose(myArr) = we need to "flip" the array... Don't worry, but Don't change it!
End Sub
You can put this macro with a button tin the ribbons, or use it as you can see in the gif
And this will be the result: (with a bigger list)
EDIT
You can use this UDF:
Function Cells2List(List As Range, Pos As Integer) As String
Cells2List = Split(List, ",")(Pos - 1)
End Function
Just need to define and index this way:
To tell the function, what index you want to see. You can use the function using ROW()-# to define an 1 at the beginning and when the formula send a #VALUE! delete the formulas. Where $A$1 is where the list are, and D7 is where the index are.
I need to print a string of arrays dependent on a difference of two values on my input page to separate sheets within the same PDF but I have been running into a few issues.
Based on the difference of two cells, the function will determine which arrays to print.
There are two possible solutions I have thought of but have been unsuccessful attempting both.
Indirectly reference a string of arrays in a cell to print such as "abc,bcd,cde,def,efg..."
(As Shown Below) Use conditional if-then functions to invoke the array based on the difference in these two cells
Primary Goals
Print into a single PDF
Determine specific arrays to print depending on the difference in two values contained in a cell on my input page
Allow for PageSetup values (have this figured out)
I am using MSFT 365. I tried initially using an indirect array reference to a cell with a variable value string including the arrays to be included without success.
Next, I tried to hardcode for all 100 possible values for this difference but in that case, I am running into line limits and errors associated with using _ to continue the array function on another line.
If the difference value equals 3, it is shown as below. If the difference value equals 4, you would add another array line including "schedule05","report05","p&l05"
Option Explicit
Sub PrintTest()
'if a certain difference value, use
If (Worksheets("Inputs").Range("D7") - Worksheets("Inputs").Range("D6")) = "3" Then
Dim pageArray As Variant
'set array for given difference
pageArray = Array("schedule01", "report01", "p&l01", _
"schedule02", "report02", "p&l02", _
"schedule03", "report03", "p&l03", _
"schedule04", "report04", "p&l04")
Worksheets("data").Activate
Worksheets("data").PageSetup.CenterHorizontally = True
'page setup values
With ActiveSheet.PageSetup
.FitToPagesWide = 1
.FitToPagesTall = 1
.Orientation = xlLandscape
End With
'call array for print
Worksheets("data").Range("pageArray").PrintOut
Elseif
'Here is where I could put another similar function for a difference of 4
'......
Else
'Here is where I could put another similar function for a difference of x
End If
End Sub
I expected this would get me a PDF where each of these arrays is printed on a separate sheet and will print a selection of arrays based on the difference value.
To expand on my comment, it would look like this:
Dim lDiff As Long
Dim pageArray As Variant
Dim sFormat As String
Dim i As Long, j As Long
'if a certain difference value, use
lDiff = Worksheets("Inputs").Range("D7").Value - Worksheets("Inputs").Range("D6").Value
ReDim pageArray(1 To (lDiff + 1) * 3)
For i = 1 To UBound(pageArray, 1) Step 3
j = j + 1
If j < 100 Then sFormat = "00" Else sFormat = "000"
pageArray(i) = "schedule" & Format(j, sFormat)
pageArray(i + 1) = "report" & Format(j, sFormat)
pageArray(i + 2) = "p&l" & Format(j, sFormat)
MsgBox pageArray(i)
Next i
I'm working on problem that necessitates the use of nested formulas in excel. For eg:
I have a column for errors and one for its analysis
Error Analysis
Enter a valid material number Invalid Material
Eg errors:
Enter a valid material number; The material number 1234 does not
exist.
PO number XYZ does not exist.
VIN number 123 does not exist.
Country of origin AB does not exist.
I have a compendium of such errors and their analyis in the next sheet, and I'm using VLOOKUP in conjuction with FIND to lookup the analysis for the known errors.
=VLOOKUP(LEFT(F2, FIND(" ", F2, FIND(" ", F2) + 1) - 1)&"*", 'Sheet2'!A:B, 2, 0)
What i'm trying to do here is extract the first two words from the error and append a * to it and use it in VLOOKUP.
It would be something like Vlookup "PO number *" in the other sheet and get the analysis for it. Asterisk is because I don 't get the same number daily. And I also know that the extracted first two words of the error will be unique. (I know that error with "Enter a" as the first two words will not appear again).
Now I get errors in the same column so I thought of making a button and writing a code which uses the above formula.
I tried to modify some code off the net, but I'm not getting anywhere with it. I'm totally new to VBA. It'd be great if you can provide a snippet for this. I'll try to replicate the procedure for other needs.
This code seems to be working for now
Sub PopulateAnalysis()
Dim an_row As Long
Dim an_clm As Long
Dim lft As String
Dim st_num As Integer
Dim fin As String
Dim searchStr As String
Dim soughtStr As String
Table1 = Sheet1.Range("F2:F6") 'ErrorColumn from Error table (How do I make the range dynamic??)
Table2 = Sheet5.Range("A1:B6")
an_row = Sheet1.Range("G2").Row ' Populate this column from the analysis table on sheet2
an_clm = Sheet1.Range("G2").Column
For Each cl In Table1
'How do I translate the above formula into VBA statements??
st_num = InStr(InStr(cl, " ") + 1, cl, " ")
lft = left(cl, st_num - 1)
fin = lft & "*"
Sheet1.Cells(an_row, an_clm) = Application.WorksheetFunction.VLookup(fin, Table2, 2, True)
an_row = an_row + 1
Next cl
MsgBox "Done"
End Sub
This should work. You don't need the debug lines of course ;)
Sub PopulateAnalysis()
Dim rngTableWithErrors As Range
Dim rngTableWithAnalysis As Range
Application.ScreenUpdating = False
'set the range for Table with error, Table1 on sheet 1
With Sheets(1) 'change to name of the sheet, more reliable than index num.
Set rngTableWithErrors = .Range("F2:F" & .Cells(.Rows.Count, 6).End(xlUp).Row)
Debug.Print rngTableWithErrors.Address
End With
'set the range for Table with Analysis, Table 2 on sheet 2
With Sheets(2) 'change to name of the sheet, more reliable than index num.
Set rngTableWithAnalysis = .Range("A1:B" & .Cells(.Rows.Count, 2).End(xlUp).Row)
Debug.Print rngTableWithAnalysis.Address
End With
'formula for cell G2
'=VLOOKUP(LEFT(F2;FIND(" ";F2;FIND(" ";F2)+1)- 1)&"*";Sheet2!A1:B23;2; 0)
rngTableWithErrors.Offset(0, 1).FormulaR1C1 = _
"=VLOOKUP(LEFT(R[0]C[-1],FIND("" "",R[0]C[-1],FIND("" "",R[0]C[-1])+1)-1)& ""*"",Sheet2!R1C1:R" & rngTableWithAnalysis.Rows.Count & "C2,2, 0)"
Application.ScreenUpdating = True
MsgBox "Done"
End Sub
Notes
You can notice, that we are setting the upper left cells of ranges manually. It's better practice to somehow find the upper left cells (using Find method is my favorite) and work from there. You never know, how the user will change the worksheet - i.e. add new rows, columns etc.
I have a long excel list (+10k rows) and a column with ordernumbers.
Unfortunatelly some orders were deleted.
My question is simple but to achieve probabily not: I want to count the deleted rows, basically the missing ordernumbers.
A hint is aprechiated.
endo
I don't know how to do this using Excel code, but if you go to the bottom and get the last order number, you can calculate how many there should be with
last order number - first order number = expected amount
How many their actually are would be
last order index - first order index = actual amount
Then you can do
expected amount - actual amount = missing order numbers
Of course, this assumes there are no blank rows between order numbers, and that you only need to do this once. (you prob want a function or something to have it update as you change the spreadsheet)
This covers blank rows and numbers missing from the sequence (however, if your min/max are deleted, this can't detect that). It's similar to #shieldgenerator7's answer.
No sorting necessary for this.
EDIT: As sheildgenerator7 pointed out, this assumes that you expect all of your order numbers to be sequential.
=(MAX(A2:A26)-MIN(A2:A26)+1)-COUNTA(A2:A26)
You can now count blanks in Excel with a simple function called COUNTBLANK. If you know the ending row number (for example, if the data were in A1 to A10000), you can use this formula:
=COUNTBLANK(A1:A10000)
If the numbers are sequential it is pretty easy.
Sort by order number
Count in B4
=(A4-A3)-1
Sum in B17
=SUM(B3:B16)
Here's something I put together to identify missing numbers and optionally print the list out on a new workbook.
You can change the minimum and maximum number, and it does not matter if the list is sorted or not.
Sub FindMissingNumbers()
Dim lstRange As Range
Dim r As Long
Dim lowestNumber As Long
Dim highestNumber As Long
Dim missingNumbers() As Variant
Dim m As Long
Dim wbNew As Workbook
'## Set this value to the lowest expected value in ordernumber'
lowestNumber = 0
'## Set this value to your highest expected value in ordernumber'
highestNumber = 100
'Assuming the order# are in column A, modify as needed:'
Set lstRange = Range("A1", Range("A1048576").End(xlUp))
For r = lowestNumber To highestNumber
'## Check to see if this number exists in the lstRange
If IsError(Application.Match(r, lstRange, False)) Then
'## Add this number to an array variable:'
ReDim Preserve missingNumbers(m)
missingNumbers(m) = r
m = m + 1
End If
Next
If MsgBox("There were " & m & " missing order numbers" _
& vbNewLine & "Do you want to print these numbers?", vbYesNo) = vbYes Then
Set wbNew = Workbooks.Add
With wbNew.Sheets(1)
' For r = LBound(missingNumbers) To UBound(missingNumbers)
' .Range("A1").Offset(r, 0).Value = missingNumbers(r)
' Next
.Range("A1").Resize(UBound(missingNumbers) + 1) = _
Application.WorksheetFunction.Transpose(missingNumbers)
End With
Else:
End If
End Sub