Create Named Ranges from Subtotal Group - vba

I need to create named ranges from a sheet to which I have added subtotals.
So as shown in the picture I need to create with VBA named ranges for all the groups (E4:F16 shown on the picture). The subtotals are created for each change in Column D ("Group"). The additional rows added by the subtotal wizard (Row 17 as shown) should not be included in the named range. I need to create about 10 similar named ranges in total.
The total number of rows with data on that sheet (I've named it R 14) is fixed but the number of elements within a group is variable. So for instance I need code to find out that cell A17 is empty and create a named range E4:F16.
So far I managed to create a public function that can create a named range given start row, end row, start column and end column:
Public Function createNamedRangeDynamic_1(sheetName As String, _
myFirstRow As Long, _
myLastRow As Long, _
myFirstColumn As Long, _
myLastColumn As Long, _
counter As Integer)
Dim myWorksheet As Worksheet
Dim myNamedRangeDynamic As Range 'declare object variable to hold reference to cell range
Dim myRangeName As String 'declare variable to hold defined name
Set myWorksheet = ThisWorkbook.Worksheets(sheetName) 'identify worksheet containing cell range
myRangeName = sheetName & "_" & counter 'specify defined name
With myWorksheet.Cells
Set myNamedRangeDynamic = .Range(.Cells(myFirstRow, myFirstColumn), .Cells(myLastRow, myLastColumn)) 'specify cell range
End With
ThisWorkbook.Names.Add Name:=myRangeName, RefersTo:=myNamedRangeDynamic 'create named range with workbook scope. Defined name is as specified. Cell range is as identified, with the last row and column being dynamically determined
End Function
My problem is that I can't make any kind of subroutine that can use the above code to produce the named ranges. I tried something like the following:
Sub makeRanges()
Dim sheetName As String
Dim firstRow As Long
Dim nextRow As Long
Dim lastRow As Long 'the lowest row of the group/range
Dim endRow As Long 'the last row with data on the sheet
Dim firstCol As Long
Dim lastCol As Long
Dim cell As Range
Dim myWorksheet As Worksheet
Dim counter As Integer
sheetName = "R 14"
firstCol = 5
lastCol = 6
groupNum = 9
fistRow = 4
endRow = 147
counter = 1
Set myWorksheet = ThisWorkbook.Worksheets(sheetName)
With myWorksheet.Cells
For Each cell In .Range("A" & firstRow, "A" & endRow)
If cell.Value = "" Then
nextRow = cell.Row
Exit For
End If
Next cell
lastRow = nextRow - 1
Call createNamedRangeDynamic_1(sheetName, _
firstRow, _
lastRow, _
firstCol, _
lastCol, _
counter) ' create named range
firstRow = nextRow + 1
counter = counter + 1
End With
End Sub
So that's my progress so far.

you can use Areas property of Range object and boil all that down to:
Option Explicit
Sub makeRanges()
Dim sheetName As String, sheetNameForNamedRange as String
Dim counter As Long
sheetName = "R 14"
sheetNameForNamedRange = Replace(sheetName, " ", "_") ' named range name doesn't accept blanks
Dim area As Range
With ThisWorkbook 'reference wanted workbook
For Each area In .Worksheets(sheetName).Range("A4:A147").SpecialCells(xlCellTypeConstants).Areas ' loop through areas of the not empty cell range along column A in 'sheetName' sheet of referenced workbook
counter = counter + 1 ' update counter
.Names.Add Name:=sheetNameForNamedRange & "_" & Format(counter, "00"), RefersTo:=area.Offset(, 4).Resize(, 2) ' add current named range as current area offset 4 columns to the right and wide two columns
Next
End With
End Sub
note: Format(counter, "00") is to have last two digits as format "_01, "_02", etc..

Related

VBA Cut entire row based on text in cell and paste to new sheet

I am attempting to have VBA scan cells in column DQ for a specific text value of "AcuteTransfer" and then to cut the row containing that cell and past into the first available row of a new sheet.
This value would be listed multiple times and each listing would need to be cut and pasted over
sheet containing the cell is "adds&reactivates" and sheet where row would be pasted to is "ChangeS".
Any recommendations would be amazing.
So far I have
Sub ohgodwhathaveIdone()
Dim endRow As Long
Dim Match1() As Variant
Dim ws As Worksheet
Set ws = Worksheets("adds&reactivates")
ICount = 0
endRow = Sheets("adds&reactivates").Range("DQ999999").End(xlUp).Row
Match1 = Sheet1.Range("DQ2:DQ" & endRow)
For I = LBound(Match1) To UBound(Match1)
If Match1(I, 1) = "AcuteTransfer" Then
Sheets("adds&reactivates").Cells(I, "A").EntireRow.Copy Destination:=Sheets("changes").Range("A" & Sheets("Changes").Rows.Count).End(xlUp).Offset(1)
Else
End If
Next I
End Sub
Try this out - this is assuming both pages have headers on row 1.
Option Explicit
Sub Test()
Dim sht1 As Worksheet, sht2 As Worksheet
Dim i As Long
Set sht1 = ThisWorkbook.Worksheets("adds&reactivates")
Set sht2 = ThisWorkbook.Worksheets("ChangeS")
For i = 2 To sht1.Cells(sht1.Rows.Count, "DQ").End(xlUp).Row
If sht1.Range("DQ" & i).Value = "AcuteTransfer" Then
sht1.Range("A" & i).EntireRow.Cut sht2.Range("A" & sht2.Cells(sht2.Rows.Count, "DQ").End(xlUp).Row + 1)
End If
Next i
End Sub

Copy columns by its header value

I would like to copy column from excel "Book1" to another excel "Book2" by determined its cell value.
Let's say the header columns in Book1 are Name, Age, Gender, Address and Group. I want to copy the column "Name", "Age" and "Group" to "Book2". Below coding is what I've done to pull column data by cell coordinate.
Is it possible if I can pull the column from its header value?
Sub copyColumns()
Dim lr As Long, r As Long
Set src = ThisWorkbook.Worksheets("Sheet1")
Set tgt = Workbooks("Book2.xlsx").Worksheets("Sheet1")
lr = src.Cells(Rows.Count, 1).End(xlUp).Row
For i = 2 To lr
src.Cells(i, 1).copy
r = tgt.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
src.Paste Destination:=tgt.Cells(r, 1)
src.Cells(i, 2).copy
src.Paste Destination:=tgt.Cells(r, 2)
src.Cells(i, 5).copy
src.Paste Destination:=tgt.Cells(r, 3)
Next i
End Sub
There is number of solutions to this problem. For example, declare variables:
Dim rw As Range
Dim cl As Range
Dim sFields As String
Dim V
Dim j As Integer
Chose your column names for copying:
sFields = "Name|Age|Group"
V = Split(sFields, "|")
And then, inside your For i loop, make two another loops:
For Each cl In Intersect(src.Rows(i), src.UsedRange)
For j = 0 To UBound(V)
If cl.EntireColumn.Range("A1") = V(j) Then
tgt.Cells(i, j).Value = src.Cells(i, j).Value
End If
Next j
Next cl
Intersect(src.Rows(i), src.UsedRange) will chose all cells in row in the range that is actually used (that is, it will not loop through all 16 384 columns. All columns names are in one variable sFields, you can easily modify it. It is string separated with pipes, you will probably never use pipes (|) in your field names, so it is safe.
A few tips along the way:
Always declare every variable you want to use and user Option Explicit at the beginning of your module.
Dont copy every single cell, copy a range
See the updated code below:
Sub copyColumns2()
Dim src As Worksheet, tgt As Worksheet
Dim lr As Long, r As Long, I As Long, Col As Long
Dim ColsToCopy, ColToCopy, counter As Integer
ColsToCopy = Array("Name", "Age", "Group")
Set src = ThisWorkbook.Worksheets("Sheet1")
Set tgt = Workbooks("Book2.xlsx").Worksheets("Sheet1")
lr = src.Cells(Rows.Count, 1).End(xlUp).Row
For Col = 1 To 20
For Each ColToCopy In ColsToCopy
If src.Cells(2, Col).Value = ColToCopy Then
counter = counter + 1
src.Range(src.Cells(2, Col), src.Cells(lr, Col)).Copy tgt.Cells(2, counter)
Exit For
End If
Next ColToCopy
Next Col
End Sub
If you would like to copy the column by column header, you can use this function to get the letter of your header:
Function Letter(oSheet As Worksheet, name As String, Optional num As Integer)
If num = 0 Then num = 1
Letter = Application.Match(name, oSheet.Rows(num), 0)
Letter = Split(Cells(, Letter).Address, "$")(1)
End Function
Implementation:
Sub copycolum()
Dim ws As Worksheet, ws2 As Worksheet
Set ws = Sheets("Sheet1"): Set ws2 = Sheets("Sheet2"):
ws.Range(Letter(ws, "Gender") & "2:" & Letter(ws, "Gender") & ws.Range(Letter(ws, "Gender") & "1000000").End(xlUp).Row).Copy Destination:=ws2.Range(Letter(ws2, "Gender") & "2")
End Sub
Note that the function is set to default at row 1, in other words your header is in column 1, if you like you can change this to whatever row your data is in.

VBA to copy rows from one sheet to another using 3 criteria

I'm trying to use a VBA to search in the sheet of one workbook "Transactional Files" and copy to a sheet in the workbook "Utilities" based on three criteria. The first one is, If column A="PV", the second criteria is if column M has either "Utilities-Water" "Utilities-Electric" or "Utilities Gas", and the third one is if column AE isn't already in the sheet, then copy and paste the row if all three are met.
I found this template, but not sure where to plug in my criteria:
Sub copyRow()
Dim ws1 As Worksheet, ws2 As Worksheet
Dim lastrowDest As Long, currowSrc As Long, currowDest As Long, lastrowSrc
As Long
Dim critvalue1 As String
Set ws1 = Sheets("Sheet2")
Set ws2 = Sheets("Sheet1")
lastrowSrc = ws2.Range("AE" & Rows.Count).End(xlUp).Offset(1).Row - 1
lastrowDest = ws1.Range("AE" & Rows.Count).End(xlUp).Row
For currowSrc = 2 To lastrowSrc
critvalue1 = ws2.Range("E" & currowSrc).Value
ws2.Cells(6, 5).Value = critvalue1
For currowDest = 2 To lastrowDest
If ws1.Range("E" & currowDest).Value = critvalue1 Then
ws2.Rows(currowSrc).Copy Destination:=ws1.Range("A" & currowDest)
End If
Next currowDest
Next currowSrc
End Sub
Thanks!!!
The example code you gave is from one sheet to another.
You want to check one sheet in one workbook, to another sheet in A DIFFERENT WORKBOOK.
So you'll have to open that workbook (And when done close it).
Then you'll look up row by row inside our sheet
Dim trx as workbook ' the other workbook (i.e. the other excel file).
' trx is a vba variable that will hold the contents of the transactions excel file
' a workbook has sheets in it.
dim sheet1 as worksheet, sheet2 as worksheet
' these are two vba variables holding the two sheets we'll work on.
dim criteria1 as boolean, criteria2 as boolean, criteria3 as boolean
' boolean variables can have the value of True or False only.
' just for reading
dim columnAindex as integer, columnMindex as integer, columnAEindex as integer
columnAindex = 1
columnMindex = Range("1M").column ' 13
columnAEindex = Range("AE").column ' yup, that number
set trx = Workbooks.open("trx.xlsx")
set sheet2 = trx.Sheets(1) ' or whatever sheet by index or name.
For Each myrow in Rows
criteria1 = (myrow.cells(1,columnAindex) = "PV') ' .cells(1,1) is columnA of current row
m = myrow.cells(1, columnMindex) ' value in cell of this current row, at column M
criteria2 = ( (m = this) or (m = that) or (m = theother) )
' if the value in AE is unique for each row, this will work:
' I got that from another question on stack overflow
ae = myrow.cells(1, columnAEindex) ' value in our row at column AE
set trxAEcolumnRange = sheet2.Range("AE:AE")
criteria3 = IsError(Application.Match(ae, trxAEcolumnRange, 0)) ' true if could not find a match to the value in our row's ae.
if criteria1 and criteria2 and criteria3 then copypasterow(myrow)
next
trx.close()
end sub
sub CopyPasteRow(aRow)
aRow.copy
sheet2.range.end.paste ' no need for xlup because this is the next empty row after the data
end sub

Dynamically set Ranges to the columns in VBA

I am importing some date to worksheet which needs to be ranged for validation and reference in other worksheets.
Say I have 4 columns in worksheet(WS1) but the row count is dynamic on every import. How can i range the columns(A:D)?
Please help.
Regards,
Mani
Use a lastRow variable to determine the last row. I included a few examples of this. Also on this example is a lastCol variable.. You can use this if the number of Columns is dynamic as well.
Private Sub lastRow()
Dim lastRow As Long
Dim lastCol As Long
Dim sheet As String
sheet = "WS1"
lastRow = Sheets(sheet).Range("A" & Rows.Count).End(xlUp).row 'Using Range()
lastRow = Sheets(sheet).Cells(Rows.Count, "A").End(xlUp).row 'Using Cells()
lastCol = Sheets(sheet).Cells(2, Columns.Count).End(xlToLeft).Column
End Sub
You can loop through your sheet easily enough using variables also. Using Cells(row,col) instead of Range(A1). you can use numbers or a letter in quotes for the column as shown in the example.
This example looks at WS1 and matches someValue. If the value in Column A of WS1 = somevalue, the record is copied to a "Master" Sheet.
Sub LoopExample()
Dim mRow As Long 'used for a master row
For lRow = 2 To lastRow
If Sheets(sheet).Cells(lRow, 1) = someValue Then
'perform something here like this. Copy columns A:D to the Master Sheet if match
For lCol = 1 To 4 'or you could go 1 to lastCol if you needed it dynamic
Sheets("MASTER").Cells(mRow, lCol) = Sheets(sheet).Cells(lRow, lCol) 'mRow as Row on Master
Next lCol
mRow = mRow + 1 'Increment the Master Row
End If
Next lRow
End Sub
Thanks anyways. But what i wanted was just to Name ranges the columns in worksheet.
I have already accomplished the copy and paste (Loading data b/w worksheets).
This is what i wanted.
vRowCount = DestWorkSheet.Cells(1, 1).SpecialCells(xlCellTypeLastCell).Row
vColCount = DestWorkSheet.Cells(1, 1).SpecialCells(xlCellTypeLastCell).Column
DestWorkSheet.usedRange.Columns.AutoFit
AddNamedRange Dest_RATES, DATA_Dest_RATES
Where AddNamedRange is a function,
Public Sub AddNamedRange(ByVal sheetCodeName As String, ByVal namedRange As String)
Dim rngToBeNamed As Range
Dim ws As Worksheet
On Error GoTo AddNamedRange_Error
Set rngToBeNamed = GetUsedRange(sheetCodeName)
Set ws = rngToBeNamed.Worksheet
ws.Names.Add name:=namedRange, RefersTo:=ws.Range(rngToBeNamed.Address)
On Error GoTo 0
Exit Sub
AddNamedRange_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure AddNamedRange of Module UtilitiesRange"
End Sub
Regards,
Mani
Seems like you could just use something like this in the sheet module:
Private Sub Worksheet_Change(ByVal target As Range)
Dim i As Long
Dim NamesOfNames(1 To 4) As String
NamesOfNames(1) = "NameOfColumn1"
NamesOfNames(2) = "NameOfColumn2"
NamesOfNames(3) = "NameOfColumn3"
NamesOfNames(4) = "NameOfColumn4"
For i = 1 To 4
ThisWorkbook.Names.Add Name:=NamesOfNames(i), _
RefersTo:=Range(Cells(1, i), Cells(Cells(Rows.Count, i).End(xlUp).Row, i))
Next i
End Sub

VBA column looping

I have a large Excel file and I need to replace all values in 12 columns completely.
Right now, there is a formula in each one of the cells, and I need to replace that formula with my own.
How do I loop through all those columns, knowing at what row it starts but don't know the end row (file is updated constantly). The hack of "A600000" seems overkill.
I am new to VBA and some guidance would be really appreciated.
ActiveSheet.UsedRange is the range of all the used cells on the current sheet.
You can use ActiveSheet.UsedRange.Rows.Count and .Columns.Count to get the height and widht of this range.
Here's a very crude function that hits every cell in the range:
Sub test()
Dim thisRange As Range
Set thisRange = ActiveSheet.UsedRange
With thisRange
For y = 1 To .Rows.Count
For x = 1 To .Columns.Count
thisRange.Cells(y, x).Value = "Formula here"
Next x
Next
End With
End Sub
But what you want may be different, can you be more specific?
The below will accomplish what you need to do. You just need to supply the startRow, .Sheets("Name"), and i arguments. If the columns are all the same length, then UsedRange will work fine if there are not random cells with values outside and below the columns you are interested in. Otherwise, try this in your code (on a throw away copy of your workbook)
Sub GetLastRowInColumn()
Dim ws as Excel.Worksheet
Set ws = Activeworkbook.Sheets("YOURSHEETNAMEHERE")
Dim startRow as long
startRow = 1
Dim lastRow as long
Dim i as long
For i = 1 to 12 'Column 1 to Column 12 (Adjust Accordingly)
lRow = ws.Cells(ws.Rows.Count, i).End(xlUp).Row
ws.Range(ws.Cells(startRow, i), ws.Cells(lRow, i)).Formula = "=Max(1)" 'Sample Formula
Next
End Sub
EDIT : Fixed typo
The below function will build the range with varying length columns. Use the function to return the desired range and fill all related cells in one shot.
Function GetVariantColumnRange(MySheet As Excel.Worksheet, _
TopRow As Long, StartColumn As Long, LastColumn As Long) As Excel.Range
Dim topAddress As String
Dim bottomAddress As String
Dim addressString As String
Dim i As Long
For i = StartColumn To LastColumn
topAddress = MySheet.Cells(TopRow, i).Address
bottomAddress = MySheet.Cells(MySheet.Rows.Count, i).End(xlUp).Address
addressString = addressString & ", " & topAddress & ":" & bottomAddress
Next
addressString = Right(addressString, Len(addressString) - _
InStr(1, addressString, ", ", vbBinaryCompare))
Set GetVariantColumnRange = MySheet.Range(addressString)
End Function
Usage follows...
Sub Test()
Dim myrange As Range
Set myrange = GetVariantColumnRange(ThisWorkbook.Sheets(1), 1, 1, 12)
myrange.Select 'Just a visual aid. Remove from final code.
myrange.Formula = "=APF($Jxx, "string1", "string2") "
End Sub