concatenating staggered columns - vba

I am working on a macro to loop through staggered columns of strings and concatenate them. Basically data gets added into the columns over time so I need to make it "future-proof". At the moment it picks up all of column H and I but only the first value of J and K. Not sure why it works for half but not the other. I am thinking its to do with my .End(xldown) as i am used to using rows.end(xlup).count to loop through things, but never when it is part of the range?
Here is my code:
Sub Concatenation_for_the_nation()
Range("H2").End(xlDown).Select
For i = 1 To ActiveCell.Row
Range("H" & i).Select
StrStrONE = StrStrONE & " " & Selection
Next i
Cells(1, 1).Select
Range("I2").End(xlDown).Select
For j = 1 To ActiveCell.Row
Range("I" & j).Select
StrStrTWO = StrStrTWO & " " & Selection
Next j
Cells(1, 1).Select
Range("J2").End(xlDown).Select
For k = 1 To ActiveCell.Row
Range("J" & k).Select
StrStrTHREE = StrStrTHREE & " " & Selection
Next k
Cells(1, 1).Select
Range("K2").End(xlDown).Select
For l = 1 To ActiveCell.Row
Range("K" & l).Select
StrStrFOUR = StrStrFOUR & " " & Selection
Next l
Cells(1, 1).Select
Cells(21, 21) = StrStrONE & StrStrTWO & StrStrTHREE & StrStrFOUR
Cells(20, 20) = "Jeff"
MsgBox "steve the pirate"
End Sub

You're repeating the same logic multiple times, so it makes sense to pull it out into a separate Sub.
Tested:
Sub Concatenation_for_the_nation()
Dim c As Range, s As String, sht As Worksheet
Set sht = ActiveSheet
For Each c In sht.Range("H1:K1")
s = s & ColumnString(c)
Next c
sht.Cells(21, 21).Value = s
sht.Cells(20, 20).Value = "Jeff"
MsgBox "arrrrr"
End Sub
Function ColumnString(cStart As Range, Optional sep As String = " ")
Dim rng As Range, c As Range, rv As String
Set rng = cStart.Parent.Range(cStart, _
cStart.Parent.Cells(Rows.Count, cStart.Column).End(xlUp))
For Each c In rng.Cells
rv = rv & sep & c.Value
Next c
ColumnString = rv
End Function

Related

vba to sort the data into matrix form

