Perform IF function in VBA - vba

I use an =IF function
=IF(RIGHT(A1;1)="-";"-"&LEFT(A1;LEN(A1)-1);A1)*1
to shift a minus sign from the end of the cell to the beginning but I'd like to use it in a macro so it is performed on the same column (or the same selection)...

1) Use the the For Each.... loop construct to loop through each cell in a range.
2) If you are wanting to convert "numbers" of the 123- to -123 to a proper number and not text, use the Val command to convert a string to a number.
Note however that if you if you have cell with something like "ABC-", this will become -ABC which VBA then attempts to convert to a number ...and produces zero as a result
Sub MoveMinus()
Dim c As Range
For Each c In Intersect(Selection, Selection.Worksheet.UsedRange)
If (Right(c, 1) = "-") Then
c = Val("-" & Left(c, Len(c) - 1)) 'Val to make the result numeric
End If
Next
End Sub

Related

Auto-Numbering depending upon Row or Column being hidden

I want the cell to number itself in an incremental order depending upon the filters. I found the easiest way is to check for the above Row if it is hidden or not then number itself from 1 if hidden and previous cell value+1 if not hidden.
I've tried to achieve this using the Formula
=IF(COUNTA(ADDR)>SUBTOTAL(103, ADDR), 1, ADDR+1)
Where ADDR is defined as follows:
=ADDRESS(ROW()-1,COLUMN(), 4, TRUE)
SUBTOTAL function returns #VALUE as it cannot contain 3-D References.
Tried replacing SUBTOTAL() function with AGGREGATE(), same issue.
Tried to use VALUE() function to convert the ADDR string to value.
I tried to use VBA
Public Function IsHid(i As Integer)
Dim re As Range, x As Integer
Set re = Range("A" & i)
If re.EntireRow.Hidden Then
Set re = Range("A" & i + 1)
re = 1
Else
x = re.Value + 1
Set re = Range("A" & i + 1)
re = x
End If
End Function
The above function returns #VALUE.
The below function also returns #VALUE.
Public Function IsHid(i As Integer)
If Excel.ThisWorkbook.ActiveSheet.Rows(i).Hidden Then
Cells(i + 1, 1).Value = 1
Else
Cells(i + 1, 1).Value = Cells(i, 1).Value + 1
End If
End Function
Very much appreciated if this functionality can be obtained by means of FORMULAS rather than the VBA
Use Subtotal combined with Count(A):
=SUBTOTAL(3,B$2:B2) and paste down.
This can be in column A and will number only visible rows when you filter on B, C, etc.
You might want to take a look here as well, for additional explanation.
Edit:
Let's say you have Sheet1 and you fill up Range A:G. In column A you want the numbering described in the question. Then Range A1 will hold a header (e.g. FilteredID) and Range B:G will hold your other values.
In range A2 all the way down, you put the formula =Subtotal(3, B$2:B2), in Range A3 this will be =Subtotal(3, B$2:B3), in A4 =Subtotal(3, B$2:B4), etc.
Now, when you filter on column B, C, D etc. so you'll have invisible rows, the numbering in column A will show the visible Row number.
For example, assuming you want to start numbering in row 2 and in column A and you have Excel 2010 or later:
=AGGREGATE(4,5,A$1:A1)+1
Just adjust the start cell as required.

Delete row if first letter in cell is a letter

I am trying to loop through a range in column A and delete each row where the value in cell A starts with a letter (e.g. delete C159, but not 8T9G3). I think the code may work correctly, if I get the between piece straight. Any suggestions how I can get the code to work?
Sub DeleteLetterRows()
Dim k as integer
For k = 2 To 100
If Asc(ActiveSheet.Range("A" & k).value) >=65 and <=90 or >=97 and <=122
Rows(k).EntireRow.Delete
Else
End If
Next k
End Sub
Thanks!
Some issues:
When you delete a row, and k increases, you actually skip a row, which you don't check. So, better go downwards with k to avoid this problem.
The way you compare against ASCII values has wrong syntax, as you need to explicitly specify a value after and immediately before each >= and <= operator. But instead of giving a correction (which would be long), I'll suggest a shorter syntax:
Checking for a letter can be done in a more readable way, which does not require the knowledge of ASCII codes. Simply check if the first character is different when put in upper case than when put in lower case. If so, it is a letter.
You are missing the Then keyword at the end of the If line;
Code:
Sub DeleteLetterRows()
Dim k as integer
Dim l as String
For k = 100 To 2 Step -1
l = Left(ActiveSheet.Range("A" & k).value, 1)
If UCase(l) <> LCase(l) Then
Rows(k).EntireRow.Delete
End If
Next k
End Sub
To check the first character of your string you use the function Left().
The easiest way to find out if is not a number is the function isNumeric().
Put together you will get
Sub DeleteLetterRows()
Dim k as integer
Dim test As String
For k = 2 To 100
test = Left(ActiveSheet.Range("A" & k).value,1)
If isNumeric(test) = False then
Rows(k).EntireRow.Delete
Else
End If
Next k
End Sub

How to remove the last charactor and replace `:` by `.`

