vba incrementing a range's row - vba

this should be a really quick question.
Is there any way to increment the value of a cell.Row?
For example, can I say something like usedCell.Row = usedCell.Row + 1?
That particular format doesn't work, but is there another way to increase the row by 1?

I believe cell.Offset(1,0) is what you are looking for.

The Row and Column properties of a cell (i.e. Range) are read-only so you can't increment them directly.
If you want to move through the cells in column A then iDevlop's answer works fine. An alternative method is to use the Cells method of the Worksheet object. Example code to write the word "hello" into every cell in column A from row 1 to 100:
Dim lRow As Long
For lRow = 1 To 100
Worksheets("Sheet1").Cells(lRow, 1).Value = "hello"
Next lRow
As you can see in the example, the Cells method takes the row number as the first parameter and the column number as the second parameter.
For the simple case of dealing with cells in the same column, you could also use the Range property of the Worksheet object and construct the actual address - e.g. A39 - each time:
Dim lRow As Long
For lRow = 1 To 100
Worksheets("Sheet1").Range("A" & lRow).Value = "hello"
Next lRow

Have you considered using
Dim c as Range
For Each c in Range("a:a")
...
Next c
?

You may be looking for
Set rng = rng.Resize(RowSize, ColumnSize)
In your case specifically
Dim rng_usedCells As Range
Set rng_usedCells = ActiveSheet.Range("A1:B10")
ActiveSheet.Range("D2").Value = rng_usedCells.Rows.Count
ActiveSheet.Range("E2").Value = rng_usedCells.Columns.Count
Set rng_usedCells = rng_usedCells.Resize(3, 4)
ActiveSheet.Range("D3").Value = rng_usedCells.Rows.Count
ActiveSheet.Range("E3").Value = rng_usedCells.Columns.Count

Related

Excel Vba: Need help to drag formula

