My code below gives me a result with a unique customer codes base on Calculation sheet. However, I want to get my result base on the list that I have in Solution Sheet. Also want to run the macro within Solution Sheet. Any help will be appreciated.
Calculation Sheet
Solution Sheet
Sub cTotals()
Dim arr, arr2, arr3
Dim Calc As Worksheet: Set TS = Worksheets("Calculation")
Dim Sol As Worksheet: Set Sol = Worksheets("Solution")
Dim x As Long, i As Long, a As Long, c As Long, ct As Long
Dim GIVMM As Single, MSU As Double, Cases As Double
arr = Calc.Range("B2:H" & Cells(Rows.Count, 1).End(xlUp).Row)
arr2 = arr
With CreateObject("Scripting.Dictionary")
For x = LBound(arr) To UBound(arr)
If Not IsMissing(arr(x, 1)) Then .Item(arr(x, 1)) = 1
Next
arr = .Keys
End With
ReDim arr3(1 To UBound(arr) + 1, 1 To 7)
c = 1: ct = 1
For i = 0 To UBound(arr)
For a = 1 To UBound(arr2)
If arr2(a, 1) = arr(i) Then
arr3(i + 1, c) = arr(i)
arr3(i + 1, c + 1) = ct
ct = ct + 1
GIVMM = GIVMM + arr2(a, 5)
arr3(i + 1, c + 2) = GIVMM
MSU = MSU + arr2(a, 6)
arr3(i + 1, c + 3) = MSU
Cases = Cases + arr2(a, 7)
arr3(i + 1, c + 4) = Cases
End If
Next
ct = 1: GIVMM = 0: MSU = 0: Cases = 0
Next
Sol.Range("B6").Resize(UBound(arr3, 1), UBound(arr3, 2)) = arr3
End Sub
Using Data Structures (Array, Collection, Dictionary)
Option Explicit
Sub CalculateData()
' Define constants.
' Source
Const sName As String = "Calculation"
Const scCol As Long = 2
Dim sCols() As Variant: sCols = VBA.Array(4, 6, 7, 8)
' Destination
Const dName As String = "Solution"
Const dfRow As Long = 6
Const dcCol As Long = 2
Const dColumnOffset As Long = 1
Dim dOffsets() As Variant: dOffsets = VBA.Array(1, 2, 3, 4)
' Reference the workbook ('wb').
Dim wb As Workbook: Set wb = ThisWorkbook
' Write the values from the source range ('srg') to an array ('sData').
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim srg As Range: Set srg = sws.Range("A1").CurrentRegion
Dim sData() As Variant: sData = srg.Value
Dim srCount As Long: srCount = srg.Rows.Count
' Write the unique source values to the 'keys' ('sString')
' of a dictionary ('sDict'). Its 'items' ('sDict(sString)') will hold
' a collection of all the (source) rows ('r') where the 'key' appeared.
Dim sDict As Object: Set sDict = CreateObject("Scripting.Dictionary")
sDict.CompareMode = vbTextCompare
Dim sString As String
Dim r As Long
For r = 2 To srCount
sString = CStr(sData(r, scCol))
If Len(sString) > 0 Then
If Not sDict.Exists(sString) Then
Set sDict(sString) = New Collection
End If
sDict(sString).Add r
End If
Next r
' Write the values from the destination lookup column range ('dlrg')
' to a 2D one-based (one-column) array ('dlData').
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dlRow As Long: dlRow = dws.Cells(dws.Rows.Count, dcCol).End(xlUp).Row
Dim drCount As Long: drCount = dlRow - dfRow + 1
If drCount < 1 Then
MsgBox "No data in the destination column.", vbCritical
Exit Sub
End If
Dim dlrg As Range: Set dlrg = dws.Cells(dfRow, dcCol).Resize(drCount)
Dim dlData() As Variant
If drCount = 1 Then ' one cell
ReDim dlData(1 To 1, 1 To 1): dlData(1, 1) = dlrg.Value
Else ' multiple cells
dlData = dlrg.Value
End If
' Write the results to the destination array ('dData').
Dim cUpper As Long: cUpper = UBound(sCols)
Dim cCount As Long: cCount = cUpper + 1
Dim dData As Variant: ReDim dData(1 To drCount, 1 To cCount)
Dim dDict As Object: Set dDict = CreateObject("Scripting.Dictionary")
dDict.CompareMode = vbTextCompare
Dim sItem As Variant
Dim sValue As Variant
Dim dString As String
Dim c As Long
For r = 1 To drCount
dString = CStr(dlData(r, 1))
If Len(dString) > 0 Then
If sDict.Exists(dString) Then ' found in the dictionary
For Each sItem In sDict(dString) ' loop through the rows
For c = 0 To cUpper
If c = 0 Then ' unique count
sString = CStr(sData(sItem, sCols(c)))
dDict(sString) = Empty
Else ' sum
sValue = sData(sItem, sCols(c))
If VarType(sValue) = vbDouble Then ' is a number
dData(r, c + 1) = dData(r, c + 1) + sValue
'Else ' is not a number; do nothing
End If
End If
Next c
Next sItem
dData(r, 1) = dDict.Count
dDict.RemoveAll
' Else ' not found in the dictionary; do nothing
End If
' Else ' vbNullString ('""'); do nothing
End If
Next r
' Write the values from the destination array to the destination range.
With dws.Cells(dfRow, dcCol).Offset(, dColumnOffset)
.Resize(drCount, cCount).Value = dData
End With
' Inform.
MsgBox "Data calculated.", vbInformation
End Sub
Related
I have 2 sheets with multiple rows and columns like this:
Sheet1:
I want to search each value from Sheet1, Column B in Sheet2, Column B then:
If the value is equal => Copy the rest of the row in sheet1.
At the end, sheet1 should look like this:
and Sheet2 the same, I don't modify in that, only I take from that the rest of the rows.
Thank you very much,
I have tried something like this:
Sub Compare()
Dim n As Integer
Dim sh As Worksheets
Dim r As Range
n = 1000
Dim match As Boolean
Dim valE As Double
Dim valI As Double
Dim I As Long, J As Long
For I = 2 To n
val1 = Worksheets("Sheet1").Range("B" & I).Value
val2 = Worksheets("Sheet2").Range("B" & I).Value
If val1 = val2 Then
Worksheets("Sheet1").Range("C" & I).Value = Worksheets("Sheet2").Range("C" & I)
Worksheets("Sheet1").Range("D" & I).Value = Worksheets("Sheet2").Range("D" & I)
Worksheets("Sheet1").Range("E" & I).Value = Worksheets("Sheet2").Range("E" & I)
I = I + 1
End If
Next I
Application.ScreenUpdating = True
End Sub
It works for 10 values or so, but I have 1200 values and it just doesn't do anything.
A VBA Lookup: Copy Rows
Type Wks
Name As String
LookupColumn As Long
FirstColumn As Long
End Type
Sub LookupData()
Dim Src As Wks
Src.Name = "Sheet2"
Src.LookupColumn = 2
Src.FirstColumn = 3
Dim Dst As Wks
Dst.Name = "Sheet1"
Dst.LookupColumn = 2
Dst.FirstColumn = 3
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Read source.
Dim sws As Worksheet: Set sws = wb.Worksheets(Src.Name)
Dim srg As Range, slData() As Variant, srCount As Long, cCount As Long
With sws.Range("A1").CurrentRegion
srCount = .Rows.Count - 1
cCount = .Columns.Count
If srCount = 0 Then Exit Sub
Set srg = .Resize(srCount).Offset(1)
End With
With srg.Columns(Src.LookupColumn)
If srCount = 1 Then
ReDim slData(1 To 1, 1 To 1): slData(1, 1) = .Value
Else
slData = .Value
End If
End With
Dim cOffset As Long: cOffset = Src.FirstColumn - 1
cCount = cCount - cOffset
Dim svData() As Variant
With srg.Resize(, cCount).Offset(, cOffset)
If srCount * cCount = 1 Then
ReDim svData(1 To 1, 1 To 1): svData = .Value
Else
svData = .Value
End If
End With
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim r As Long, cString As String
For r = 1 To srCount
cString = CStr(slData(r, 1))
If Not dict.Exists(cString) Then dict(cString) = r
Next r
' Read destination.
Dim dws As Worksheet: Set dws = wb.Worksheets(Dst.Name)
Dim drg As Range, dlData() As Variant, drCount As Long
With dws.Range("A1").CurrentRegion
drCount = .Rows.Count - 1
If drCount = 0 Then Exit Sub
Set drg = .Resize(drCount).Offset(1)
End With
With drg.Columns(Dst.LookupColumn)
If drCount = 1 Then
ReDim dlData(1 To 1, 1 To 1): dlData(1, 1) = .Value
Else
dlData = .Value
End If
End With
Dim dvData() As Variant: ReDim dvData(1 To drCount, 1 To cCount)
' Lookup and write to destination.
Dim dr As Long, c As Long
For r = 1 To drCount
cString = CStr(dlData(r, 1))
If dict.Exists(cString) Then
dr = dict(cString)
For c = 1 To cCount
dvData(r, c) = svData(dr, c)
Next c
End If
Next r
Dim dfCell As Range: Set dfCell = drg.Columns(Dst.FirstColumn).Cells(1)
Dim dvrg As Range: Set dvrg = dfCell.Resize(drCount, cCount)
dvrg.Value = dvData
' Inform.
MsgBox "Data copied.", vbInformation
End Sub
I have below code in which i want to create a loop for multiple values that are available in Sheet1.Range(A2:A100) code will pick one by one each value and match then paste result in Column B.
thisvalue = Sheet1.Range("A2:A100"). Can someone please help me to create the loop. Your help will be appreciated.
Sub Macro1()
Dim thisvalue As Double, sh As Worksheet, lastR As Long, arr, arrFin, i As Long
thisvalue = 3.61
Set sh = Worksheets("Sheet1")
lastR = sh.Range("J" & sh.rows.count).End(xlUp).row
arr = sh.Range("E7:J" & lastR).Value
ReDim arrFin(1 To UBound(arr), 1 To 1)
For i = 1 To UBound(arr)
If arr(i, 1) < thisvalue And arr(i, 2) > thisvalue Then arrFin(i, 1) = arr(i, 6)
Next i
sh.Range("B2").Resize(UBound(arrFin), 1).Value = arrFin
End Sub
Please, try the next code:
Sub Macro1__2()
Dim thisvalue As Double, sh As Worksheet, lastR As Long
Dim arrVal, arr, arrFin, i As Long, j As Long
Set sh = Worksheets("Sheet2")
arrVal = Worksheets("Sheet1").Range("Q2:Q100").Value
Worksheets("Sheet1").Range("R2:R200").ClearContents
lastR = sh.Range("J" & sh.Rows.Count).End(xlUp).Row
arr = sh.Range("E7:J" & lastR).Value
For j = 1 To UBound(arrVal)
thisvalue = arrVal(j, 1)
If CStr(thisvalue) <> "" Then 'excluding the cases of empty cells. I didn't know that they may exist
arrFin = Worksheets("Sheet1").Range("R2:R200").Value 'firstly input the existing range in the array
For i = 1 To UBound(arr)
If arr(i, 1) < thisvalue And arr(i, 2) > thisvalue Then arrFin(j, 1) = arr(i, 6)
Next i
Worksheets("Sheet1").Range("R2").Resize(UBound(arrFin), 1).Value = arrFin
End If
Next j
MsgBox "Ready..."
End Sub
Ranges and Arrays
Option Explicit
Sub Macro1()
' Source
Const sName As String = "Sheet1"
Const slrCol As String = "J"
Const sCols As String = "E:J"
Const sfRow As Long = 7
Const scColLess As Long = 1
Const scColGreater As Long = 2
Const srCol As Long = 6
Const sCriteria As Double = 3.61
' Destination
Const dName As String = "Sheet1"
Const dFirst As String = "B2"
' Create a reference to the Workbook ('wb').
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
' Create a reference to the Source Worksheet ('sws').
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
' Create a reference to the Source Range ('srg').
If sws.Columns(sCols).Columns.Count < srCol Then Exit Sub ' too few columns
Dim slRow As Long: slRow = sws.Cells(sws.Rows.Count, slrCol).End(xlUp).Row
If slRow < sfRow Then Exit Sub ' no data
Dim rCount As Long: rCount = slRow - sfRow + 1 ' for source and destination
Dim srg As Range
Set srg = sws.Rows(sfRow).Columns(sCols).Resize(rCount)
' Write the values from the Source Range
' to the Source Array ('sData').
Dim sData As Variant: sData = srg.Value
' Define the Destination Array ('dData').
Dim dData As Variant: ReDim dData(1 To rCount, 1 To 1)
' Write the appropriate values from the Source Array
' to the Destination Array.
Dim cValue As Variant
Dim r As Long
For r = 1 To rCount
cValue = sData(r, scColLess)
If IsNumeric(cValue) Then
If cValue < sCriteria Then
cValue = sData(r, scColGreater)
If IsNumeric(cValue) Then
If cValue > sCriteria Then
dData(r, 1) = sData(r, srCol)
End If
End If
End If
End If
Next r
' Create a reference to the Destination Worksheet ('dws').
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
' Create a reference to the Destination Range ('drg').
Dim dfCell As Range: Set dfCell = dws.Range(dFirst)
Dim drg As Range: Set drg = dfCell.Resize(rCount)
' Write the values from the Destination Array
' to the Destination Range ('drg').
drg.Value = dData
' Clear the Destination Clear Range ('dcrg'),
' the range below the Destination Range.
Dim dcrg As Range
Set dcrg = drg.Resize(dws.Rows.Count - drg.Row - rCount + 1).Offset(rCount)
dcrg.Clear ' or maybe rather 'dcrg.ClearContents'
' Format the Destination Range.
'drg.Font.Bold = True
'drg.EntireColumn.AutoFit
'drg.Interior.Color = 14348258
' Save the workbook.
'wb.Save
End Sub
Source File
Master/Target
Sub Transfer_Reasons_decl()
Const srcID As Variant = "G. Reasons declnd & wdrawn clms" ' Name or Index e.g. "Sheet1" or 1
Const srcAddr1 As String = "B22:E26"
Const tgtID As Variant = "Reasons declnd & wdrawn clms" ' Name or Index e.g. "Sheet1" or 1
Const tgtCol1 As Variant = 3 ' Number or String e.g. 1 or "A"
Const Pattern As String = "*.xlsx*"
Dim wbPath As String: wbPath = ThisWorkbook.Path & Application.PathSeparator
Dim tgt As Worksheet: Set tgt = ThisWorkbook.Worksheets(tgtID)
Application.ScreenUpdating = False
Dim wb As Workbook, src As Worksheet, tgtCell1 As Range
Dim Source1 As Variant, Target1 As Variant ' Arrays
Dim Val1 As Variant
Dim i As Long, j As Long, l, m As Long, Name, Count As Long ' Counters (Longs)
Dim SrcRng As Range, TgtRng As Range
Name = tgt.Cells(Rows.Count, 3).End(xlUp).Row
Dim wbname As String: wbname = Dir(wbPath & Pattern)
Do Until wbname = ""
If wbname <> ThisWorkbook.Name Then
Name = Name + 1
ThisWorkbook.Worksheets("Reasons declnd & wdrawn clms").Cells(Name, 1) = wbname
ThisWorkbook.Worksheets("Reasons declnd & wdrawn clms").Cells(Name, 2) = FileDateTime(wbPath & wbname)
'******************************************************************
GoSub readSource
GoSub writeSource
GoSub writeTarget
End If
WorksheetNotFound:
wbname = Dir
Loop
Application.ScreenUpdating = True
Application.CutCopyMode = False
MsgBox "Copied data from " & Count & " workbook(s)"
Exit Sub
readSource:
' Write values from Source Range to Source Array.
On Error Resume Next
Set src = Workbooks.Open(wbPath & wbname).Worksheets(srcID)
Set src_B = Workbooks.Open(wbPath & wbname).Worksheets(srcID_B)
If Err.Number <> 0 Then GoTo closeSourceError
On Error GoTo 0
Source1 = src.Range(srcAddr1).Value
src.Parent.Close False
Return
writeSource:
' Write values from Source Array to Target Array.
'ReDim Target1(1 To 1, 1 To UBound(Source1) * UBound(Source1, 2))
ReDim Target1(1 To UBound(Source1), 1 To UBound(Source1) * UBound(Source1, 2))
'**************************************************
l = 0
For i = 1 To UBound(Source1)
For j = 1 To UBound(Source1, 2)
l = l + 1
If IsEmpty(Source1(i, j)) Then
Target1(i, j) = 0
'Target1(m, l) = 0
Else
Target1(i, j) = Source1(i, j)
'Target1(m, l) = Source1(i, j)
End If
Next j
Next i
Return
writeTarget:
' Write values from Target Array to Target Range.
Set tgtCell1 = tgt.Cells(tgt.UsedRange.Rows.Count, tgtCol1).End(xlUp).Offset(1)
tgtCell1.Resize(, UBound(Source1, 2)).Value = Target1
Count = Count + 1
Return
closeSourceError:
src.Parent.Close False
On Error GoTo 0
GoTo WorksheetNotFound
End Sub
Group Data
The code will write the resulting values (no formatting) to the destination worksheet.
Adjust the values in the constants section.
From One Mess into Another
Option Explicit
Sub TransferReasonsDecl()
Const sName As String = "Sheet1"
Const sSearch As String = "DC: TOP 5 REASONS"
Const sTitlesList As String _
= "DC: TOP 5 REASONS,#DCs,DC: TOP 5 REASONS,#WCs"
Const stPattern As String = "Source "
Const strOffset As Long = -1
Const sdrOffset As Long = 1
Const sRows As Long = 5
Const sCols As Long = 4
Const dName As String = "Sheet2"
Const dFirst As String = "A1"
Const drlCount As Long = 2
Const dclCount As Long = 2
Dim wb As Workbook: Set wb = ThisWorkbook
' Source
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim aCell As Range: Set aCell = sws.Cells(sws.Rows.Count, sws.Columns.Count)
Dim srg As Range: Set srg = sws.Cells
Dim fCell As Range: Set fCell = RefFirstOccurrence(srg, aCell, sSearch)
If fCell Is Nothing Then Exit Sub
' Populate the Dictionary ('dict'). Each key will contain a jagged array.
Dim dict As Object: Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim FirstAddress As String: FirstAddress = fCell.Address
Dim sData(0) As Variant
Dim Arr As Variant
Dim uMax As Long
Dim sTitle As String
Do
sTitle = CStr(fCell.Offset(strOffset).Value)
sData(0) = fCell.Offset(sdrOffset).Resize(sRows, sCols).Value
If dict.Exists(sTitle) Then
Arr = dict(sTitle)
AddItemToArray Arr, _
fCell.Offset(sdrOffset).Resize(sRows, sCols).Value
dict(sTitle) = Arr
If UBound(Arr) > uMax Then
uMax = UBound(Arr)
End If
Else
dict(sTitle) = sData
End If
Set aCell = fCell
Set fCell = RefFirstOccurrence(srg, aCell, sSearch)
Loop Until fCell.Address = FirstAddress
' Populate the Destination Array ('dData').
uMax = uMax + 1
Dim drCount As Long: drCount = uMax * sRows + dclCount
Dim tCount As Long: tCount = dict.Count
Dim dcCount As Long: dcCount = tCount * sCols + drlCount
Dim dData As Variant: ReDim dData(1 To drCount, 1 To dcCount)
Dim n As Long
Dim r As Long
' Write first row labels.
dData(1, 1) = "Name"
dData(2, 1) = "Name"
For n = 1 To uMax
For r = 1 To sRows
dData((n - 1) * sRows + r + dclCount, 1) = stPattern & n
Next r
Next n
' Write second row labels.
dData(1, 2) = "Name"
dData(2, 2) = "Name"
n = 0
Dim Key As Variant
' Write first column labels.
For Each Key In dict.Keys
n = n + 1
dData(1, (n - 1) * sCols + drlCount + 1) = Key
Next Key
' Write second column labels.
Dim sTitles() As String: sTitles = Split(sTitlesList, ",")
Dim tUpper As Long: tUpper = UBound(sTitles)
Dim c As Long
For n = 1 To tCount
For c = 0 To tUpper
dData(2, (n - 1) * sCols + c + drlCount + 1) = sTitles(c)
Next c
Next n
' Write the data.
n = 0
Dim t As Long
For Each Key In dict.Keys
n = n + 1
Arr = dict(Key)
For t = 0 To UBound(Arr)
For r = 1 To sRows
For c = 1 To sCols
dData(t * sRows + dclCount + r, _
(n - 1) * sCols + drlCount + c) = Arr(t)(r, c)
Next c
Next r
Next t
Next Key
' Write the values from the Destination Array
' to the Destination Range ('drg').
Dim dws As Worksheet: Set dws = wb.Worksheets(dName)
Dim dCell As Range: Set dCell = dws.Range(dFirst)
Dim drg As Range: Set drg = dCell.Resize(drCount, dcCount)
drg.Value = dData
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Creates a reference to the cell of the first occurrence
' of a string. The search is performed by rows and starts
' with the cell after the given cell ('AfterCell')...
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function RefFirstOccurrence( _
ByVal rg As Range, _
ByVal AfterCell As Range, _
ByVal SearchString As String) _
As Range
If rg Is Nothing Then Exit Function
Set RefFirstOccurrence _
= rg.Find(SearchString, AfterCell, xlFormulas, xlWhole, xlByRows)
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Adds an item to a zero-based array. Note the 'ByRef' i.e.
' that the given array ('Arr') is being changed in-place.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub AddItemToArray( _
ByRef Arr As Variant, _
ByVal Item As Variant)
If IsEmpty(Arr) Then Exit Sub
On Error Resume Next
Dim NewIndex As Long: NewIndex = UBound(Arr) + 1
If Err.Number > 0 Then
NewIndex = 0
End If
On Error GoTo 0
ReDim Preserve Arr(0 To NewIndex)
Arr(NewIndex) = Item
End Sub
I'm trying to create a macro that will go into a workbook, find cells that have a comma in them, split them and paste them starting at the last row in that column. The code below somewhat works but
It's limited in terms of changing the column range every time (a,b,c,etc). How do I dynamic, meaning run on multiple columns regardless of wherever the data starts?
Only start after the first row since I have headers.
The actual code below is a bit slow, what can I do to have it run more efficiently?
Sub LoopRange()
Dim rCell As Range
Dim rRng As Range
Dim myarray As Variant
Set rRng = Sheet1.Range("A:A")
For Each rCell In rRng
If InStr(1, rCell, ",") > 0 Then
myarray = Split(rCell, ",")
For i = 0 To UBound(myarray)
Range("A" & Rows.Count).End(xlUp).Offset(1).Value = myarray(i)
Next
Else
End If
Next
End Sub
Split Cell Values
Option Explicit
Sub GetSplitsTEST()
' The number of empty rows between the source (initial) data
' and the destination (resulting) data.
Const EmptyRows As Long = 0
Const Delimiter As String = ","
' Create a reference to the range.
Dim rg As Range
With Sheet1.Range("A1").CurrentRegion
'Debug.Print .Address ' with headers
Set rg = .Resize(.Rows.Count - 1).Offset(1)
'Debug.Print rg.Address ' without headers
End With
' Use the 'GetSplits' function to get the result in an array.
Dim Data As Variant: Data = GetSplits(rg, Delimiter)
' Validate the array.
If Not IsEmpty(Data) Then
' Write the result below the range.
rg.Cells(1).Offset(rg.Rows.Count + EmptyRows) _
.Resize(UBound(Data, 1), UBound(Data, 2)).Value = Data
End If
End Sub
Function GetSplits( _
ByVal rg As Range, _
Optional ByVal Delimiter As String = ",") _
As Variant
If rg Is Nothing Then Exit Function
Dim rCount As Long: rCount = rg.Rows.Count
Dim cCount As Long: cCount = rg.Columns.Count
Dim sData As Variant
If rCount = 1 And cCount = 1 Then
ReDim sData(1 To 1, 1 To 1): sData(1, 1) = rg.Value
Else
sData = rg.Value
End If
Dim cDat As Variant: ReDim cDat(1 To cCount, 1 To 3)
Dim rDat As Variant: ReDim rDat(1 To rCount)
Dim c As Long, sr As Long, n As Long, drCount As Long, maxCount As Long
For c = 1 To cCount
drCount = 0
cDat(c, 1) = rDat
cDat(c, 2) = rDat
n = 0
For sr = 1 To rCount
If InStr(1, sData(sr, c), Delimiter) > 0 Then
n = n + 1
cDat(c, 1)(n) = Split(sData(sr, c), Delimiter)
cDat(c, 2)(n) = UBound(cDat(c, 1)(n))
drCount = drCount + cDat(c, 2)(n) + 1
End If
Next sr
cDat(c, 3) = n
If drCount > maxCount Then
maxCount = drCount
End If
Next c
Dim dData As Variant: ReDim dData(1 To maxCount, 1 To cCount)
Dim dr As Long
For c = 1 To cCount
dr = 0
For n = 1 To cDat(c, 3)
For sr = 0 To cDat(c, 2)(n)
dr = dr + 1
dData(dr, c) = cDat(c, 1)(n)(sr)
Next sr
Next n
Next c
GetSplits = dData
End Function
I have a table ("horiz") with following values
and table ("data") that shows different values per column
I want to make a VBA code that will save table "data" as following.
Basically looking for a code, which can do it in the following way:
1)load "horiz" values as an array
2)load "data" as a range
3)delete all zero values from "horiz" array
4)save the "data" table with column indexes that follow the values from array "horiz"
I tried the following code, however, the saving part is not working properly and do not know how to delete zeros in 3) step (I read that something should be done with If condition and ReDim function)
Sub sample()
Dim DirArray As Variant
DirArray = Range("horiz").Value
Dim rng As Range
Set rng = Range("data")
Worksheets("Sheet1").Range("L1").Cells.Value = rng.Cells(, DirArray).Value
End Sub
Copy 'Selected' Columns
Option Explicit
Sub copySelectedColumns()
Dim srg As Range: Set srg = Range("horiz") ' Select Range
Dim cCount As Long: cCount = Application.CountIf(srg, ">0") ' Columns Count
Dim sData As Variant: sData = srg.Value ' Select Data (Array)
Dim Data As Variant: Data = Range("data").Value ' Data
Dim ColData As Variant: ReDim ColData(1 To cCount) ' Column Data (Array)
Dim n As Long, c As Long
For n = 1 To UBound(sData, 2)
If sData(1, n) > 0 Then
c = c + 1
ColData(c) = sData(1, n)
End If
Next n
Dim rCount As Long: rCount = UBound(Data, 1)
Dim Result As Variant: ReDim Result(1 To rCount, 1 To cCount) ' Result
Dim r As Long
For r = 1 To rCount
For c = 1 To cCount
Result(r, c) = Data(r, ColData(c))
Next c
Next r
Worksheets("Sheet1").Range("L1").Resize(rCount, cCount).Value = Result
End Sub
EDIT
The improvement is about not allowing impossible columns (greater than the number of columns in the Data Range (0 was previously included)) and clearing the contents of a previous result.
The small range study is about writing the addresses of the four ranges to the Immediate window (CTRL+G).
An Improvement feat. a Small Range Study
Sub copySelectedColumns()
Debug.Print "***** The Ranges *****"
Dim srg As Range: Set srg = Range("horiz") ' Select Range
Debug.Print "Select Range: " & srg.Address(0, 0)
Dim sData As Variant: sData = srg.Value ' Select Data (Array)
Dim sCount As Long: sCount = UBound(sData, 2) ' Select Columns Count
Dim drg As Range: Set drg = Range("data") ' Data Range
Debug.Print "Data Range: " & drg.Address(0, 0)
Dim Data As Variant: Data = drg.Value ' Data
Dim dCount As Long: dCount = UBound(Data, 2) ' Data Columns Count
Dim ColData As Variant: ReDim ColData(1 To sCount) ' Column Data (Array)
Dim n As Long, c As Long
For n = 1 To sCount
If sData(1, n) > 0 And sData(1, n) <= dCount Then
c = c + 1
ColData(c) = sData(1, n)
End If
Next n
If c > 0 Then
Dim cCount As Long: cCount = c
Dim rCount As Long: rCount = UBound(Data, 1)
Dim Result As Variant: ReDim Result(1 To rCount, 1 To cCount) ' Result
Dim r As Long
For r = 1 To rCount
For c = 1 To cCount
Result(r, c) = Data(r, ColData(c))
Next c
Next r
With Worksheets("Sheet1").Range("L2")
' Clear contents of previous result.
Dim crg As Range ' Clear Range
Set crg = .Resize(.Worksheet.Rows.Count - .Row + 1, sCount)
Debug.Print "Clear Range: " & crg.Address(0, 0)
crg.ClearContents
' Write result.
Dim rrg As Range: Set rrg = .Resize(rCount, cCount) ' Result Range
Debug.Print "Result Range: " & rrg.Address(0, 0)
rrg.Value = Result
End With
Else
' all values in Select Range are invalid
' (0 or greater than Data Columns Count (dCount))
Debug.Print "The Select Range '" & srg.Address(0, 0) & "' contains " _
& "only invalid data."
End If
End Sub
Try:
Sub cut_paste_delete()
Dim ArrayHeader As Variant
Dim ArrayData As Variant
Dim FinalArray As Variant
Dim i As Long
Dim ZZ As Long
Dim vColumn As Long
ArrayHeader = Range("horiz").Value
ArrayData = Range("data").Value
i = Application.WorksheetFunction.CountIf(Range("horiz"), "<>0") 'how many valid columns
ReDim FinalArray(1 To UBound(ArrayData), 1 To i) As Variant
For i = 1 To 5 Step 1
If ArrayHeader(1, i) <> 0 Then
vColumn = vColumn + 1
For ZZ = 1 To UBound(ArrayData) Step 1
FinalArray(ZZ, vColumn) = ArrayData(ZZ, i)
Next ZZ
End If
Next i
'paste final array somewhere, in my case in P1
Range(Cells(1, 16), Cells(1 + ZZ - 2, 16 + vColumn - 1)).Value = FinalArray
Erase ArrayHeader, ArrayData, FinalArray
End Sub
The output i get afcter executing code:
Another approach could be
Sub CopyRg()
Dim rgKeep As Range
Dim rgData As Range
Dim rgResult As Range
Set rgKeep = Range("B2").CurrentRegion
Set rgData = Range("D7").CurrentRegion
Dim i As Long
i = 1
Dim sngColumn As Range
For Each sngColumn In rgData.Columns
If rgKeep.Columns(i).Value <> 0 Then
If rgResult Is Nothing Then
Set rgResult = sngColumn
Else
Set rgResult = Union(rgResult, sngColumn)
End If
End If
i = i + 1
Next sngColumn
rgResult.Copy
Range("B12").PasteSpecial
End Sub
with the following data (input and output)
The code does not transfer the data into arrays which could be slow for large datasets but on the other hands it only loops through the columns.