I have some data, for the first column date, it contains two dates.
Then I have the fund code and the categories, the last column is the categories value.
How shall I put them into matrix format, for example, the categories is horizontal and the value correspond to the fund name and categories and the date.
Following code should be helpful.
Option Explicit
Sub Demo()
With Application
.ScreenUpdating = False 'stop screen flickering
.Calculation = xlCalculationManual 'prevent calculation while execution
End With
Dim i As Long, lastrow As Long, tblLastRow As Long, tblLastColumn As Long
Dim dict As Object
Dim rng As Variant
Dim ws As Worksheet
Dim cel As Range, dateRng, fundCodeRng As Range, categoryRng As Range, valueRng As Range
Set dict = CreateObject("Scripting.Dictionary")
Set ws = ThisWorkbook.Worksheets("Sheet1") 'change Sheet1 to your worksheet
With ws
lastrow = .Range("A" & .Rows.Count).End(xlUp).Row 'get last row with data
'set ranges for date, fund code, category and value to be used later in code
Set dateRng = .Range("A2:A" & lastrow)
Set fundCodeRng = .Range("B2:B" & lastrow)
Set categoryRng = .Range("C2:C" & lastrow)
Set valueRng = .Range("D2:D" & lastrow)
'get unique records for date and fund coding combined together
For i = 2 To lastrow
dict(.Cells(i, 1).Value & "|" & .Cells(i, 2).Value) = dict(.Cells(i, 1).Value & "|" & .Cells(i, 2).Value)
Next
With .Range("F2").Resize(dict.Count) 'date and fund code will be displayed from cell F2
.Value = Application.Transpose(dict.Keys)
.TextToColumns Destination:=.Cells, DataType:=xlDelimited, Other:=True, OtherChar:="|"
.Offset(, 2).Resize(dict.Count).Value = Application.Transpose(dict.Items)
End With
'empty dictionary
dict.RemoveAll
Set dict = Nothing
Set dict = CreateObject("Scripting.Dictionary")
'get unique categories and display as header
rng = .Range("C1:C" & lastrow)
For i = 2 To UBound(rng)
dict(rng(i, 1) & "") = ""
Next
.Range("H1").Resize(1, UBound(dict.Keys()) + 1).Value = dict.Keys 'categories will be displayed from column H
tblLastRow = .Range("F" & Rows.Count).End(xlUp).Row 'get last row in new table
tblLastColumn = Cells(1, Columns.Count).End(xlToLeft).Column 'get last column of category in new table
'display corresponding values for date, fund code and category
For Each cel In .Range(.Cells(2, 8), .Cells(tblLastRow, tblLastColumn)) 'Cells(2, 8) represent Cell("H2")
cel.FormulaArray = "=IFERROR(INDEX(" & valueRng.Address & ",MATCH(1,(" & dateRng.Address & "=" & .Cells(cel.Row, 6) & ")*(" & fundCodeRng.Address & "=""" & .Cells(cel.Row, 7) & """)*(" & categoryRng.Address & "=""" & .Cells(1, cel.Column) & """),0)),"""")"
cel.Value = cel.Value
Next cel
End With
With Application
.ScreenUpdating = True
.Calculation = xlCalculationAutomatic
End With
End Sub
See image for reference.
EDIT :
If Fund Code could be numbers also then replace
cel.FormulaArray = "=IFERROR(INDEX(" & valueRng.Address & ",MATCH(1,(" & dateRng.Address & "=" & .Cells(cel.Row, 6) & ")*(" & fundCodeRng.Address & "=""" & .Cells(cel.Row, 7) & """)*(" & categoryRng.Address & "=""" & .Cells(1, cel.Column) & """),0)),"""")"
with
cel.FormulaArray = "=IFERROR(INDEX(" & valueRng.Address & ",MATCH(1,(" & dateRng.Address & "=" & .Cells(cel.Row, 6) & ")*(Text(" & fundCodeRng.Address & ",""0"")=""" & .Cells(cel.Row, 7) & """)*(" & categoryRng.Address & "=""" & .Cells(1, cel.Column) & """),0)),"""")"

Arrive Date Ranges VBA