I need help placing a formula in row 51 from column A to AE on sheet "COPY". The formula is "Trim(A1)" and needs to be dragged until "Trim(AE1)" while still being in row 51 (A51:AE51)
This is what I have so far, but its pulling up an error on "lascolumn = range..."
Sub INSERT_TRIM_COPY()
Sheets("COPY").Select
Dim Lastcolumn As Long
Lastcolumn = Range("A:AE" & Columns.Count).End(xlToRight).Column
Range("A51:AE51" & Lastcolumn).FORMULA = "=TRIM(A1)"
End Sub
You need to use: Range(Cells(51,1), Cells(51,Lastcolumn).Formula = "=Trim(A1) Because your lastcolumn is variable is numeric you need to use the cells function in the range. The first number is for row number and the second is for the column.
I believe the following will do what you expect it to, the code you used to get the Last Column wasn't right:
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("COPY")
LastCol = ws.Cells(1, ws.Columns.Count).End(xlToLeft).Column
'get the last Column on Row 1 with data
ws.Range(ws.Cells(51, 1), ws.Cells(51, LastCol)).Formula = "=Trim(A1)"
'add formula from column A to the last Column
End Sub

Sum Values based on unique ID

Just started a new job. I'm automating a month-end report and I'm new at VBA. Been googling most of my issues with success, but I've finally run into a wall. In essence I'm downloading some data from SAP and from there I need to build a report.
My question is: How to do a sumif function using loops in VBA?
Data pull:
Sheet1 contains a product code and purchase amounts (columns A & B) respectively. One product code can have several purchases (several rows with the same product code).
Steps so far:
I arranged the data sheet1 to be in ascending order.
Copied unique values for the product codes onto another sheet (sheet2). So Sheet2 has a list of all the products (in ascending order).
I want to get the sum of all purchases in sheet2 column B (per product code). I know how to do this using formulas, but I need to automate this as much as possible. (+ I'm genuinely interested in figuring this out)
This is what I did in VBA so far:
Sub Macro_test()
Dim tb As Worksheet
Dim tb2 As Worksheet
Dim x As Integer
Dim y As Integer
Dim lrow As Long
Set tb = Sheets("sheet1")
Set tb2 = Sheets("sheet2")
lrow = tb.Cells(Rows.Count, "A").End(xlUp).Row
For x = 2 To lrow
For y = 2 To lrow
If tb2.Cells(x, 1).Value = tb.Cells(y, 1).Value Then
tb2.Cells(x, 2).Value = tb.Cells(y, 2).Value
End If
Next y
Next x
End Sub
If i'm not mistaken, for each product_code in sheet2 col A, I'm looping through all the product codes in sheet1 and getting back the LAST value it finds, instead of the sum of all values... I understand why it doesn't work, I just don't know how to fix it.
Any help would be much appreciated. Thanks!
This statement overwrites the value of tb2.Cells(x, 2).Value at each iteration:
tb2.Cells(x, 2).Value = tb.Cells(y, 2).Value
Instead, I think you need to keep adding to it:
tb2.Cells(x, 2).Value = tb2.Cells(x, 2).Value + tb.Cells(y, 2).Value
But I don't like the looks of your double-loop which uses only one lrow variable to represent the "last row" on the two different worksheets, that could be causing some issues.
Or, in your loop do something like this which I think will avoid the duplicate sum. Still, assumes the second worksheet doesn't initially have any value in
' Base our lRow on Sheet2, we don't care how many rows in Sheet1.
lrow = tb2.Cells(tb2.Rows.Count, 1).End(xlUp).Row
Dim cl as Range
Set cl = tb.Cells(2,1) 'Our initial cell value / ID
For x = 2 to lRow '## Look the rows on Sheet 2
'## Check if the cell on Sheet1 == cell on Sheet2
While cl.Value = tb2.Cells(x,1).Value
'## Add cl.Value t- the tb2 cell:
tb2.Cells(x, 2).Value = tb2.Cells(x, 2).Value + cl.Offset(0,1).Value
Set cl = cl.Offset(1) '## Reassign to the next Row
Wend
Next
But it would be better to omit the double-loop and simply use VBA to do 1 of the following:
1. Insert The Formula:
(See Scott Holtzman's answer).
This approach is better for lots of reasons, not the least of which is that the WorksheetFunction is optimized already, so it should arguably perform better though on a small dataset the difference in runtime will be negligible. The other reason is that it's stupid to reinvent the wheel unless you have a very good justification for doing so, so in this case, why write your own version of code that accomplishes what the built-in SumIf already does and is specifically designed to do?
This approach is also ideal if the reference data may change, as the cell formulas will automatically recalculate based on the data in Sheet1.
2. Evaluate the formula & replace with values only:
If you prefer not to retain the formula, then a simple Value assignment can remove the formula but retain the results:
With .Range(.Range("B2"), .Range("A2").End(xlDown).Offset(, 1))
.FormulaR1C1 = "=SUMIF(Sheet1!C[-1]:C[-1],RC[-1],Sheet1!C:C)"
.Value = .Value 'This line gets rid of the formula but retains the values
End With
Use this approach if you will be removing Sheet1, as removing the referents will break the formula on Sheet2, or if you otherwise want the Sheet2 to be a "snapshot" instead of a dynamic summation.
If you really need this automated, take advantage of VBA to place the formula for you. It's very quick and easy using R1C1 notation.
Complete code (tested):
Dim tb As Worksheet
Dim tb2 As Worksheet
Set tb = Sheets("sheet1")
Set tb2 = Sheets("sheet2")
Dim lrow As Long
lrow = tb.Cells(tb.Rows.Count, 1).End(xlUp).Row
tb.Range("A2:A" & lrow).Copy tb2.Range("A2")
With tb2
.Range("A2").CurrentRegion.RemoveDuplicates 1
With .Range(.Range("B2"), .Range("A2").End(xlDown).Offset(, 1))
.FormulaR1C1 = "=SUMIF(Sheet1!C[-1]:C[-1],RC[-1],Sheet1!C:C)"
End With
End With
Note that with R1C1 notation the C and R are not referring to column or row letters . Rather they are the column and row offsets from the place where the formula is stored on the specific worksheet. In this case Sheet!C[-1] refers to the entire A column of sheet one, since the formula is entered into column B of sheet 2.
I wrote a neat little algorithm (if you can call it that) that does what you want them spits out grouped by totals into another sheet. Basically it loops through the first section to get unique names/labels and stores them into an array. Then it iterates through that array and adds up values if the current iteration matches what the current iteration of the nested loop position.
Private Sub that()
Dim this As Variant
Dim that(9, 1) As String
Dim rowC As Long
Dim colC As Long
this = ThisWorkbook.Sheets("Sheet4").UsedRange
rowC = ThisWorkbook.Sheets("Sheet4").UsedRange.Rows.Count
colC = ThisWorkbook.Sheets("Sheet4").UsedRange.Columns.Count
Dim thisname As String
Dim i As Long
Dim y As Long
Dim x As Long
For i = LBound(this, 1) To UBound(this, 1)
thisname = this(i, 1)
For x = LBound(that, 1) To UBound(that, 1)
If thisname = that(x, 0) Then
Exit For
ElseIf thisname <> that(x, 0) And that(x, 0) = vbNullString Then
that(x, 0) = thisname
Exit For
End If
Next x
Next i
For i = LBound(that, 1) To UBound(that, 1)
thisname = that(i, 0)
For j = LBound(this, 1) To UBound(this, 1)
If this(j, 1) = thisname Then
thisvalue = thisvalue + this(j, 2)
End If
Next j
that(i, 1) = thisvalue
thisvalue = 0
Next i
ThisWorkbook.Sheets("sheet5").Range(ThisWorkbook.Sheets("Sheet5").Cells(1, 1), ThisWorkbook.Sheets("Sheet5").Cells(rowC, colC)).Value2 = that
End Sub
Yay arrays

Finding and returning first column that contains data - VBA Excel

I am trying to locate the first column that contains data in a given worksheet, I have seen various formulas from searching online but I am not getting the correct value. I have tried the formula from this website but it is not working.
http://www.vbaexpress.com/kb/getarticle.php?kb_id=418
Any ideas?
Use the End property of the Range class; this is similar to when you press Ctrl + an arrow key from an active cell. Personally I find UsedRange to be a bit unreliable because you may have used a cell unrelated to the range you're trying to find (e.g. you have a table in columns A to D and some calculations in E).
Dim firstColumn as Long
If Range("A1") <> "" then
firstColumn = 1
Else
firstColumn = Range("A1").End(xlToRight).column
End If
This assumes you're likely to have data in A1. If not then do the same with columns first instead of rows, in this case using Cells instead of Range:
Dim firstRow, firstColumn as integer
If Range("A1") <> "" then
firstColumn = 1
Else
firstRow = range("A1").End(xlDown).row
if Range("A" & firstRow <> "" then
firstColumn = 1
else
firstColumn = Cells(firstRow, 1).End(xlToRight).Column
End If
End If
well, this one is easy:
activesheet.usedrange.columns(1).column
this will give you the column number I understand you are asking for

How to get the value of a range within a range

So I need to extract information from a sheet with only certain values. From about 550 rows down to 50 which are spread across the entire sheet.
So I used autofilter for that. Now I only see the rows which match to my criteria but how can I get the values of a specific range from?
This far I came:
I know that I have to use
RangeINamed.SpecialCells(xlCellTypeVisible)
to work with only the visible information.
It worked for getting the starting and last row
startRow = bulkbatchRange.SpecialCells(xlCellTypeVisible).row
endRow = startRow + bulkbatchRange.SpecialCells(xlCellTypeVisible).rows.Count
But now I need to get the value of a specific column, I want to use a For loop so I can loop through all visible rows.
So I tried to do
RangeINamed.SpecialCells(xlCellTypeVisible).range("U" & rowNumber).value
That didn't work it gave me nothing. Now I'm rather clueless so does someone maybe know how I get the value of that row in column U in RangeINamed?
Thank you
You can always retrieve the value in a specific cell like U10 with:
Range("U10").Value
whether the row is hidden or not.
EDIT#1:
Here is a little example that loops down thru column A of an AutoFiltered table. It looks for the third visible row (not including the header row):
Sub GoDownFilter()
Dim rLook As Range, r As Range
Set rLook = Intersect(ActiveSheet.UsedRange, Range("A:A").Cells.SpecialCells(xlCellTypeVisible))
rLook.Select
K = 0
For Each r In rLook
If K = 3 Then
r.Select
MsgBox "The third visible row has been selected"
Exit Sub
End If
K = K + 1
Next r
End Sub
I think you need to choose if you want to get a specific cell like:
Range("U10").Value
Or a relative cell using something like
RangeINamed.SpecialCells(xlCellTypeVisible)(2,3).Value
Or
RangeINamed.SpecialCells(xlCellTypeVisible)(2,3).Address 'To see if you are getting it right
EDIT:
A complete code to Filter and Iterate.
Sub Filter()
Dim tableRange As Range, var, actualRow As Integer, lastRow As Integer
Set tableRange = Range("PUT_THE_TABLE_RANGE_HERE")
' Filter
With tableRange
Call .AutoFilter(5, "SPECIFIC_FILTER")
End With
Set f = tableRange.SpecialCells(xlCellTypeVisible)
With tableRange
Call .AutoFilter(5)
End With
For Each var In f.Cells.Rows
actualRow = var.Row
If actualRow <> 1 Then
' Do something
End If
Next
End Sub

Converting words to numbers in VBA

In excel I have a column of words which I need to convert to integers. For example, I have a column of industries:
Capital Goods,
List item,
Consumer Services,
Technology, etc.
I want to replace each of these industries with an integer.
Below, something I was trying in VBA but which didn't work. Here I am trying to loop through the column and if the word in the current cell is different from the word in the previous cell then I assign it a different integer. (But it's not working)
Sub WordtoNum()
Dim ws As Worksheet
Dim varList
Dim rng1 As Range
Dim lngCnt As Long
Dim startrow, wsheet, tt As Integer
' Enter the worksheet and starting row
'---------------------------------------
wsheet = 2
startrow = 2
'---------------------------------------
Set ws = Sheets(wsheet)
Set rng1 = ws.Range(ws.[a1], ws.Cells(Rows.Count, "A").End(xlUp))
varList = rng1.Value2
tt = 0
For lngCnt = startrow To UBound(varList)
If varList(lngCnt, 2) <> varList(lngCnt - 1, 2) Then _
tt = tt + 1
varList2(lngCnt, 2) = tt
Next
rng1.Value2 = varList
End Sub
This code is largely based on help I received in a recent, related post.
Why not use the build in Excel function, VLOOKUP? It looks up a word in a sorted column and returns a value from another column, but the same row as the match. Read more on Office Help about VLOOKUP
I realise the question you asked was how to do this in VBA, however, I'm not sure if you really wanted to use VBA as an exercise or just didn't know about this function?