I'm conducting an optimization using Solver and I've had some issues now with decimals. Using a Swedish Excel my default decimal point is a comma "," but I've read from forums that Solver need to have decimals in the form of a period sign "."
Private Sub CommandButton1_Click()
Worksheets("Blad1").Activate
SolverReset
SolverOk SetCell:=B63, MaxMinVal:=1, ByChange:=Range("B45:B62")
SolverAdd CellRef:=B45, Relation:=1, FormulaText:=P24
SolverAdd CellRef:=B45, Relation:=3, FormulaText:=O24
SolverAdd CellRef:=L45, Relation:=1, FormulaText:=L3
SolverAdd CellRef:=L46, Relation:=3, FormulaText:=L4
SolverAdd CellRef:=B63, Relation:=3, FormulaText:=B42
SolverSolve
End Sub
The above is my code with in short (there are many more constraints in reality). Here all cell references are to cells containing decimal number with delimiter ",". I tried to add a Custom Format of the type "0.00" (if it's not possible to convert the decimals to periods and convert it back to commas in the answer, then that's not the optimal solution but an OK solution) but it resulted in integers, say 3, becoming "0.03" where the intention were to have it like "3.00".
Is there a way to read a decimal number having "," convert it to "." while doing calculation and subsequentially provide the answer with "," without having to "globally" change the decimals from commas to periods, as I have many Excel workbooks which will not work as intended using ".".
Thanks in advance
EDIT: Tested some more now and I realize that the Solver actually can "handle" comma decimals. Indeed Solver can print a solution consisting of comma decimals, however it appears that some functions, for instance SolverSolve, is based around end point decimals. I tested to implement the following in a new Workbook:
Sub Makro1()
Dim MyNumber1 As Double
Dim MyNumber2 As Double
Set MyNumber1 = Sheets("Blad1").Cell(20, 1).Value
Set MyNumber2 = Sheets("Blad1").Cell(21, 1).Value
Dim i As Integer
Dim rSetCell As Range, rByChange As Range, rPrecision As Range
Range("A1").Resize(1, 4) = Array("Precision", "c", "b", "a")
For i = 1 To 16
Set rPrecision = Range("A1").Offset(i, 0)
Set rSetCell = Range("A1").Offset(i, 1)
Set rByChange = Range("A1").Offset(i, 2).Resize(1, 2)
rSetCell.Formula = "=SQRT(" & rByChange(1).Address & "^2+" & rByChange(2).Address & "^2)"
rPrecision = 10 ^ -i
SolverReset
SolverOk MyNumber1, 3, 5, rByChange.Address
SolverOptions Precision:=rPrecision.Value
SolverAdd rByChange(1).Address, 4
SolverAdd MyNumber1, 1, MyNumber2
SolverSolve True
Next
End Sub
(Where the vast majority of code is taken from Andreas Killer at http://answers.microsoft.com/en-us/office/forum/office_2010-customize/excel-solver-macros-do-not-work-when-using-comma/8b595198-8492-4382-89c4-5fc144f7bc43?page=1 in order to check whether the program can print comma decimals, which it could).
Adding SolverAdd MyNumber1, 1, MyNumber2 where MyNumber1 is cell A20, which is 2,3 and MyNumber2 is A21 which is 2,5. Hence, the solution would not be feasible if the program actually could manage comma decimals (these parameters are never changed, and the constraint is always violated). Checking the TypeName Before adding the Dim Double for the two variables showed that A20 where, in fact, recognized as an String.
Trying to implement this solution gives me the error Object Required and a highlight on the code snippet Sub Makro1(). Why won't this work? Has anyone a clue how to make this work without needing to change the system language for all Excel workbooks, so I can avoid to adjust every Excel sheet I've ever created containing formulae?
EDIT:
Set MyNumber1 = Sheets("Blad1").Cell(20, 1).Value and Set MyNumber2 = .. both gives the error Object Required.
The problem is that solver accepts the cell reference not the cell values.
The MSDN page doesn't give any switches or options to change the format of numbers.
I am surprised that a comma is used in Sweeden for the decimal point.
What happens if you change your international settings in the control panel>Language settings (in additional settings you have to type a , no select on the combo). You can change the number format used.
I change my decimal point to use a comma and this chnaged how excel stored and showed it it. (the range properties .value .value2 and .text all had a comma instead of a full stop)
Try changing the language or the number format and see if this changes how they are displayed in excel so you can use solver.
Another concern is that they may not be stored as numbers in excel, but as text.
Please experiment.
Also here's some code that probably won't help, but may be of use:
This VBA function
replace("Mystringwith,commas", ",", ".")
will output
"Mystringwith.commas"
Although the parameters are a string you can use it with numbers as it will work (it implicitly converts the numbers to strings).
Also, the following code might give you some inspiration.
Sub aaa()
Dim c As Range
Set c = Worksheets("Sheet1").Range("B14")
c.Value = 1198.3
c.NumberFormat = "$#,##0_);($#,##0)"
Debug.Print c.Value ' 1198.3
Debug.Print c.Value2 ' 1198.3
Debug.Print c.Text ' £1,198.3
End Sub
Related
I have a worksheet with data and some columns which I fill with formulas via vba. The formula I struggle with is an array formula that looks like this:
Workbooks(job_file).Worksheets(1).Cells(h + b, 195).Formula = _
"{=IF(MAX(IF(B2:M2>$FY" & currentRow & ",$B$1:$M$1))=0,0," & _
"MAX(IF(sheet1!B2:M2>$FY" & currentRow & "," & _
"sheet1!$B$1:$M$1)))+1}"
It's supposed to be an array formula, so that's why I put {} there. However, when run it simply display the formula's text in a cell, without calculating it. I have to manually remove the brackets, and then press Ctrl+Shift+Enter myself.
Is there any way to avoid it? I have a great many rows and I can't ctrlshiftenter each.
I tried running it without brackets, it works, but gives a #VALUE! error, which can also be fixed by applying Ctrl+Shift+Enter.
To create an array formula with a simulated ctrl+shift+enter (aka CSE), use the Range.FormulaArray Property instead of the Range.Formula Property and let Excel add the 'curly braces'.
with Workbooks(job_file).Worksheets(1)
.Cells(h + b, 195).FormulaArray = _
"=IF(MAX(IF(B2:M2>$FY" & currentRow & ",$B$1:$M$1))=0,0," & _
"MAX(IF(sheet1!B2:M2>$FY" & currentRow & ", sheet1!$B$1:$M$1)))+1"
end with
I noticed in your formula that you use B2:M2 and sheet1!B2:M2. Shouldn't they both be sheet1!B2:M2?
There are some considerations.
Runtime Error: 1004 - Too long. There is a reduced character limit of 255 for FormulaArray but there are work-arounds.
Runtime Error: 1004 - Broken String. Remember that all quotes within a quoted string must be doubled up. This is easily one of the most common causes of errors when trying to write a formula into a cell through VBA. Hint: TEXT(,) can be used instead of "" so you don't have to type """" for a zero-length string.
FormulaArray accepts both xlR1C1 and xlA1 style formulas. If you can wrap your head around xlR1C1 style formula syntax, it is generally easier to construct a concatenated formula string in xlR1C1 since you can use digits to represent column numbers instead of trying to convert column ordinals to a column letter. However, do not try to mix-and-match xlA1 and xlR1C1 range references in the same formula; they must all be one style or the other.
If you are having trouble formulating a string that will be accepted as a formula, put a tick (e.g. ' ) in front of the first equals sign then run the code and return to the worksheet to see what was put in. Make modifications on the worksheet until you have a working formula then transfer those modifications to the VBA code.
As a note, it looks like this can be done without an array formula, like so:
Workbooks(job_file).Worksheets(1).Cells(h + b, 195).Formula = _
"=IF($FY" & currentRow & ">MAX(B2:M2),0," & _
"MAX(INDEX((B2:M2>$FY" & currentRow & ")*$B$1:$M$1,)))+1"
I have a huge list, which I have to work on each day. I want to change my reference columns data from number to text so it enables me to do some advanced filtering and referencing futures. I don't want to add any new column and for instance, use Text() function in excel as this range is same in different files I have to concatenate and link. I am just wondering if it is possible to do that in place with a VBA code or not. I tried:
selection=selection.text
or doing some paste special efforts with ', empty cells and converting to array back and forth but not succeded. Any idea? I also don't want to do a loop as I need to this effort for several files which I can't manipulate their structure or change them with my weak laptop each time every day!
Regards,
M
You may want to use built-in Excel functionality Text-to-columns.
If you want it automated using VBA, try this:
With Sheet1
.Range("B1", "B4").TextToColumns Destination:=.Range("B1"), _
DataType:=xlDelimited, _
FieldInfo:=Array(1, 2)
End With
Above converts all numbers in the range B1:B4 in sheet1.
The FieldInfo argument identifies the conversion.
We use Array(1,2) which means we want to convert column 1 to text.
Check Text to columns functionality to understand it further. HTH.
This should help you out. It converts a a range of values into text format
Sub Convert_To_Text()
Dim vData As Variant
vData = Range("A1:D1")
Range("A1:D1").NumberFormat = "#"
Range("A1:D1") = vData
End Sub
Based on #l42 answer and point here is what I was needed... and came to
Sub NumbtoText()
If Not ActiveSheet.FilterMode Then Selection.TextToColumns _
DataType:=xlDelimited, _
FieldInfo:=Array(1, 2)
End Sub
Thanks
Alright, so I have a spreadsheet, and I want to use this code (and more) but I'm getting problems. I want to take row A and Find the [-x] I should be using (see RC[StartColumn]) and autofill the rest. I then want to be able to repeat the steps for columns A-AF.
So essentially I want to perform the first calculation below, and the second one right next to that, and I want to be able to start it anywhere on the document. I then want to be able to copy that down each row for every column leading up to the first cell I chose.
Problem is I'm incredibly new to Macros, I put that together by observing my record macro results and playing around a little, but this is becoming too advanced for my current level and I want to finish this by today if possible. Could someone please show me the way, in a way that could teach me so I could do this on my own in the future?
Sub ConcatenateStep1()
Dim StartColumn As Integer
StartColumn = 1 - ActiveCell.Column
ActiveCell.FormulaR1C1 = _
"=IF(AND(RC14=R[1]C14),IF(OR(ISNUMBER(FIND(LOWER(RC[StartColumn]),LOWER(R[1]C[StartColumn]),1)),ISNUMBER(FIND(LOWER(R[1]C[StartColumn]),LOWER(RC[StartColumn]),1))),IF(LEN(RC[StartColumn])<LEN(R[1]C[StartColumn]),R[1]C[StartColumn],RC[StartColumn]),CONCATENATE(RC[StartColumn],"", "",R[1]C[StartColumn])),IF(AND(RC14<>R[1]C14,RC14<>R[-1]C14),RC[StartColumn],"" ""))"
Range("AK2").Select
Set SourceRange = Range("AK2")
Set fillRange = Range("AK2:AK22000")
SourceRange.AutoFill Destination:=fillRange
Range("AL2").Select
ActiveCell.FormulaR1C1 = _
"=IF(AND(RC14=R[1]C14,RC14<>R[-1]C14),IF(OR(R[1]C[-1]="" "",ISNUMBER(FIND(LOWER(RC[-1]),LOWER(R[1]C[-1]),1)),ISNUMBER(FIND(LOWER(R[1]C[-1]),LOWER(RC[-1]),1))),IF(LEN(RC[-1])<LEN(R[1]C[-1]),R[1]C[-1],RC[-1]),CONCATENATE(RC[-1],"", "",R[1]C[-1])),IF(AND(RC14<>R[1]C14,RC14<>R[-1]C14),RC[-1],"" ""))"
Set SourceRange = Range("AL2")
Set fillRange = Range("AL2:AL22000")
SourceRange.AutoFill Destination:=fillRange
Range("AL2").Select
End Sub
This is only a partial answer.
Are these incredibly long and complicated formulae the best way of achieving whatever you want to achieving?
There are two errors in the first formula that prevent it being accepted by Excel when the macro attempts to place it in a cell.
The formula is a long string containing StartColumn. But StartColumn is a variable. You need to replace every occurence of StartColumn with " & StartColumn & ".
Within a string each double quote required in the result must be replaced by two double quotes. Within CONCATENATE(RC[StartColumn],"", "",R[1]C[StartColumn]) the double quotes have not been doubled although those at the end of the formula have.
Excel will accept the following (which means it is syntactically correct) although I do not understand its purpose so it may be logically incorrect:
ActiveCell.FormulaR1C1 = _
"=IF(AND(RC14=R[1]C14),IF(OR(ISNUMBER(FIND(LOWER(RC[" & StartColumn & _
"]),LOWER(R[1]C[" & StartColumn & "]),1)),ISNUMBER(FIND(LOWER(R[1]C[" & _
StartColumn & "]),LOWER(RC[" & StartColumn & "]),1))),IF(LEN(RC[" & _
StartColumn & "])<LEN(R[1]C[" & StartColumn & "]),R[1]C[" & StartColumn & _
"],RC[" & StartColumn & "]),CONCATENATE(RC[" & StartColumn & _
"],"""", """",R[1]C[" & StartColumn & _
"])),IF(AND(RC14<>R[1]C14,RC14<>R[-1]C14),RC[" & StartColumn & "],"" ""))"
If I have parsed this formula correctly, CONCATENATE(RC[StartColumn],"", "",R[1]C[StartColumn]) has the same output as CONCATENATE(RC[StartColumn],R[1]C[StartColumn])
I agree with MP24, you must clarify your question before I or someone else can help you further. The Macro Recorder records syntactically correct VBA but it is not good VBA. It does not know your objective so records individual keyboard actions when a skilled programmer would combine them. It is almost never a good idea to use Select or to work with the ActiveCell but the Macro Recorder has no choice but to do so.
Part 2
Although Excel allowed the macro to place the first forumula, it later complained the formula contains a circular reference. Its gone midnight here, I will look at it tomorrow.
Part 3
Having looked at your first formula again, I believe I incorrectly parsed it last night. I now believe the concatenate function is correct. Also it appears that the circular reference depends on the location of the ActiveCell when the macro starts. I was able to clear this error by selecting a different start position.
My current attempt to parse your formula gives:
Outer: "=IF(X1,X2,X3)"
X1: AND(RC14=R[1]C14)
X2: IF(X4,X5,X6)
X3: IF(X7,RC[StartColumn],"" "")
X4: OR(X8,X9)
X5: IF(X10,R[1]C[StartColumn],RC[StartColumn])
X6: CONCATENATE(RC[StartColumn],"", "",R[1]C[StartColumn])
X7: AND(RC14<>R[1]C14,RC14<>R[-1]C14)
X8: ISNUMBER(X11)
X9: ISNUMBER(X12)
X10: LEN(RC[StartColumn])<LEN(R[1]C[StartColumn])
X11: FIND(LOWER(RC[StartColumn]),LOWER(R[1]C[StartColumn]),1)
X12: FIND(LOWER(R[1]C[StartColumn]),LOWER(RC[StartColumn]),1)
I have used macros to place formulae so they can later give changing results as the user enters values. Do you want changing results? If not, replacing this formula with VBA would almost certainly give more understandable and more maintainable code.
I do not approve of formulae like this. I am aware that many people use complex formulae successfully and are pleased with the results. However, I have spent too much of my life picking up the pieces left by over clever people who have moved on. How long does it take to get a formula like this correct? How long does it take to amend when six or twelve months later a slightly different result is required? Too often, in my experience, the person who has to amend it six or twelve months later is not the original author. With VBA the author can leave comments saying what the code is doing but with formulae the author cannot. Over clever authors don't always leave the comments they should but at least VBA code is usually easier to decode than a complex formula.
I am having difficulty with solver. I have tried to develop a for-loop that would call solver many times but I am having issues. I have already added the reference of solver to the workbook from tools/references. I have successfully gotten the loop to work with the solver; unfortunately, solver won't change my input cells automatically. I felt that this issue could be resolved by the SolverReset function but every time I use it it fails, and the loop can no longer find a solution without manually re-solving the system. My code is as follows.
Sub Go_Click()
'Variable Types
Dim x As Double
Dim First_Variable As Range
Dim Second_Variable As Range
Dim Number_of_Calculations As Double
Dim Input_Cells As Range
Dim First_Dimension_Lower As Double
Dim y As Double
Number_of_Calculations = 6
x = (First_Dimension_Upper_Bound - First_Dimension_Lower_Bound) / Number_of_Calculations
For y = 1 To Number_of_Calculations
' x is the integer that the first dimension of the solve will increase by.
' by finding the difference between the upper and lower bound and dividing by the number of calculations
' then the rest is just a for loop, adding the value "x" to each loop
SolverOk SetCell:="First_Dimension", _
MaxMinVal:=3, _
ValueOf:=First_Dimension_Lower_Bound, _
ByChange:="Input_Cells", _
Engine:=1, EngineDesc:="GRG Nonlinear"
SolverSolve
'Just to test the results
MsgBox (Range("b4"))
'increase in the lower first dimension bound
First_Dimension_Lower_Bound = First_Dimension_Lower_Bound + x
Next y
End Sub
Check the return result of SolverSolve to discover if it completed successfully:
If a Solver problem has not been completely defined, SolverSolve
returns the #N/A error value. Otherwise the Solver runs, and
SolverSolve returns an integer value corresponding to the message that
appears in the Solver Results dialog box.
The linked page lists the possible integer values that are returned. For example, 5 indicates "Solver could not find a feasible solution".
You can also supply a Function name as the ShowRef argument to SolverSolve:
SolverSolve(UserFinish, ShowRef)
This Function will be called if Solver pauses for one of the reasons listed in the linked page. For example, 2 means:
Function called because the Max Time limit in the Solver Options
dialog box was exceeded.
I'm having some trouble with VLOOKUP in my VBA. Here's an example of the code I'm using:
Sub Macro15()
'
' Macro15 Macro
Dim LR As Long
LR = Cells(Rows.Count, "A").End(xlUp).Row
Range("B1:B" & LR).FormulaR1C1 = _
"=VLOOKUP(RC[-1],'https://internal_sharepoint_address
/[Vendor_Information.xlsx]Sheet1'!R3C3:R150C18,4,FALSE)"
Range("C1:C" & LR).FormulaR1C1 = _
"=VLOOKUP(RC[-2],'https://internal_sharepoint_address
/[Vendor_Information.xlsx]Sheet1'!R3C3:R150C18,5,FALSE)"
With Range("B1:C" & LR)
.Value = .Value
End With
End Sub
The problem is that the values in Columns B & C (the VLOOKUP formulas) return a value of #N/A.
However, if I stop the code before converting the formula to values (the "With Range("B1:C" & LR)" line), the VLOOKUP formula returns the correct values.
Also strange - if I clear the contents of Columns B & C and re-run the above code, the values return fine. If I try to add a second cycle to the VBA, however, it does NOT work.
Any wisdom that anyone can provide would be a huge help. I've been stuck on this for a long time, and I'm just at my wit's end.
Thanks all,
David
You'll probably need to add in a step that runs a calculation cycle before you try to replace with the value:
Application.Calculate
Edit from comment: I would imagine that retrieving lookup data from a linked workbook on a Sharepoint site would take awhile. Maybe add some delay loops? Can you make two separate macros (one ending with the formulas, and a second one starting at the Paste Values), and run them separately with a pause in between?