Location of first cell in an excel table - excel

I use Excel tables a lot. It makes it possible to have multiple tables in one worksheet. But I have got into a problem in VBA which I do not know how to solve.
Let us say that I have a table called "tbl" in my spreadsheet. It contains the following data:
id value
1 1000
1 2000
2 3000
2 4000
I have the following code which works:
Sub test()
Dim rng As Range
Dim arr() As Variant
Set rng = ActiveSheet.ListObjects("tbl").DataBodyRange
arr = rng.Value
End Sub
This code will put the whole table into arr. But there are situations where I only want some rows in the table. Let us say that I want row 4 and 5 i.e:
2 3000
2 4000
The following code will do the trick:
arr = Range("A4:B5")
But there are cases where I have many tables located in different places in my spreadsheet. Therefore I need to work out where the first cell in the table is located (upper left). If it is located in K1, I need to get both the column and the row (K and 1).
Is there an easy way to do this?

Use Cells():
Sub test()
Dim rng As Range
Dim arr() As Variant
Set rng = ActiveSheet.ListObjects("tbl").DataBodyRange
arr = ActiveSheet.Range(rng.Cells(3, 1), rng.Cells(4, 2)).Value
End Sub

If you just want the third and fourth data rows (i.e. fourth and fifth rows if you count the heading row), you could use
Sub test()
Dim rng As Range
Dim arr() As Variant
Set rng = ActiveSheet.ListObjects("tbl").DataBodyRange
arr = rng.Rows("3:4").Value
'or
arr = rng.Range("A3:B4").Value
End Sub
Because the Rows and Range properties are being applied to the rng object, the references ("3:4" and "A3:B4") are relative to the start of the rng object.
So you could also get the worksheet address of the first cell in rng by using rng.Range("A1").Address (or rng.Cells(1, 1).Address), or you can get the first cell's worksheet row and column by just using rng.Row and rng.Column respectively (they both default to returning the value for the first cell).

The first cell in a table has some unique properties:
-It is not empty
-The cell above it is null
-The cell to the left of it is null
-The cell below it is not null
Using these properties you can do something like:
1) Loop over every cell
2) If that cell has the above properties, then it is a "top left" cell
3) Do whatever with that table

Related

Is there any way to dynamically select an excel column using VBA?

