Simple task with confusing alternate results.
I'm copying a range of data using:
WS.Range("A2:Z" & lRow).Copy
ThisWorkbook.Worksheets("Import").Range("A2:Z" & lRow).PasteSpecial xlPasteValues
The first column from the copy sheet is a date in format 12/05/2017 01:00:00 (note the double space in between date and time)
In one instance of this the date values are pasted across fine and come out as dates - great!
In another instance of this the date values are pasted but come out as 14/05/2017 01:00 and these aren't registering as dates, rather as a text string.
I noticed I could go through the dates cell by cell and press enter which converted them to dates, so I tried using .range("A1:A100").value = .range("A1:A100").value to no avail.
I suspect it may have something to do with the day-month-year format as opposed to being month-day-year (since it works for a sheet that starts on 12-may but not on 14-may) but (1) could there be another difference, (2) why does pressing ENTER work fine and (3) how can I emulate pressing ENTER on my whole range of cells (bearing in mind .value = .value doesn't work)
In short: Type conversion (fixing the values before they cause trouble on your worksheet in the first place) is absolutely the best way to go.
In long:
Let's start with these two values:
22.05.2017 12:00
22.05.2017 12:00
I'll place the first in A1 and the second in B1. Note that Excel will often try to do the type conversion, so in this case I'll manually enforce A1 to contain text values by formatting the cell as such after-the-fact.
Let's verify:
Using the Immediate window, we can see that the compiler recognizes the content of A1 as a pure text value, while it recognizes the content in B1 as a date value.
The solution you need is to ensure that any text values are converted to date values:
Option Base 1
Sub pasteTextAsDate()
Dim dateArr As Variant
Dim rng As Range
Set rng = ThisWorkbook.Worksheets(1).Range("A1:A3")
' In the line below, we fill the variant variable with the content of the range, casting the variable into an array of variants
dateArr = rng
' For the sake of proving this code works, we'll start by printing the content and what type it is
For Each s In dateArr
Debug.Print s & " - " & TypeName(s)
Next s
For i = 1 To UBound(dateArr)
' This is where we loop through the array and cast any string values to date values
dateArr(i, 1) = CDate(dateArr(i, 1))
' Here we verify for ourselves that the conversions are OK
Debug.Print dateArr(i, 1) & " - " & TypeName(dateArr(i, 1))
Next i
' And here we print the result to the worksheet
ThisWorkbook.Worksheets(1).Range("C1:C3").Value = dateArr
End Sub
Result:
Related
Sorry if the title is confusing, not sure how to phrase this. Basically I want to write a formula which takes a date from a cell specified by the user (myDate) and the address of a starting cell (FirstCell) and based on that:
Sets a range (myArea) which starts from the specified cell and extends for a number of cells equal to the number of days in the month and year of the date we selected (numDays).
Counts cells in that range that do not have a blank interior (there is no conditional formatting in the sheet) and returns that number.
For example let's say I have the date 1/4/2022 and April in 2022 has 5 days but my table has 6 columns. I want to define a range based on the length of this specific month so that the number of cells which do not have a blank interior is equal to 1 and not 2. And I want this to be reproducible for different months.
The 2nd point is done and works with a simple user-specified range, the 1st point is the one giving me trouble because I don't want it to return anything in the sheet. The specific issue is setting numDays but there may be other errors I didn't catch - basically I tried to transplant the excel function solution to counting this into VBA but I'm pretty sure I'm getting the syntax wrong and/or this is not doable. Couldn't find anything that would answer my question on here, when I try to use the function it returns #VALUE! in the spreadsheet.
Function SPECIALDAYS (FirstCell as Range, myDate as Date)
Dim myCell as Range
Dim myArea as Range
Dim numDays as Integer
numDays = Application.Evaluate("Day(Eomonth(" & myDate & ",0))")
Set myArea = Range(FirstCell, FirstCell.Offset(0, numDays-1))
For Each myCell In myArea
If myCell.Interior.ColorIndex <> -4142 Then
SPECIALDAYS=SPECIALDAYS+1
End If
Next myCell
End Function
Instead of using Evaluate and formula use a pure VBA solution with WorksheetFunctions:
numDays = Day(Application.WorksheetFunction.EoMonth(Date, 0))
See WorksheetFunction.EoMonth method.
Is it possible to get all the values from Table 1 corresponding to the values in the cell of Table 2 without using VBA ?
For example.
In table 2 one value is "India Australia" (F3). I need to get the result as "IND AUS" looking from the table 1.
Thanks in advance!
Using TEXTJOIN:
=TEXTJOIN(" ",TRUE,VLOOKUP(FILTERXML("<a><b>"&SUBSTITUTE(F3," ","</b><b>")&"</b></a>","//b"),A:B,2,FALSE))
If one does not have TEXTJOIN then VBA is the only way to get it in one cell. See Here for a UDF that mimics TEXTJOIN: VLOOKUP with multiple criteria returning values in one cell
1] For Office 365 and Excel 2019 user, to use CONCAT function
In G3 array formula copied down :
=CONCAT(IF(ISNUMBER(SEARCH($A$3:$A$6,F3)),$B$3:$B$6,"")&" ")
This is an array formula and needs to be confirmed by pressing with Ctl + Shift + Enter.
2] For all Excel version user, try this longer formula
Create a range name
Select G3 >> Define name >>
Name : Abb
Refers to : =IF(ISNUMBER(SEARCH($A$3:$A$6,$F3)),$B$3:$B$6,"")
OK >> Finish
Then
In G3, enter formula and copied down :
=TRIM(IFERROR(INDEX(Abb,1),"")&" "&IFERROR(INDEX(Abb,2),"")&" "&IFERROR(INDEX(Abb,3),"")&" "&IFERROR(INDEX(Abb,4),"")&" "&IFERROR(INDEX(Abb,5),""))
I borrowed this VBA solution from extendoffice.com. It's very simple, easy to set up, and worked really well for my situation.
My scenario -
Sheet 1 contains the lookup value in column A where I want the formula results in column B.
Sheet 2 contains the table array in columns A and B where column A is again the lookup value and column B contains the values to be returned in the results.
First, press ALT+F11 to bring up the VBA editor.
Second, paste this Function into Module1 within the VBA editor:
Function MYVLOOKUP(pValue As String, pWorkRng As Range, pIndex As Long)
'Updateby Extendoffice
Dim rng As Range
Dim xResult As String
xResult = ""
For Each rng In pWorkRng
If rng = pValue Then
xResult = xResult & ", " & rng.Offset(0, pIndex - 1) 'I changed EO's delimiter from a space to a comma+space.
End If
Next
If Left(xResult, 2) = ", " Then
xResult = Right(xResult, Len(xResult) - 2) 'This removes any leading commas that make it into the results.
Else
'All is well in the world!
End If
MYVLOOKUP = xResult
End Function
Third, double click on the cell you want the multi-results in and paste this formula.
=MYVLOOKUP(A2, 'SheetName'!A1:B1816, 2)
Fourth, edit the formula to fit your spreadsheet.
A2 - The lookup value that will be compared against the other list.
'SheetName'!A1:B1816 - Replace SheetName with the name of the sheet your other list is on and replace A1:B1816 with the full range of values that are both being looked up and returned.
2 - If the values you want returned in your results are in a column other than B, replace '2' with the column that contains the values you want returned in your results.
You're done! If you did everything correctly (and you don't have any Trust Center settings blocking VBA code) then it should look like a VLOOKUP returned multiple results in a single cell.
My file has two identical Worksheets for users to input two different sets of assumption variables, called "InputA" and "InputB". I want to quickly switch which Input sheet is feeding into the other sheets of the model.
Using Find and Replace took over 5 minutes, and there were over 350,000 references to "InputA".
I tried the following macro, which takes an instant to run, but unfortunately also changes all references in the workbook, effectively keeping everything referenced to the original input sheet.
Sheets("InputA").Name = "temp"
Sheets("InputB").Name = "InputA"
Sheets("temp").Name = "InputB"
Is there a way to execute the macro but prevent any references to worksheets from changing to the new sheet name, basically freezing everything except the sheet name change? Or perhaps any other solution that will work quickly? I don't want to go through 350,000 instances and rewrite using INDIRECT(), as that is the only other solution I've seen, because my references are complex and nested and that will take an age.
Thanks.
Assuming that your 2 Input-Sheets have the same structure, I would suggest the following:
Create a named range on Workbook-level, name it for example InputData. This range should contain all data from InputA.
Create a helper-sheet and name it Input - you can later set it to hidden.
Mark the range in the new sheet that is exactly the size of the Input-Data-Range and enter the formula =InputData as Array-formula. You can do so by entering Ctrl+Shift+Enter. The formula should have curly brackets and the sheet should now display the data of InputA.
Change all you formulas to refer to the helper Sheet Input instead of InputA.
Create a macro:
Sub switchInput()
Const sheetAName = "InputA"
Const sheetBName = "InputB"
With ThisWorkbook.Names("inputData")
If InStr(.RefersTo, sheetAName) > 0 Then
.RefersTo = Replace(.RefersTo, sheetAName, sheetBName)
Else
.RefersTo = Replace(.RefersTo, sheetBName, sheetAName)
End If
End With
End Sub
This routine will just change the definition of the named range to point either to the first or second input sheet. As a consequence, the helper sheet will show the data of the selected Input-Sheet. All your formulas itself stays unchanged.
As stated in the comments, you could take the approach recommended by Damian and use a conditional in all relevant cells. The generic conditional would be
=IF(A1="InputA",FORMULA INPUTA,FORMULA INPUTB)
This formula makes A1 the cell that decides which input to pull. This will make changing the around 350.000 output formulas in your workbook the bottleneck, the following macro takes care of changing all the formulas to conatin the conditional:
Sub changeFormulas()
Dim rng As Range, cll As Range
Set rng = shOutput.Range("A2:C10") 'Your relevant worksheet and range here
Dim aStr As String, bStr As String, formulaStr As String
aStr = "InputA"
bStr = "InputB"
For Each cll In rng
If cll.HasFormula And InStr(1, cll.Formula, aStr, 1) Then
formulaStr = Right(cll.Formula, Len(cll.Formula) - 1)
cll.Formula = "=IF(A1=" & Chr(34) & aStr & Chr(34) & "," & formulaStr & "," & Replace(formulaStr, aStr, bStr) & ")" 'Change A1 to the reference most suited for your case
End If
Next cll
End Sub
This might take a bit of time, since it has to access all the relevant cells one by one, but it will only have to run once.
To explain: This macro will go through all the cells in your range rng specified at the top. If a cell has a formula in it and the formula contains "InputA" it will change that formula to fit the generic conditional (with the cells own formula of course). Chr(34) is the quotation mark ", I find using Chr(34) more readable than multiple quotation marks """, but that is just preference.
I can't seem to figure out how to overwrite my date with what's being displayed.
For example, what's being displayed is 06/01/2016, but the actual data behind that is showing 01/06/2016. The date I want to have in there is June 1, 2016 (which is what's displayed). Changing the format of the cell doesn't help, because I'm doing some formulas with the Dates later, so actually need to have Excel have the correct date.
I have about 10,000 of such dates, where the displayed date is exactly what I want to have, but the actual formula "reversed".
Essentially, I would love to just copy that column (or run a sub) that puts what the cell literally displays into the cell.
What sort of works is doing this, but it only works on those cells with "backwards" dates...essentially passing the day as a month, and month as a day.
=DATE(YEAR(C1),DAY(C1),MONTH(C1)).
It "falls apart" though when the date is actually correct and I don't want it to change:
So, what'd be best is to just literally overwrite the cell with what's displayed.
Or, what would an IF statement be that I could use to somehow check if the date displayed is what I want, and if so keep that, otherwise use that =DATE(YEAR(),DAY(),MONTH()) "trick"?
I also tried =IF(DAY(C2)>12,DATE(YEAR(C2),DAY(C2),MONTH(C2)),C2) but that doesn't work either because it returns June 01 2016 for both 01/06/2016 and 06/01/2016.
I may be overlooking something simple, I've been staring at this for an hour or so...Any thoughts/ideas are appreciated!
Say wee have dates in column C from C1 through C100 that appear to be correct. (so if you see 06/12/2017 you want it to be June 12th and not December 6th)
Try this short macro:
Sub dateFixer()
Dim ary(1 To 100) As String
Dim rng As Range, r As Range
Dim i As Long
Set rng = Range("C1:C100")
i = 1
For Each r In rng
ary(i) = r.Text
i = i + 1
Next r
rng.Clear
rng.NumberFormat = "mm/dd/yyyy"
i = 1
For Each r In rng
r.Value = ary(i)
i = i + 1
Next r
End Sub
A really clean solution is to use the CELL() function.
=CELL("format",A1) will return "D1" for cells formatted as d/m/y, and "D4" for cells formatted m/d/y. So, with this you can conditionally flip month and day:
=IF(CELL("format",A2)="D1",DATE(YEAR(A2),DAY(A2),MONTH(A2)),A2)
The Text property of a range returns the display text: "what the cell literally displays."
Note that literal is the right word - for example, if your column is too narrow, Text will return the displayed ##### characters instead of any useful value.
The Text property will not return an array, so you'll have to loop through your range and read/write individual cells with something like this:
For Each c in rng
c.Value = c.Text
Next c
Incidentally, the documentation on this property is almost nonexistent. This blog post goes into a more detailed review of the property and how it relates to Value and Value2.
I have a cell with a time value as 1:23:45 AM and would like to display just the hour value or use it for other calculations. Need to do this using vba. No, I cannot format the column manually.
Thanks,
Sam
To use VBA to format the cell to only display the hour:
With Worksheets("Sheet1").Cells(1, 1)
.NumberFormat = "H"
End With
To read the hour value in VBA (regardless of how the cell is formatted):
Dim hourValue As Integer
With Worksheets("Sheet1").Cells(1, 1)
If (IsDate(Format(.Value, "hh:mm:ss"))) Then
hourValue = Hour(.Value)
Else
// handle the error
End If
End With
If you want to use the displayed value in code without performing the conversion in VBA then use the Text property of the cell (Range) rather than the Value property. You'll need to be sure that the cell is formatted correctly first:
Dim hourValue As Integer
With Worksheets("Sheet1").Cells(1, 1)
If ((.NumberFormat = "H") And _
(IsDate(Format(.Value, "hh:mm:ss")))) Then
hourValue = CInt(.Text)
Else
// handle the error
End If
End With
If you just want to use the hour value in a worksheet formula then use the HOUR() worksheet function - e.g. =HOUR(A1)
Finally, if you cannot format the column manually because the sheet is protected then you won't be able to format it in VBA either
I am not sure whether your cell is a time field or not. If it is, can you do the following?
dim h as integer
h = Hour(Cell(1,1))