First of all i'm just a beginner in VBA and I'm Stuck in the middle and couldn't find a possible way out. To be precise on my requirement, Attached below is the Snapshot of the data which i have currently. In the Date Range column i would need a date range based on the Dates available in each invoices. If a continuity breaks in the dates i would need the dates separated by comma which is shown in the sample data. Below is my piece of code which arrives only the dates and couldn't form a date range. Hope i can find my way out and would be earning something new out of this :-) Thanks!]1
Sub DD()
With Application
.ScreenUpdating = False
.DisplayAlerts = False
.EnableCancelKey = False
.EnableEvents = False
End With
Sheets("Claim Lines").Select
ActiveWorkbook.Worksheets("Claim Lines").Sort.SortFields.Clear
ActiveWorkbook.Worksheets("Claim Lines").Sort.SortFields.Add Key:=Range("B1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortTextAsNumbers
ActiveWorkbook.Worksheets("Claim Lines").Sort.SortFields.Add Key:=Range("D1"), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortTextAsNumbers
With ActiveWorkbook.Worksheets("Claim Lines").Sort
.SetRange ActiveSheet.UsedRange
.Header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
Range("B2").Select
Do
If ActiveCell.Value = ActiveCell.Offset(1, 0).Value Then
StrtRow = 2
tmperow = ActiveSheet.UsedRange.Rows.Count
For j = 0 To Application.WorksheetFunction.CountIf(Range("B" & StrtRow & ":B" & tmperow), ActiveCell.Value) - 1
If j = 0 Then
DOS = CDate(Cells(ActiveCell.Offset(0, 2).Row + j, "D").Value)
ElseIf j = Application.WorksheetFunction.CountIf(Range("B" & StrtRow & ":B" & tmperow), ActiveCell.Value) - 1 Then
ElseIf DOS = DOS Then
DOS = CDate(Cells(ActiveCell.Offset(0, 2).Row + j, "D").Value)
ElseIf j = Application.WorksheetFunction.CountIf(Range("B" & StrtRow & ":B" & tmperow), ActiveCell.Value) - 1 Then
ElseIf DOS = DOS Then
DOS = DOS & " & " & CDate(Cells(ActiveCell.Offset(0, 2).Row + j, "D").Value)
Else
DOS = DOS & ", " & CDate(Cells(ActiveCell.Row + j, "D").Value)
End If
Next
Range("N" & ActiveCell.Row).Value = DOS & " to " & DOS
DOS = ""
Else
Range("N" & ActiveCell.Row).Value = Range("D" & ActiveCell.Row).Value
End If
ActiveCell.Offset(1, 0).Select
Loop Until IsEmpty(ActiveCell.Value)
End Sub
I quickly wrote this. I am sure there can be better ways to achieve this but I could only spend this much time before I hit the sack :)
Sub Sample()
Dim ws As Worksheet
Dim dString As String, ss As String
Dim lRow As Long, i As Long
Dim sRow As Long, eRow As Long
Dim sDate As Date, eDate As Date
'~~> This is your worksheet which has data
Set ws = ThisWorkbook.Worksheets("Claim Lines")
'~~> Setting start row and end row for Col C
sRow = 2: eRow = 2
With ws
'~~> Sort Col A and B on Col A first and then on Col B
.Columns("A:B").Sort Key1:=.Range("A1"), Key2:=.Range("B1"), _
Order1:=xlAscending, Header:=xlYes, OrderCustom:=1, MatchCase:=False, _
Orientation:=xlTopToBottom, DataOption1:=xlSortNormal
'~~> Find Last Row of Col A
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Set the Initial Start Date and End Date
sDate = .Range("B2").Value: eDate = .Range("B2").Value
'~~> Loop through the data
For i = 2 To lRow
'~~> Check if the value of the current cell in Col A
'~~> is the same as the value in the next cell
If .Range("A" & i) = .Range("A" & i + 1) Then
'~~> Compare date values in Col B to check if they are in sequence
If .Range("B" & i + 1) - .Range("B" & i) = 1 Then
'~~> If yes then set it as new End Date
eDate = .Range("B" & i + 1)
Else
'~~> Get the string to be written in Col C
dString = GetDString(dString, sDate, eDate, .Range("B" & i))
'~~> Set New Start Date
sDate = .Range("B" & i + 1)
End If
Else
eRow = i
dString = GetDString(dString, sDate, eDate, .Range("B" & i))
.Range("C" & sRow & ":C" & eRow).Value = dString
dString = "": sRow = eRow + 1
sDate = .Range("B" & i + 1).Value
eDate = .Range("B" & i + 1).Value
End If
Next i
End With
End Sub
'~~> Function to get the string to be written in Col C
Private Function GetDString(s As String, StartDate As Date, _
endDate As Date, CurCell As Range) As String
If s = "" Then
If endDate = CurCell.Value Then
If StartDate = endDate Then
s = StartDate
Else
s = StartDate & "-" & endDate
End If
Else
s = (StartDate & "-" & endDate) & "," & CurCell.Value
End If
Else
If endDate = CurCell.Value Then
s = s & "," & StartDate & "-" & endDate
Else
s = s & "," & CurCell.Value
End If
End If
GetDString = s
End Function
ScreenShot of various tests

Excel VBA To Add New Row If Condition Is Met

I am attempting to write some VBA that will accomplish
if row O is not null then copy all data to new row, then in current row clear columns I, J, K, L, M, N
in the newly inserted row clear columns O
The caveat I am not sure to account for is - throws a
Type mismatch error
Here is the syntax that I am trying to work with
Sub BlueBell()
Application.ScreenUpdating = False
Dim i As Long, y
ReDim y(2 To Range("A" & Rows.Count).End(3).Row)
For i = UBound(y) To LBound(y) Step -1
If Cells(i, "O") Then
If Cells(i, "I") = "" And Cells(i, "K") = "" And Cells(i, "M") = "" Then
GoTo DoNothing
Else
Rows(i).Copy
Cells(i, "A").Insert
Range("I" & i & ":J" & i & ":K" & i & ":L" & i & ":M" & i & ":N" & i & ":O" & i + 1).ClearContents
GoTo DoNothing
End If
End If
DoNothing:
Next i
End Sub
Apart from your error with using a string as a boolean expression, there are several things that can be changed in your code:
Sub BlueBell()
Application.ScreenUpdating = False
Dim i As Long ', y() As Variant
'ReDim y(2 To Range("A" & Rows.Count).End(3).Row) 'Why use an array?
For i = Range("A" & Rows.Count).End(3).Row To 2 Step -1
If Not IsEmpty(Cells(i, "O").Value) Then
'Avoid the use of GoTo
If Cells(i, "I").Value <> "" Or _
Cells(i, "K").Value <> "" Or _
Cells(i, "M").Value <> "" Then
Rows(i).Copy
Cells(i, "A").Insert
'Don't use a "Ix:Jx:Kx:Lx:Mx:Nx:Ox+1" range - it will lead to problems
'because even really experienced users don't understand what it does
Range("I" & i & ":N" & i).ClearContents
Range("O" & i + 1).ClearContents
End If
End If
Next i
'It's a good habit to reset anything that you disabled at the start of your code
Application.ScreenUpdating = True
End Sub

