formula for array max if Excel VBA - excel

So this is my formula:
ws8.Range("Y2:Y" & lrowWFS).FormulaArray = "=Max(if(x2='New Stock Data'!H:H,'New Stock Data'!G:G))"
Problem is it's returning to {=Max(if(x2='New Stock Data'!H:H,'New Stock Data'!G:G))} in Y3 onwards.
Any idea how to solve?

It should be as simple as to change FormulaArray to Formula. (I haven't tested this), try:
ws8.Range("Y2:Y" & lrowWFS).Formula = "=Max(if(x2='New Stock Data'!H:H,'New Stock Data'!G:G))"

Couple of things. The generic formula looks like this: {=MAX(IF(criteria_range=criteria,value_range))}. So, instead of x2='New Stock Data'!H:H, you want to use: 'New Stock Data'!H:H=x2. Other than that, unlike regular formulas, array formulas assigned to a range do not adjust relative cell references.
E.g. Range("A1:A2").Formula = "=B1" will successively lead to =B1 and =B2, while Range("A1:A2").FormulaArray = "=B1" will lead to 2x =B1.
One solution is to loop through your range and assign the array formulas to each cell individually. E.g.
Dim rng As Range
Dim lrowWFS As Long
lrowWFS = 3
Set rng = ws8.Range("Y2:Y" & lrowWFS)
For Each cell In rng
cell.FormulaArray = "=MAX(IF('New Stock Data'!H:H=" & Cells(cell.Row, "X").Address(False, False) & ",'New Stock Data'!G:G))"
Next cell
A better solution would be to use SUMPRODUCT instead. This avoids the use of an array formula, and thus also the need for a loop.
E.g.:
With rng
.Formula = "=SUMPRODUCT(MAX(('New Stock Data'!H:H=X2)*('New Stock Data'!G:G)))"
End With
More in general, I would advise proper specification of the ranges in the formula. Define first_row and last_row and use something like this:
"=SUMPRODUCT(MAX(('New Stock Data'!H$" & first_row & ":H$" & last_row & "=X2)*('New Stock Data'!G$" & first_row & ":G$" & last_row & ")))"

Related

Combining IF formulas in Excel with variable ranges [duplicate]

I want to plug a range object into a formula. An example should look roughly like this:
Dim x As Range
Set x = Range(Cells(1, 1), Cells(2, 1))
Range("C1").Formula = "=SUM(" & x & ")"
The result should be "=SUM(A1:A2)" in cell C1.
The point is to plug the the range object into a formula. I used SUM as an example, the real formula is more complicated.
I guess there is an easy answer to this, e.g. some method for a range object, but I have't found it after some pondering ...
Thanks in advance,
Dainis
You need
Range("C1").Formula = "=SUM(" & x.Address(False, False) & ")"
See also
http://www.dailydoseofexcel.com/archives/2004/04/16/worksheet-formulas-in-vba-part-i/
http://www.dailydoseofexcel.com/archives/2004/04/16/worksheet-formula-in-vba-part-ii/
Use x.address, that should do the trick

Generate serial numbers and barcodes as per logic defined based on the count mention in certain cell

I need to generate lot numbers for my products and since my software doesn't auto generate them, I have defined a logic in Excel for the same. It is quite basic and combines data in 2 or more cells to create a unique code.
To be more specific, I shall introduce our operations to you. We have a few collection centers for our products and we require them to apply lot number labels onto their bags of coffee before transferring it to our main warehouse. I have created an excel sheet in which I enter the current date in one cell and select the name of the center in another which then generates a lot number for the same. For example, cell A1 has today's date and A2 has center 'MBR' selected. The formula in cell C2 =IF(A2="MBR","MB:"&TEXT(A1,"YYYYMMDD")&"-001",IF(A2="MAY","AY:"&TEXT(A1,"YYYYMMDD")&"-001",IF(A2="MZM","MM:"&TEXT(A1,"YYYYMMDD")&"-001",""))) shall give me a result as MB:20171010-001
Now comes the tricky part. I want to mention in cell A3 the number of lots to be generated. For example, if I say I want 10 labels, then the formula should give me 10 labels from "-001" to "-010." If possible, it could start with the number which I would define in possibly cell A4 and then give me the sequence as required.
Lastly, I use this info to generate barcodes using an add-on in excel which I downloaded from the internet which converts the text into barcodes and then I can print them. I have tried barcode fonts but they don't work at all. If you have another alternative to this where I could generate sequential lot numbers based on the logic defined and create barcodes for the same, please share it with me. If not, please give me a formula which will work with excel.
Enter your data in columns A - C, run the bit of sample code below, result is in column D. You can modify the code to suit your needs but here is a start for you:
Public Sub RunTags()
Dim oWS As Worksheet
Dim lRow As Long
Dim lNextTagRow As Long
Set oWS = Worksheets("Sheet1")
lRow = 2
lNextTagRow = 2
Do Until oWS.Range("A" & lRow) = ""
Select Case oWS.Range("A" & lRow)
Case "MBR"
PrintTags oWS, lNextTagRow, "MB:", oWS.Range("B" & lRow), oWS.Range("C" & lRow)
Case "MAY"
PrintTags oWS, lNextTagRow, "MA:", oWS.Range("B" & lRow), oWS.Range("C" & lRow)
Case "MZM"
PrintTags oWS, lNextTagRow, "MZ:", oWS.Range("B" & lRow), oWS.Range("C" & lRow)
End Select
lRow = lRow + 1
Loop
End Sub
.
Private Sub PrintTags(ByRef oWS As Worksheet, ByRef lTagRowStart As Long, sPrefix As String, lQty As Long, lStart As Long)
Dim x As Long
Dim dtNow As Date
dtNow = Now()
For x = lStart To lStart + lQty - 1
oWS.Range("D" & lTagRowStart) = sPrefix & Format(dtNow, "YYYYMMDD") & "-" & Format(x, "000")
lTagRowStart = lTagRowStart + 1
Next x
End Sub

Dynamic formula autofill

I would like to know how may i convert this code into dynamic range autofill for the column I to enable the freedom of editing the excel sheet at a later time without the need to adjust the vba code.
Sub Rowcount()
'Charles, M
Dim H As Long
Set sh = Worksheets("Report-Charles, M")
H = sh.UsedRange.Rows.Count
Range("I2:I" & H).Formula = "=IF((AND(OR(E2=""Unrated"",E2=""""),OR(G2<>""Unrated"",G2<>""""))),G2,IF(E2>=""4,25"",""High Performance"",IF(E2<""3,35"",""Low Performance"",IF(AND(E2>=""3,35"",E2<""4,25""),""Medium Performance"",""Unrated""))))"
MsgBox (H) & "Rows have been Autofilled with 3 scale Rating Results"
End Sub
Have a look into the AutoFill function:
Range("I2:I" & H).AutoFill Destination:=Range("I2:I2000"), Type:=xlFillDefault
First off, you are only implicitly referencing the Report-Charles, M as the ActiveSheet property to put the .Formula into; not explicitly with direct reference.
Second, To assign the formula, use a direct .Formula write, the Range.AutoFill method or the Range.FillDown method.
Lastly, writing a formula .Formula through VBA should be done with EN-US syntax and regional settings. If you want to use non-EN-US regional settings like 3,35 or 4,25 then use the Range.FormulaLocal property.
Above all else, treat numbers as numbers; not as text-that-look-like-numbers.
with Worksheets("Report-Charles, M")
h = .UsedRange.Rows.Count
.Range("I2:I" & H).FormulaLocal = _
"=IF((AND(OR(E2=""Unrated"", E2=TEXT(,)), OR(G2<>""Unrated"", G2<>TEXT(,)))), G2, IF(E2>=4,25, ""High Performance"", IF(E2<3,35, ""Low Performance"", ""Medium Performance"")))"
.Range("I2:I" & H).Formula = _
"=IF((AND(OR(E2=""Unrated"", E2=TEXT(,)), OR(G2<>""Unrated"", G2<>TEXT(,)))), G2, IF(E2>=4.25, ""High Performance"", IF(E2<3.35, ""Low Performance"", ""Medium Performance"")))"
end with
with Worksheets("Report-Charles, M")
'any of the following will fill column I with formulas to the last row in .UsedRange
'all of the following assumes a valid formula in I2
h = .UsedRange.Rows.Count
.Range("I2:I" & H).Formula = .Range("I2").Formula
.Range("I2:I" & H).FillDown
.Range("I2:I" & H).DataSeries Rowcol:=xlColumns, Type:=xlAutoFill
.Range("I2").AutoFill Destination:=.Range("I2:I" & H), Type:=xlFillDefault
end with
I've shortened one IF condition since the AND was unnecessary and Medium was the default response. Using TEXT(,) is the same as saying "" and does not have to double-up double-quotes.

How to divide an entire column by the sum of it's average in excel

Hello I have a large file in excel. I was able to calculate the average for an entire column by using =AVERAGE(A2:A34315) and got it's average. Now i'm trying to divide the entire column by it's average. Is there an easier way to do this without me going into a new column and manually typing it. For example let's take the average number to be 1000
=A2/1000
I don't want to type this all the time or drag because the file i have is large. The columns start from 2 all the way to 34315. Reason for doing this is i'm trying to normalize or scale the data.
The following short procedure takes the AVERAGE of all of the numbers in column A and stores it in B1. It then disguises the number with a custom number format that looks like a common column header label. The underlying value is used to 'scale' or 'normalize' the numbers from column A into column B.
Sub scaled()
With Worksheets("sheet4")
.Range("B1").Formula = "=average(a:a)"
.Range("B1").NumberFormat = "\S\c\a\l\e"
.Range("B2:B" & .Cells(Rows.Count, "A").End(xlUp).Row).Formula = "=a2/b$1"
End With
End Sub
You could use a formula in column B like =A2/AVERAGE(A:A) but that calculates the same average over and over again. Calculating it once and leaving it 'hidden' should be much more efficient.
try with below code
if you need individual percentage then add Range("B" & i).NumberFormat = "0.00%"
Sub getavg()
Dim lastrow As Long
Dim avgg As Variant
lastrow = Range("A" & Rows.Count).End(xlUp).Row
avgg = Application.WorksheetFunction.Average(Range("A2:A" & lastrow))
For i = 2 To lastrow
Range("B" & i).Value = Range("A" & i).Value / avgg
'Range("B" & i).NumberFormat = "0.00%"
Next i
End Sub

VBA clean up conditional duplication of rows

I have a data set in which there is an id field and a number of other fields.
The id field at times has a second id. In this case, I need to create a new duplicated record and put a single id in each of the records. There is an additional condition in which each of the ids may be followed by a number which is a percentage. In all cases, I need to display the correct percentage as a decimal value in a field on the record.
id examples:
AJ01-25/ST01-75
AJ01/LM03
RICH01
Correct representation of ids and percentages:
id percent
AJ01 .25
ST01 .75
AJ01 .5
LM03 .5
RICH01 1.0
I used the following code to create a new record and parse any percentages into a new field whenever a "/" is detected, but I'd very much like to have something cleaner. Sort order does not matter (my script places new records at the end). Thoughts?
Sub breakemup()
Dim wb As Workbook
Dim ws As Worksheet
Dim id As String
Dim rng As Range
Dim ar() As String
Set wb = ThisWorkbook
Set ws = wb.Worksheets("data")
Dim currentRow As Integer
Dim finalRow As Integer
finalRow = ws.UsedRange.Rows.Count
For currentRow = 2 To finalRow
ar() = Split(ws.Range("a" & currentRow).Value, "/")
If UBound(ar) = 1 Then
ws.Rows(currentRow).Copy Destination:=ws.Range("A" & Rows.Count).End(xlUp).Offset(1)
If UBound(Split(ar(1), "-")) = 1 Then
ws.Range("A" & Rows.Count).End(xlUp).Value = Split(ar(1), "-")(0)
ws.Range("A" & Rows.Count).End(xlUp).Offset(0, 1).Value = CDbl(Split(ar(1), "-")(1)) / 100#
ws.Range("A" & currentRow).Value = Split(ar(0), "-")(0)
ws.Range("A" & currentRow).Offset(0, 1).Value = CDbl(Split(ar(0), "-")(1)) / 100#
Else
ws.Range("A" & Rows.Count).End(xlUp).Value = ar(1)
ws.Range("A" & Rows.Count).End(xlUp).Offset(0, 1).Value = 50 / 100#
ws.Range("A" & currentRow).Value = ar(0)
ws.Range("A" & currentRow).Offset(0, 1).Value = 50 / 100#
End If
Else
ws.Range("A" & currentRow).Offset(0, 1).Value = 1#
End If
Next
End Sub
Not sure if you would consider this cleaner but here is a formula solution rather than a VBA one. It uses the functions:
INDIRECT
MID
LEN
IF
RIGHT
FIND
Lets say we have the following in sheet 1:
Insert the following formulas in sheet 2:
Cell A2:
=IF(E2=1, IF(D2="", IF(C2="", INDIRECT(G2), LEFT(INDIRECT(G2), C2-1)), LEFT(INDIRECT(G2), D2-1)), IF(D2="",IF(C1="", INDIRECT(G2), RIGHT(INDIRECT(G2), LEN(INDIRECT(G2))-C1)), MID(INDIRECT(G2), C1+1, D2 -1-C1)))
Drag / Copy it down to cover the rest of the cells in column A
Cell B2:
=IF(E2=1,IF(C2="",IF(D2="",1,MID(INDIRECT(G2),C2+1,LEN(INDIRECT(G2)))),IF(D2="",0.5,MID(INDIRECT(G2),D2+1,C2-D2-1)/100)), IF(D2="",0.5,MID(INDIRECT(G2),D2+1, LEN(INDIRECT(G2))-D2)/100))
Drag / Copy it down to cover the rest of the cells in column B
Cell C2:
=IF(E2=1, IF(ISNUMBER(FIND("/",INDIRECT(G2))),FIND("/", INDIRECT(G2)), ""), "")
Drag / Copy it down to cover the rest of the cells in column C
Cell D2:
=IF(E2=1, IF(ISNUMBER(FIND("-", INDIRECT(G2))),FIND("-", INDIRECT(G2)), ""),IF(D1 ="", "", FIND("-", INDIRECT(G2), D1+1)))
Drag / Copy it down to cover the rest of the cells in column D
Cell E2:
The number value "1"
Cell E3:
=IF(E2=1, IF(C2="", 1, 2),1)
Drag / Copy it down to cover the rest of the cells in column E
Cell F2:
the number value "1"
Cell F3:
=IF(E3=1, F2+1, F2)
Drag / Copy it down to cover the rest of the cells in column F
Cell G2:
="Sheet1!A" & TEXT(F2, 0)
Drag / Copy it down to cover the rest of the cells in column G
Result:
You could hide the extra columns so you only see the columns you need:
I have an article on my blog that provides an example with pretty much the same functions used here, it might help with understanding the functions used Excel Functions and Formulas Sample #1, Split Strings Based on Delimeter

Resources