Task: I need to copy a cell value from E12 to the range E23:E25. I achieved this easily using a simple copy and paste statement in the editor. But the range isn't always fixed. I have managed to capture the start point of this range as: Set rangeStart = Range("E12").End(xlDown).Offset(6, 0). I am unable to use this as a starting point for a range selection statement as follows:
Range("E23:E" & Range("A23").End(xlDown).Row).Select
That is how I'm selecting the range to be filled with data in the next step via a paste statement. How do I edit the first half of that range call to something more dynamic? Using rangeStart instead of E23:E.
I have just started working with Excel VBA, so this might be a very basic question to ask. Any help would be appreciated.
First I'd like to address the point that using Select is in most cases highly discouraged. Since you're only looking to paste a value into other cells, you can simply use a line like the following example:
Range("A1:A10").Value = Range("C2:C11").Value
or
Range("A1:A10").Value = 1
This would fill the A1:A10 range with either the values from C2:C11 or the integer 1, respectively. Note the two ranges in the first line are of the same size.
Now, a dynamic approach to your problem could be something along the following example
'Initialise two range variables
Dim SourceRange As Range, TargetRange As Range
'And integers for target rows
Dim fRow As Long, lRow As Long
With ThisWorkbook 'Assuming the code needs to be performed on the Workbook that hosts the macro
'Determine target rows
fRow = 23 'You have not specified how the first row should be determined
lRow = .Cells(fRow, "E").End(xlDown).Row
'Populate range vars
Set SourceRange = .Range("E12")
Set TargetRange = .Range(.Cells(fRow, "E"),.Cells(lRow, "E"))
'Paste values to target range
TargetRange.Value = SourceRange.Value
End With
Note that, since you haven't specified how this should be determined instead, the code above is hard-coded to have a target range in column E that starts at row 23.
If you have
Set rangeStart = Range("E12").End(xlDown).Offset(6, 0)
You can also have
Set rangeEnd = Range(rangeStart, Range("E" & Range("A23").End(xlDown).Row)
which will form a range from the first cell to the second cell.
But read the link posted by Vitaliy.

Excel VBA: How to clear contents for specified cells when another cell contains specific text or string

I'm having some trouble trying to find VBA code to delete multiple specific cells if a certain cell contains a specific text. This spreadsheet can run close to 100k rows as well, but will vary depending on the data pull.
The specific VBA would be able to do the following:
If Cell J3 equals #N/A, Blank, or 0, then clear contents of cells J3:K3 and P3:X3, and then repeat til it reaches the bottom of column J.
Thanks in advance
How to clear contents for specified cells when another cell contains specific text or string
Dim cellToClear As Range
Dim cellToCheck As Range
Dim specificText As String
If cellToCheck.Value = specificText Then cellToClear.ClearContents
"I'm having some trouble trying to find VBA code "
These links contain VBA code that you can use when you no longer have trouble trying. They contain examples you can paste into your project and modify for your needs.
This link has examples of how to read the contents of a cell.
A range is a group of one or more cells in a worksheet. You can perform an operation on a range and it will affect all the cells inside the range. This link has examples of how to work with a range.
A loop is when the program repeats the same sequence of steps, usually until a specific condition is met. You can find examples of different loops here.
I prefer placing values into an array if you are going to be changing a bunch of cells in a routine. This generally makes the process much quicker.
Start out by setting your worksheet and range objects. Please take note that the below code is currently using index 1 for the worksheet here: Set ws = ThisWorkbook.Worksheets(1). If this is not the worksheet you are personally needing, then you will need to change this.
Then place the cell contents of the entire range into an array. As I mentioned earlier, this process is quicker than making adjustments to individual cells 1 at a time.
Loop the array, checking for either the specific error value #N/A or the other criteria. If this criteria is a match, you will enter another loop that quickly loops through the 'columns' in the row that will delete the values from only the columns you specified.
Once finished, rewrite the array back to the worksheet.
Sub main()
Dim ws As Worksheet, rng As Range, dataArr() As Variant
Set ws = ThisWorkbook.Worksheets(1)
Set rng = ws.Range("J3:X" & ws.Cells(ws.Rows.Count, "J").End(xlUp).Row)
' Place the entire contents of worksheet range into an array
dataArr = rng.Value
Dim i As Long, x As Long, clearRow As Boolean
For i = LBound(dataArr) To UBound(dataArr)
If IsError(dataArr(i, 1)) Then
If dataArr(i, 1) = CVErr(xlErrNA) Then clearRow = True
ElseIf dataArr(i, 1) = vbNullString Or dataArr(i, 1) = 0 Then
clearRow = True
End If
' Loop thru the columns (x) of the current row (i)
If clearRow Then
For x = 1 To 15
Select Case x
Case 1, 2, 7 To 15
dataArr(i, x) = ""
End Select
Next x
clearRow = False
End If
Next i
' Re-write the entire array back to the worksheet in one step
rng.Value = dataArr
End Sub

Working with the Columns function - Excel VBA

I have been looking around the site for a while for an answer to this question but no luck just yet. I have this code where I loop through a row of numbers and depending on what number is in the cell at the time, determines what I copy and paste to the sheet. I am using Columns for this because it is the only way I can make my code dynamic. It works but when I paste I would like to paste in cells lower than where it's pasting right now. I was wondering if Columns had a way of specifying what column and where to paste my data.
Code:
Dim sh As Worksheet
Dim rw As Range
Dim row As Range
Dim cell As Range
Dim RowCount As Integer
Set rw = Range("A5:CG5")
Set sh = ActiveSheet
For Each row In rw.Rows
For Each cell In row.Cells
Select Case cell.Value
Case "2"
ThisWorkbook.Worksheets("Sheet1").Range("E27:E51").Copy Destination:=Sheets("Sheet2").Columns(4)
End Select
Next cell
Next row
Your problem can be solved as Jeeped said, use Destination:=Sheets("Sheet2").Cells(27, 5) or Destination:=Worksheets(2).Range("E27")
Since you want to learn a little bit more, i made an example explanation:
https://msdn.microsoft.com/en-us/vba/excel-vba/articles/range-column-property-excel
On the link it is explained that .Column:
Column A returns 1, column B returns 2, and so on.
And the same is with the .Rows
Use .Cells https://msdn.microsoft.com/pt-br/library/office/ff194567.aspx So you can use the .Cells(Rows,Columns) or .Cells(Index from a Range) or the entire Object:
With Worksheets("Sheet1").Cells.Font
.Name = "Arial"
.Size = 8
End With
So an example if you want to turn your spreadsheet dynamical: to copy from range $E$27 to last row with something written from column $E on Sheet1 To
the last column with nothing written on row 1 on Sheet2.
Sub test()
'Declare variables here
Dim sht1, sht2 As Worksheet
'sht1 has the data and sht2 is the output Worksheet, you can change the names
last_row = Worksheets(1).Range("E65536").End(xlUp).Row
last_column = Worksheets(2).Cells(1, sht1.Columns.Count).End(xlToLeft).Column
'Data add
For i = 27 To last_row
'Start from Row 27
Worksheets(2).Cells(i - 26, last_column + 1) = Worksheets(1).Cells(i, 5)
Next i
MsgBox "Data Updated"
End Sub
And an example of a basic dynamical workbook with i=i+1 and For loops split a single row of data into multiple unique rows into a new sheet to include headers as values and cell contents as values

Excel List of Blank Cells

So I have a big excel sheet with a bunch of empty cells in various locations. I want an easy to work with list of which cells are empty. I was hoping to make a new worksheet that was populated with the locations of the empty cells. I wanted to have this to just populate the cells I want it to. I kept the header from the worksheet I will be checking and added a blank cells count, so I want the following cells in the column to be populated by the list of empty cell locations.
Now I know I can use =ISBLANK to test if a cell is empty or not, but I only care about the cells that return TRUE. So I figure I'll need a loop. And I want the location of the cell so I can use =CELL. And to make this most readable I want to do this on a column by column basis.
But I want to populate a spreadsheet with this information in a manner similar to how functions work (I just want to copy and paste it to other cells and columns). But it's pretty clear that I am going to need VBA.
My question is how can I create a macro to populate my spreadsheet with a list of empty cells? How do I apply it to the cells?
I assume you have data in sheet1, I have used sample range// Range("A1:c15") however you can define range as per need and blank cells address will be published in next sheet.
Sub FindBlank()
Dim rng As Range
dim i as long
For Each rng In Sheet1.Range("A1:c15").SpecialCells(xlCellTypeBlanks)
i = i + 1
Sheet2.Cells(i, 1) = rng.Address
Next
End Sub
If you want a list of the cells that are empty, you can use Range().SpecialCells(xlCellTypeBlank):
Sub getEmptyCellAddresses()
Dim rng As Range
Dim ws as Worksheet
Set ws = Sheets("Sheet1") ' CHANGE AS NECESSARY
Set rng = ws.Range("A1:A15").SpecialCells(xlCellTypeBlanks) ' Edit/change range as necessary
ws.Cells(1, 2).Value = rng.Cells.Address ' Change `ws.cells(1, 2)` to whatever destination you like
End Sub
Edit: Ah, beaten by 16 seconds by #RamAnuragi ...but anyways, they're slightly different ways to tackle the question so I'll leave it.
Edit: For funsies, here's another way to put them all in a column, one row per cell...and more, per your comments.
Sub listEmptyCells()
Dim emptyAddresses() As String
Dim i As Long
Dim ws As Worksheet
Dim rng As Range
Set ws = Sheets("Sheet1") ' CHANGE AS NECESSARY
Set rng = ws.Range("A1:A15")
If WorksheetFunction.CountBlank(rng) = 0 Then
MsgBox ("No empty cells in the range")
Exit Sub
End If
emptyAddresses() = Split(rng.SpecialCells(xlCellTypeBlanks).Address, ",")
For i = LBound(emptyAddresses) To UBound(emptyAddresses)
ws.Cells(i + 1, 2).Value = emptyAddresses(i)
Next i
End Sub

Visual Basic 2007 Adding values

To start with I'm not really a wise man. So I was trying to add up two values and display them in a third (that's easy) but I also wanted it to repeat an infinite amount of times (namely that it does the same for every row, let's say I place the result in I5, I want also, on every I under it (I6, I7, I8, etc...)
Should it be:
Private Sub Worksheet_Change()
if IsNumeric(Range("B1").sort) And IsNumeric(Range("B2").sort) then
Dim value1 As Single
Dim value2 As Single
range(I5).sort = value+1 + value2
End Sub
Or as I think I'm horribly mistaken?
You're using the .Sort property of Range where you should be using .Value.
There's a couple of ways to achieve what you're looking to do. First option is to iterate through the range and add the relevant value to each cell like so:
Public Sub addCells()
Dim rng As Range, cell As Range
'Set the sheet name
With ThisWorkbook.Worksheets("Sheet_Name")
'Set the range below:
Set rng = .Range("I1:I10")
'Loop through range and add cells together
For Each cell In rng
cell.Value = cell.Offset(0, 2) + cell.Offset(0, 1)
Next cell
End Sub
Another way to do it if the values to be added is ordered in for example column A and B would be:
Public Sub addCells()
Dim rng As Range, cell As Range
'Set the sheet name
With ThisWorkbook.Worksheets("Sheet1")
'Add the first formula to the sheet
.Range("C1").Value = "=SUM(A1+B1)"
'Change the range below to the range to be filled
.Range("C1").AutoFill Destination:=.Range("C1:C10")
End With
End Sub

Resources