Ignore string if empty in concatenation formula

I have a formula which concatenates strings in different columns. It works great when there is data in each of the columns but if one column is blank I get an error "invalid procedure call or argument" for the string formed by the empty column. Is there a clause i can add into my code to ignore the string if it is empty?
Sub Concatenation_for_the_nation()
'Range("H2").End(xlDown).Select
Cells(rows.Count, "H").End(xlUp).Select
For i = 1 To ActiveCell.Row
Range("H" & i).Select
StrStrONE = StrStrONE & "" & Selection
Next i
Cells(1, 1).Select
'Range("I2").End(xlDown).Select
Cells(rows.Count, "I").End(xlUp).Select
For j = 1 To ActiveCell.Row
Range("I" & j).Select
StrStrTWO = StrStrTWO & "" & Selection
Next j
Cells(1, 1).Select
'Range("J2").End(xlDown).Select
Cells(rows.Count, "J").End(xlUp).Select
For k = 1 To ActiveCell.Row
Range("J" & k).Select
StrStrTHREE = StrStrTHREE & "" & Selection
Next k
Cells(1, 1).Select
'Range("K2").End(xlDown).Select
Cells(rows.Count, "K").End(xlUp).Select
For l = 1 To ActiveCell.Row
Range("K" & l).Select
StrStrFOUR = StrStrFOUR & "" & Selection
Next l
Cells(1, 1).Select
StrStrONE = Trim(StrStrONE)
StrStrTWO = Trim(StrStrTWO)
StrStrTHREE = Trim(StrStrTHREE)
StrStrTHREE = Left(StrStrTHREE, Len(StrStrTHREE) - 3)
StrStrFOUR = Trim(StrStrFOUR)
StrStrFOUR = Left(StrStrFOUR, Len(StrStrFOUR) - 3)
Cells(14, 7) = "(ISAV(" & StrStrONE & " " & StrStrTWO & " " & StrStrTHREE & ")=1 OR (" & StrStrFOUR & ")=1)=1"
Cells(14, 7).Select
End Sub
You can check if the columns are not empty by using ISBLANK() function
As user2471313 said used ISBLANK() function or I would add something like this for checking the string:
If StrStrONE<>"" and StrStrTWO<>"" and StrStrTHREE<>"" and StrStrFOUR<>"" then
StrStrONE = Trim(StrStrONE)
''''your code until end
End if

Find matching cell with different strings inside one cell

My goal of my macro:
I have 2 sheets, sheet1 master report and sheet2 import Input.
In column A of both sheets I have several strings in one cell.
I would like to see if there is a match and if there is the match the row from sheet2 (from column B) will be copied and paste in the row corresponding in sheet1.
This part of my code is done.
But now it starts to be tricky: If there is new string in the same cell as the matching string so I would like to add them as well in the cell of the column A sheet1.
For instance:
Sheet1 Column A Cell34:
MDM-9086
Sheet2 Column A Cell1:
MDM-9086,MDM-12345
After the macro it would be like this:
Sheet1 Column A cell34:
MDM-9086,MDM-12345
If there is no match between column A of both sheets so I would like to copy the entire row of the sheet2 and past it in the last free row of the sheet1.
See my code:
Sub MDMNumbers()
Dim LastRw1 As Long, LastRw2 As Long, NxtRw As Long
Dim I As Integer
Dim m As Range
Dim Tb
LastRw1 = Sheets(1).Range("A" & Rows.Count).End(xlUp).Row
LastRw2 = Sheets(2).Range("A" & Rows.Count).End(xlUp).Row
With Worksheets(2)
LastRw2 = .Range("A" & Rows.Count).End(xlUp).Row
For NxtRw = 2 To LastRw2
Tb = Split(.Range("A" & NxtRw), ",")
For I = 0 To UBound(Tb)
With Sheets(1).Range("A2:A" & LastRw1)
Set m = .Find(Trim(Tb(I)), LookAt:=xlPart)
If Not m Is Nothing Then
Sheets(2).Range("B" & NxtRw & ":Q" & NxtRw).Copy _
Sheets(1).Range("B" & m.Row)
Set m = Nothing
End If
End With
Next I
Next NxtRw
End With
End Sub
Example:
Sheet 1, Column A (start row 2)
MDM-123,MDM-27827
MDM-1791728,MDM-124
MDM-125
MDM-126,MDM-28920
MDM-127,MDM-1008
""
Sheet 2, Column A (start row 2)
MDM-123,MDM-27272
MDM-124
MDM-125,MDM-1289
MDM-126
MDM-1008
MDM-127
MDM-172891
Result on Sheet 1, Column A (start row 2):
MDM-123,MDM-27827,MDM-27272
MDM-124,MDM-1791728
MDM-125,MDM-1289
MDM-126,MDM-28920
MDM-127,MDM-1008
MDM-1008
MDM-172891
For your # 2.
Option Explicit
Public Sub MDMNumbers()
Dim LastRw1 As Long, LastRw2 As Long, NxtRw As Long, rng1 As Range, rng2 As Range
Dim i As Long, m As Range, tb() As String, celVal As String, notFound As Boolean
Dim additions1 As String, additions2 As String
LastRw1 = Worksheets(1).Range("A" & Worksheets(1).Rows.Count).End(xlUp).Row + 1
LastRw2 = Worksheets(2).Range("A" & Worksheets(2).Rows.Count).End(xlUp).Row
notFound = True
For NxtRw = 2 To LastRw2
celVal = Worksheets(2).Range("A" & NxtRw).Value2
If Len(celVal) > 0 Then
tb = Split(celVal, ",")
For i = 0 To UBound(tb)
Set m = Worksheets(1).Columns(1).Find(Trim(tb(i)), LookAt:=xlPart)
If Not m Is Nothing And notFound Then
Set rng1 = Worksheets(2).Range("B" & NxtRw & ":Q" & NxtRw)
Set rng2 = Worksheets(1).Range("B" & m.Row & ":Q" & m.Row)
rng1.Copy rng2
With Worksheets(2).Range("A" & NxtRw)
additions1 = Replace(.Value2, "," & tb(i), vbNullString)
additions1 = Replace(additions1, tb(i) & ",", vbNullString)
additions1 = Replace(additions1, tb(i), vbNullString)
End With
With Worksheets(1).Range("A" & m.Row)
additions2 = Replace(.Value2, "," & tb(i), vbNullString)
additions2 = Replace(additions2, tb(i) & ",", vbNullString)
additions2 = Replace(additions2, tb(i), vbNullString)
If Len(additions2) > 0 Then
If Len(additions1) > 0 Then
.Value2 = tb(i) & "," & additions2 & "," & additions1
Else
.Value2 = tb(i) & "," & additions2
End If
Else
.Value2 = tb(i) & "," & additions1
End If
End With
Set m = Nothing
notFound = False
End If
Next
If notFound Then
Set rng1 = Worksheets(2).Range("A" & NxtRw & ":Q" & NxtRw)
Set rng2 = Worksheets(1).Range("A" & LastRw1 & ":Q" & LastRw1)
rng1.Copy rng2
LastRw1 = LastRw1 + 1
End If
notFound = True
End If
Next
End Sub
It should work as expected now
Test data and result:
Why don't you copy the whole row from sheet2 to sheet1 like
For NxtRw = 2 To LastRw2
...
Sheets(2).Range("A" & NxtRw & ":Q" & NxtRw).Copy _
Sheets(1).Range("A" & m.Row)
...
Next NxtRw
? (The rest of the loop should stay the same.)