I found this piece of code which does 99% what i need.
Procedure description: In my workbook there is one SQL Sheet with named columns, based on the Column Header I have to loop through all other sheets (variable from 10 to 50 sheets) in the workbook where the Column Header has the identical name, all columns from the source SQL Sheet are copied to the goal sheets. In the goal sheets the column header consist of 4 rows, in the source the column header has only 1 row.
Problem-1: How can I copy the column without the header and paste the content with an offset of 4 rows.
Problem-2: How can I copy only the real used range, the workbook is getting huge.
Code-Sample:
Sub Test()
Dim Sh2Cell As Range
Dim Sh3Cell As Range
Dim ShQuelleTitle As Range
Dim ShZielTitle As Range
'Here we loop through the Range where the Title Columns for source and goal sheet are stored
'The columns in the Source Sheet do not have the same order as in the Goal Sheet
Set ShQuelleTitle = Sheets("SQL").Range("SQL_Titel")
Set ShZielTitle = Sheets("Ziel").Range("Ziel_Titel")
For Each Sh2Cell In ShQuelleTitle
For Each Sh3Cell In ShZielTitle
If Sh2Cell = Sh3Cell Then
Sh2Cell.EntireColumn.Copy Sh3Cell.EntireColumn
' Problem-1 is: in the goal sheet the copy range has to be shifted 4 rows down because
' i have different column title structure which has to be maintained (with this goal
' sheet there happens a txt-export from another external developer.
' Problem-2 is: how can i only copy and paste cells with content - the worksheets are getting
' huge on file size if the copy range has some weird formatting
End If
Next
Next
End Sub
Sub UpDateData()
Dim i As Long
Dim j As Long
Dim k As Long
Dim n As Long
Dim wData As Worksheet
Dim Process(1 To 2) As String
Dim iProc As Long
Dim Dict As Object
Process(1) = "SQL"
Process(2) = "ACCOUNT ACC STD"
Set wData = Sheets("ACCOUNT")
Set Dict = CreateObject("Scripting.Dictionary")
With wData
For j = 1 To .Cells(1, .Columns.Count).End(xlToLeft).Column
If Len(.Cells(1, j)) > 0 Then Dict.Add LCase$(.Cells(1, j)), j
Next j
End With
i = 5
For iProc = 1 To 2
With Sheets(Process(iProc))
n = .Cells(.Rows.Count, 1).End(xlUp).Row
For j = 1 To .Cells(1, .Columns.Count).End(xlToLeft).Column
If Dict.exists(LCase$(.Cells(1, j))) Then
k = Dict(LCase$(.Cells(1, j)))
.Cells(2, j).Resize(n - 1).Copy wData.Cells(i, k).Resize(n - 1)
End If
Next j
End With
i = i + n - 1
Next iProc
End Sub
You can loop through range as if it was an array:
Dim srcRng As Range
dim trgRng As Range
Dim iii As Long
Dim jjj As Long
Dim iRowStart As Long
Set srcRng = Sheets("your_source_sheet").Range("source_range")
Set trgRng = Sheets("your_target_sheet").Range("target_range")
iRowStart = 4
For iii = iRowStart To UBound(srcRng(), 1)
For jjj = 1 To UBound(srcRng(), 2) ' <~~ necessary only if you were dealing with more than one column
With trgRng
If srcRng(iii, jjj).Value <> "" Then .Cells(.Rows.Count + 1, jjj).Value = srcRng(iii, jjj).Value
End With
Next jjj
Next iii
Set srcRng = Nothing
Set trgRng = Nothing
I haven't tested the code, but it should do the trick
Sub CopyHeaders()
Dim header As Range, headers As Range
Set headers = Worksheets("ws1").Range("A1:Z1")
For Each header In headers
If GetHeaderColumn(header.Value) > 0 Then
Range(header.Offset(1, 0), header.End(xlDown)).Copy Destination:=Worksheets("ws2").Cells(4, GetHeaderColumn(header.Value))
End If
Next
End Sub
Function GetHeaderColumn(header As String) As Integer
Dim headers As Range
Set headers = Worksheets("ws2").Range("A1:Z1")
GetHeaderColumn = IIf(IsNumeric(Application.Match(header, headers, 0)), Application.Match(header, headers, 0), 0)
End Function
Related
I'm new in VBA and actually don't know how to deal with that task. Maybe you can help me.
I have two tables in two sheets.
Table from sheet 1 is updated daily.
What I need to do is check if any value in column A (sheet 1) is in column A (sheet 2).
If yes, then do nothing.
If no, then copy whole row into the table in sheet 2.
Basing on google results I started to write some code but I stuck.
Dim source As Worksheet
Dim finaltbl As Worksheet
Dim rngsource As Range
Dim rngfinaltbl As Range
'Set Workbook
Set source = ThisWorkbook.Worksheets("Sheet 1")
Set finaltbl = ThisWorkbook.Worksheets("Sheet 2")
'Set Column
Set rngsource = source.Columns("A")
Set rngfinaltbl = finaltbl.Columns("A")
I assume that next I need to write some loop but I really don't know how it works.
Update Worksheet With Missing (Unique) Rows (Dictionary)
Adjust the values in the constants section.
Sub UpdateData()
' Source
Const sName As String = "Sheet1"
Const sFirstCellAddress As String = "A2"
' Destination
Const dName As String = "Sheet2"
Const dFirstCellAddress As String = "A2"
' Reference the destination worksheet.
Dim dws As Worksheet: Set dws = ThisWorkbook.Worksheets(dName)
Dim drg As Range
Dim dCell As Range
Dim drCount As Long
' Reference the destination data range.
With dws.Range(dFirstCellAddress)
Set dCell = .Resize(dws.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If dCell Is Nothing Then Exit Sub ' no data in column range
drCount = dCell.Row - .Row + 1
Set drg = .Resize(drCount)
End With
Dim Data As Variant
' Write the values from the destination range to an array.
If drCount = 1 Then
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = drg.Value
Else
Data = drg.Value
End If
' Write the unique values from the array to a dictionary.
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim Key As Variant
Dim dr As Long
For dr = 1 To drCount
Key = Data(dr, 1)
If Not IsError(Key) Then ' exclude errors
If Len(Key) > 0 Then ' exclude blanks
dict(Key) = Empty
End If
End If
Next dr
' Reference the source worksheet.
Dim sws As Worksheet: Set sws = ThisWorkbook.Worksheets(sName)
Dim srg As Range
Dim sCell As Range
Dim srCount As Long
' Reference the source data range.
With sws.Range(sFirstCellAddress)
Set sCell = .Resize(sws.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If sCell Is Nothing Then Exit Sub ' no data in column range
srCount = sCell.Row - .Row + 1
Set srg = .Resize(srCount)
End With
' Write the values from the source range to an array.
If srCount = 1 Then
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = srg.Value
Else
Data = srg.Value
End If
Dim surg As Range
Dim sr As Long
' Loop through the source values...
For sr = 1 To srCount
Key = Data(sr, 1)
If Not IsError(Key) Then ' exclude errors
If Len(Key) > 0 Then ' exclude blanks
If Not dict.Exists(Key) Then ' if source value doesn't exist...
dict(Key) = Empty ' ... add it (to the dictionary)...
If surg Is Nothing Then ' and combine the cell into a range.
Set surg = srg.Cells(sr)
Else
Set surg = Union(surg, srg.Cells(sr))
End If
End If
End If
End If
Next sr
' Copy all source rows in one go below ('.Offset(1)') the last cell.
If Not surg Is Nothing Then
surg.EntireRow.Copy dCell.Offset(1).EntireRow
End If
MsgBox "Data updated.", vbInformation
End Sub
No you don't need a loop. You need the Find function for a Range
See Documentation for Find Method (Excel)
also Excel VBA Find A Complete Guide
I'm having a problem with my excel program. I want to make the names and the telephone numbers to paste at the other sheet, but names must be sorted randomly and the phone numbers must be the same. For Example at the first sheet i have Kalin Kalinov +22222222 and Martin Martinov +99119911 and at the other sheet after the copy paste action they must be like Martin Martinov +99119911 and Kalin Kalinov +22222222.
Sub GenerateNames()
Dim ssheet1 As Worksheet
Dim rnsheet As Worksheet
Set ssheet1 = ThisWorkbook.Sheets("Sheet1")
Set rnsheet = ThisWorkbook.Sheets("RandomNames")
ssheet1.Range("A3:A70").Copy rnsheet.Range("A3:A70")
ssheet1.Range("B3:B70").Copy rnsheet.Range("B3:B70")
End Sub
Add something like this, and apply it either on the source sheet or on the target sheet:
Range("C3").Formula = "=RAND()"
Range("C3").AutoFill Destination:=Range("C3:C70")
Range("A:C").Sort key1:=Range("C3"), order1:=xlAscending, Header:=xlYes
It creates a column of random values, used for sorting. You might want to delete it afterwards.
Sub randomName()
Dim ws As String, ws2 As String, rg As Range, rg2 As Range
Dim DataRange As Variant, i As Integer
Dim n As Integer, tmp As String
Dim nData As Integer
'== set by user
nData = 70 '== data size
ws = "sheet1": ws2 = "RandomNames" '== sheets name
Set rg = Sheets(ws).Cells(3, 1): Set rg2 = Sheets(ws2).Cells(3, 1) '=range with start row
'== Run
rg2.Resize(nData, 2).Value = rg.Resize(nData, 2).Value
DataRange = rg.Resize(nData).Value
For i = 1 To UBound(DataRange)
n = CLng(Rnd(i) * Second(Now) * 100) Mod UBound(DataRange) + 1
If i <> n Then tmp = DataRange(n, 1): DataRange(n, 1) = DataRange(i, 1): DataRange(i, 1) = tmp
Next i
rg2.Resize(nData) = DataRange: Set rg = Nothing: Set rg2 = Nothing
End Sub
I am trying to run a macro on 3 different ranges, one after another. Once the range is selected, the code works just fine (where variables F and L are defined). I would like to set r1-r3 as Ranges I need and then use a string variable to concatenate the range numbers together. This code works, but doesn't provide the starting and ending row number in the range selected. This is vital because it tells the "TableCalc" macro when to start and stop the code. I would then like to move on to the next range. Thanks for your help.
Sub TestRangeBC()
WS.Select
Dim r1 As Range
Dim r2 As Range
Dim r3 As Range
Dim rngx As String
Dim num As Integer
Dim rng As Range
Set r1 = WS.Range("ONE")
Set r2 = WS.Range("TWO")
Set r3 = WS.Range("THREE")
For num = 1 To 3
rngx = "r" & num
Set rng = Range(rngx)
Dim F As Integer
Dim L As Integer
F = rng.Row + 1
L = rng.Row + rng.Rows.Count - 2
Cells(F, 8).Select
Do While Cells(F, 8) <> "" And ActiveCell.Row <= L
'INSERT SITUATIONAL MACRO
Call TableCalc
WS.Select
ActiveCell.Offset(1, 0).Select
Loop
Next num
End Sub
This is not the answer (as part of your code and what you are trying to achieve is unclear yet), but it is a "cleaner" and more efficient way to code what you have in your original post.
Option Explicit
Dim WS As Worksheet
Your original Sub shorten:
Sub TestRangeBC()
' chanhe WS to your Sheet name
Set WS = Sheets("Sheet1")
Call ActiveRange("ONE")
Call ActiveRange("TWO")
Call ActiveRange("THREE")
End Sub
This Sub gets the Name of the Named Range (you set in your workbook) as a String, and sets the Range accordingly.
Sub ActiveRange(RangeName As String)
Dim Rng As Range
Dim F As Integer
Dim L As Integer
Dim lRow As Long
With WS
Set Rng = .Range(RangeName)
' just for debug purpose >> to ensure the right Range was passed and set
Debug.Print Rng.Address
F = Rng.Row + 1
L = Rng.Row + Rng.Rows.Count - 2
lRow = F
' what you are trying to achieve in this loop is beyond me
Do While .Cells(F, 8) <> "" And .Cells(lRow, 8).Row <= L
Debug.Print .Cells(lRow, 8).Address
'INSERT SITUATIONAL MACRO
' Call TableCalc
' not sure you need to select WS sheet again
WS.Select
lRow = lRow + 1
Loop
End With
End Sub
What are you trying to test in the loop below, what are the criteria of staying in the loop ?
Do While Cells(F, 8) <> "" And ActiveCell.Row <= L
it's really hard to tell what you may want to do
but may be what follows can help you clarifying and (hopefully) doing it!
first off, you can't "combine" variable names
So I'd go with an array of named ranges names (i.e. String array) to be filled by means of a specific sub:
Function GetRanges() As String()
Dim ranges(1 To 3) As String
ranges(1) = "ONE"
ranges(2) = "TWO"
ranges(3) = "THREE"
GetRanges = ranges
End Function
so that you can clean up your "main" sub code and keep only more relevant code there:
Sub TestRangeBC()
Dim r As Variant
Dim ws As Worksheet
Set ws = Worksheets("Ranges") '<--| change "Ranges" to your actual worksheet name
For Each r In GetRanges() '<--| loop through all ranges names
DoIt ws, CStr(r) '<--| call the range name processing routine passing worksheet and its named range name
Next r
End Sub
the "main" sub loops through the named ranges array directly collected from GetRanges() and calls DoIt() to actually process the current one:
Sub DoIt(ws As Worksheet, rangeName As String)
Dim cell As Range
Dim iRow As Long
With ws.Range(rangeName) '<--| reference the passed name passed worksheet named range
For iRow = .Rows(2).Row To .Rows(.Rows.Count - 2).Row '<--| loop through its "inner" rows (i.e. off 1st and last rows)
Set cell = ws.Cells(iRow, 8) '<--| get current row corresponding cell in column "F"
If cell.value = "" Then Exit For '<--| exit at first blank column "F" corresponding cell
TableCalc cell '<-- call TableCalc passing the 'valid' cell as its parameter
Next iRow
End With
End Sub
I have 3 worksheets(base) and a master worksheet (master) to compare with.
For each base worksheet, I need to compare the string value in each row for Col H (e.g.) to each row in master worksheet for Col G (e.g.). If the string value does not exist in the whole col G, the row in the base worksheet must be copied over to the master worksheet.
TIA!
I think its easy enough the use the worksheet function Match which will error if the items doesn't exist so we handle the error. Here's my answer:
Sub MyCompare()
Dim wksMaster As Worksheet
Dim wksBases(2) As Worksheet
Dim wksBase As Variant
Dim intRowCountBase As Integer
Dim intRowCountMaster As Integer
Dim rngCell As Range
Dim rngMasterColG As Range
Dim intMatch As Integer
'set up sheet vaiables
Set wksMaster = ActiveWorkbook.Worksheets("Master")
Set wksBases(0) = ActiveWorkbook.Worksheets("Base1")
Set wksBases(1) = ActiveWorkbook.Worksheets("Base2")
Set wksBases(2) = ActiveWorkbook.Worksheets("Base3")
'get the range of the master sheet col G
intRowCountMaster = wksMaster.UsedRange.Rows.Count
Set rngMasterColG = wksMaster.Range(wksMaster.Cells(1, 7), wksMaster.Cells(intRowCountMaster, 7))
'Loop through the base sheets
For Each wksBase In wksBases
intRowCountBase = wksBase.UsedRange.Rows.Count
'Loop through the cells in col H of the base sheet
For Each rngCell In wksBase.Range(wksBase.Cells(1, 8), wksBase.Cells(intRowCountBase, 8))
If rngCell.Value <> "" Then 'only do something if there is a value in the base sheet
On Error Resume Next
'the match value will error if the item doesn't exist
intMatch = Application.WorksheetFunction.Match(rngCell.Value, rngMasterColG, 0)
If Err.Number > 0 Then ' ie there is no match
On Error GoTo 0
intRowCountMaster = intRowCountMaster + 1
'put the item on the master sheet
wksMaster.Cells(intRowCountMaster, 7).Value = rngCell.Value
'reset the master range
Set rngMasterColG = wksMaster.Range(wksMaster.Cells(1, 7), wksMaster.Cells(intRowCountMaster, 7))
End If
End If
Next rngCell
Next wksBase
End Sub
Here is the Macro I've just written out, unfortunately it doesn't seem to do anything and I can't find the error! I am trying to copy the column with the header "Offset Acct" from sheet 1 (SAPDump) to sheet 2 (Extract) which is blank. Can anyone see explain to me why this isn't working? Fairly new to VBA so it's probably an easy fix. Cheers
Sub ExtractData()
' Define sheets
Dim SAPDump As Worksheet
Dim Extract As Worksheet
' Set sheets
Set SAPDump = ActiveSheet
Set Extract = ThisWorkbook.Sheets("Extract")
' Define row and column counters
Dim r As Long
Dim c As Long
' Set last non-empty column
Dim lastCol As Long
lastCol = SAPDump.Cells(1, Columns.Count).End(xlToLeft).Column
' Set last non-empty row
Dim lastRow As Long
lastRow = SAPDump.Cells(Rows.Count, "A").End(xlUp).row
' Look a all columns
For c = 1 To c = lastCol
' Examine top column
If SAPDump.Cells(1, c).Value = "Offset Acct" Then
' Loop round all rows
For r = 1 To r = lastRow
' Copy column into A on Extract
Extract.Cells(r, 1) = SAPDump.Cells(r, c)
Next r
Else
End If
Next c
End Sub
You need to change these lines:
For c = 1 To c = lastCol
to
For c = 1 To lastCol
and
For r = 1 To r = lastRow
to
For r = 1 To lastRow
Edit:
A better way may be to do this:
Sub ExtractData()
' Define sheets
Dim SAPDump As Worksheet
Dim Extract As Worksheet
'Define Heading range
Dim rHeadings As Range
Dim rCell As Range
' Set sheets
Set SAPDump = ActiveSheet
Set Extract = ThisWorkbook.Sheets("Extract")
'Set Heading range.
With SAPDump
Set rHeadings = .Range(.Cells(1, 1), .Cells(1, Columns.Count).End(xlToLeft))
End With
'Look at each heading.
For Each rCell In rHeadings
If rCell.Value = "Offset Acct" Then
'If found copy the entire column and exit the loop.
rCell.EntireColumn.Copy Extract.Cells(1, 1)
Exit For
End If
Next rCell
End Sub
The set is not sure, how to run the same within excel macro.
Request you to send the same via .pdf formate.
Regards
Stalin.