I have two columns M and U containing employee time record in an Excel sheet but the time format is wrong it is something like 08:13a. I want it look like 08.13 in order to use subtraction formula like =U2-M2 to get the difference. so I want to replace : by . and get ride of the last character using Excel VBA.
if 8:13a is really 8 hours, 13 minutes, then running:
Sub TimeFixer()
For Each r In Selection
ary = Split(Mid(r.Text, 1, Len(r.Text) - 1), ":")
r.Value = TimeSerial(ary(0), ary(1), 0)
r.NumberFormat = "hh:mm:ss"
Next r
End Sub
will produce a time you can use numerically.
EDIT#1:
Formula:
=TIMEVALUE(LEFT(A1,LEN(A1)-1))
for example:
Or in the code replace Selection with something like Range("A1:A100")There are many ways to skin this cat.

Excel SUMPRODUCT with VBA based conditions

Does anyone know how to use a VBA function within a worksheet based call to SUMPRODUCT?
This works fine, summing the values in column N where column L contains "Y" and col A contains a different value to col K...
=SUMPRODUCT(--(Input!L1:L100="Y"), --(Input!A1:A100<>Input!K1:K100), Input!N1:N100)
But I want to be able to apply more logic than just A<>K in my second criteria, something like this...
=SUMPRODUCT(--(Input!L1:L100="Y"), --(MatchNames(Input!A1:A100,Input!K1:K100)), Input!N1:N100)
I have a function called MatchNames in my VBA, but I can't work out what it needs to return in order to work. I've tried returning an array of boolean, integer (set to 0 or 1), I've tried transposing the results before returning them, but nothing is working. I've debugged through the MatchNames function, and it does return something "useful" (i.e. an array of booleans), so it's not that the function is bombing out part way through, but I get #VALUE! when I try to use it in a SUMPRODUCT.
This is my VBA function...
Public Function MatchNames(ByVal rng1 As Range, rng2 As Range) As Boolean()
Dim blnOut() As Boolean
Dim k As Long
ReDim blnOut(rng1.Rows.Count - 1)
For k = 1 To rng1.Rows.Count
If rng1.Cells(k, 1).Value <> "" Then
If rng1.Cells(k, 1).Value <> rng2.Cells(k, 1).Value Then
blnOut(k - 1) = True
End If
End If
Next
MatchNames = blnOut
End Function
I think your MatchNames array needs to be transposed as you suggest (because it appears to be returning the equivalent of a row of values - which doesn't work with the other columns of values in SUMPRODUCT).
I don't know how you'd transpose that in VBA but if you can't do that then transpose in SUMPRODUCT like
=SUMPRODUCT(--(input!L1:L100="Y"),--TRANSPOSE(MatchNames(input!A1:A100,input!K1:K100)), input!N1:N100)
but use of TRANSPOSE means that formula now needs to be "array-entered" with CTRL+SHIFT+ENTER
...or you can use MMULT which will multiply a 1x100 range by a 100x1, i.e.
=MMULT(MatchNames(input!A1:A100,input!K1:K100)+0,input!N1:N100*(input!L1:L100="Y"))
for that latter formula to work the sum range - input!N1:N100 - must be all numeric

Collect numbers from a column containing empty cells using Excel VBA

I have a little problem, I occasionally bump into this kind of problem, but I haven’t found a fast solution so far.
So, imagine we have an Excel worksheet and let's suppose that we have a couple of numbers in column ’A’ with some empty cells in it. Altogether (just to make it simple) we have the first 10 cells in column 'A' to observe. For example:
3
(empty cell)
(empty cell)
6
(empty cell)
4
(empty cell)
23
(empty cell)
2
Now in the next step I would like to collect these numbers into another column (for example, column ’B’) using VBA. Obviously I just want to collect those cells which contain a number and I want to ignore the empty cells. So I would like to get a column something like this:
3
6
4
23
2
I have already written the following code, but I’m stuck at this point.
Sub collect()
For i = 1 To 10
if cells(i,1)<>"" then...
Next i
End Sub
Is there an easy way to solve this problem?
Probably the quickest and easiest way is to use Excel's Advanced Filter - the only amendment you'll need to make is it add a field name and criteria. You can even list unique items only:
The VBA equivalent is
Sub test()
With Sheet1
.Range("B1:B8").AdvancedFilter Action:=xlFilterCopy, CriteriaRange:=.Range( _
"D1:D2"), CopyToRange:=.Range("F1"), Unique:=False
End With
End Sub
You should be able to use the method in the post int the comments, but you could also use SpecialCells like Range("A:A").SpecialCells(xlCellTypeConstants,xlNumbers).Copy to get all of the filled cells.
Edit: needed constants not formulas.
This will work for any number of rows that you select. It will always output in the next column at the start of your selection e.g. if data starts in B10 it will ooutput in C10
Sub RemoveBlanks()
Dim cl As Range, cnt As Long
cnt = 0
For Each cl In Selection
If Not cl = vbNullString Then
Cells(Selection.Cells(1, 1).Row, Selection.Cells(1, 1).Column).Offset(cnt, 1) = cl
cnt = cnt + 1
End If
Next cl
End Sub
If you wish to loop manually and don't mind specifying the maximum row limit;
Dim i As long, values As long
For i = 1 To 10
If cells(i, 1).Value <> "" Then
values = (values + 1)
' // Adjacent column target
cells(values, 2).value = cells(i, 1).value
End If
Next i