Comma Delimitting values in a new created column based on a matching condition - vba

I have a value in column A on the transactions sheet which contains an Identifier for a Deal.
To be able to find out the customer information for this Deal I look in another sheet called Deal Information. Here there is a value in Column F which matches a value in Column A on the transactions sheet. Although on the Deal Information sheet it lists all the customers who are part of this deal as well as a unique identifier for each of the customers.
On the transactions sheet I have created a new column where by I want to display the list of ID's associated to a particular deal in comma delimited format if possible if not then a space will be good too.
transactions data:
Column A:ID Column: AA: BID Multiple
1 ?
2 ?
3 ?
4 ?
Roots data:
Column C: ID Column: D: BID
1 100
1 200
1 300
2 101
Expected Result in transaction table based on example.
Column A ID Column AA: BID Multiple
1 100,200,300
2 101
3 ?
4 ?
Sub test()
Dim wb As Workbook
Set wb = ThisWorkbook
Dim ws As Worksheet
Dim lastRow As Long
Set ws = ThisWorkbook.Worksheets("Roots")
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim valuesArr()
valuesArr = ws.Range("F2:G" & lastRow) ' 1 TO 4, 1 TO 2
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim valuesString As String
Dim currValue As Long
Dim currRotation As Long
Dim index As String
For currRotation = LBound(valuesArr, 1) To UBound(valuesArr, 1)
index = valuesArr(currRotation, 1)
currValue = CStr(valuesArr(currRotation, 2))
If Not dict.Exists(index) Then
dict.Add index, currValue
Else
dict(index) = dict(index) & ";" & currValue
End If
Next currRotation
Dim wsTarget As Worksheet
Dim lastRowTarget As Long
Set wsTarget = ThisWorkbook.Worksheets("transactions")
lastRow = wsTarget.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim valuesArr2()
valuesArr2 = wsTarget.Range("A2:AA" & lastRow)
Dim testValue As String
For currRotation = LBound(valuesArr2, 1) To UBound(valuesArr2, 1)
testValue = dict(CStr(valuesArr2(currRotation, 1)))
If testValue = vbNullString Then testValue = "?"
valuesArr2(currRotation, 27) = testValue
Next currRotation
wsTarget.Range("A2").Resize(UBound(valuesArr2, 1), UBound(valuesArr2,
27)) = valuesArr2
End Sub

This does an unordered, for the original posting . Assumes data starts in row 2 and has layout as shown below.
Column D being where the concatenated string is output.
*Please note repeated edits to the original question may mean code will no longer fit the stated requirements.
Option Explicit
Sub test()
Dim wb As Workbook
Set wb = ThisWorkbook
Dim ws As Worksheet
Dim lastRow As Long
Set ws = ThisWorkbook.Worksheets("Roots")
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim valuesArr()
valuesArr = ws.Range("A2:B" & lastRow) ' 1 TO 4, 1 TO 2
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim currValue As Long
Dim currRotation As Long
Dim index As String
For currRotation = LBound(valuesArr, 1) To UBound(valuesArr, 1)
index = valuesArr(currRotation, 1)
currValue = CStr(valuesArr(currRotation, 2))
If Not dict.exists(index) Then
dict.Add index, currValue
Else
dict(index) = dict(index) & ";" & currValue
End If
Next currRotation
Dim wsTarget As Worksheet
Dim lastRowTarget As Long
Set wsTarget = ThisWorkbook.Worksheets("transactions")
lastRow = wsTarget.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim valuesArr2()
valuesArr2 = wsTarget.Range("A2:D" & lastRow)
Dim testValue As String
For currRotation = LBound(valuesArr2, 1) To UBound(valuesArr2, 1)
testValue = dict(CStr(valuesArr2(currRotation, 1)))
If testValue = vbNullString Then testValue = "?"
valuesArr2(currRotation, 4) = testValue
Next currRotation
wsTarget.Range("A2").Resize(UBound(valuesArr2, 1), UBound(valuesArr2, 2)) = valuesArr2
End Sub

Edited as per OP's amended input and output columns
as per your examples, IDs are consecutive in Roots sheet
so you may go as follows
Sub main()
Dim cell As Range
With Worksheets("transactions") 'reference "transaction" sheet
For Each cell In .Range("A2", .Cells(.Rows.Count, "A").End(xlUp)) 'loop through referenced sheet column A cells from row 2 down to last not empty one
cell.Offset(, 26).value = GetIDDeals(cell.value) 'write in current cell offset 26 columns (i.e. column AA) the value of the BID
Next
End With
End Sub
Function GetIDDeals(ID As Variant) As String
With Worksheets("Roots")
With .Range("C1", .Cells(.Rows.Count, "C").End(xlUp)) 'reference its column C cells from row 1 (header) down to last not empty one
.AutoFilter Field:=1, Criteria1:=ID ' filter referenced cells on 1st column with passed ID content
Select Case Application.WorksheetFunction.Subtotal(103, .Columns(1)) 'let's see how many filtered cells
Case Is > 2 'if more than 2, then we have more than 1 filtered value, since header gets always filtered
GetIDDeals = Join(Application.Transpose(.Offset(1, 1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).value), ",")
Case 2 'if two filtered cells, then we have 1 filtered value, since header gets always filtered
GetIDDeals = .Offset(1, 1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).value
End Select
End With
.AutoFilterMode = False
End With
End Function

Related

VBA: How can i select the cell in a row which matches a variable's value?

I have 2 sheets. Sheet1 has 2 rows: column names and values.
Sheet 2 is a master sheet with all the possible column names in. I need to copy the values from sheet 1 into their appropriate column.
I think i can do this via a match function, and so far i have this:
Sub dynamic_paste()
Dim Columnname As String
Dim inputvalue As String
Dim starter As Integer
Dim i As Integer
starter = 0
For i = 1 To 4
'replace 4 with rangeused.rows.count?
Sheets("sheet1").Select
Range("a1").Select
ActiveCell.Offset(0, starter).Select
Columnname = ActiveCell
'sets columnname variable
ActiveCell.Offset(1, 0).Select
inputvalue = ActiveCell
'sets inputname variable
Sheets("sheet2").Select
'**Cells(0, WorksheetFunction.Match(Columnname, Rows(1), 0)).Select**
Range("a1").Offset(1, starter).Value = inputvalue
'inputs variable in the next cell along
starter = starter + 1
Next
End Sub
I need to find out how to use my columnname variable as the matching value, and then offset down to the first row that is empty - then change the value of that cell to the variable called inputvalue.
For extra points: I need to make sure the code doesnt break if they dont find a matching value, and if possible put any values that dont match into the end of the row?
What about this:
Dim LR As Long, X As Long, LC As Long, COL As Long
Dim RNG As Range, CL As Range
Option Explicit
Sub Test()
LR = Sheets(2).Cells.SpecialCells(xlCellTypeLastCell).Row 'Get last used row in your sheet
LC = Sheets(2).Cells(1, Sheets(2).Columns.Count).End(xlToLeft).Column 'Get last used column in your sheet
Set RNG = Sheets(2).Range(Sheets(2).Cells(1, 1), Sheets(2).Cells(1, LC))
'Loop through all the columns on your sheet with values
For X = 1 To Sheets(1).Cells(1, Sheets(1).Columns.Count).End(xlToLeft).Column
Set CL = RNG.Find(Sheets(1).Cells(1, X).Value, lookat:=xlWhole)
If Not CL Is Nothing Then
COL = CL.Column
Sheets(2).Cells(LR + 1, COL).Value = Sheets(1).Cells(2, X).Value 'Get the value on LR offset by 1
Else
Sheets(2).Cells(1, Sheets(2).Cells(1, Sheets(2).Columns.Count).End(xlToLeft).Column).Value = Sheets(1).Cells(1, X).Value
Sheets(2).Cells(LR + 1, Sheets(2).Cells(1, Sheets(2).Columns.Count).End(xlToLeft).Column).Value = Sheets(1).Cells(2, X).Value
End If
Next X
End Sub
This way you will avoid using select. Which is very recommandable!
This is Sheet1:
This is Sheet2:
This is the code:
Option Explicit
Sub DynamicPaste()
Dim col As Long
Dim wks1 As Worksheet: Set wks1 = Worksheets(1)
Dim wks2 As Worksheet: Set wks2 = Worksheets(2)
For col = 1 To 3
Dim currentRow As Long
currentRow = WorksheetFunction.Match(wks2.Cells(1, col), wks1.Columns(1))
wks2.Cells(2, col) = wks1.Cells(currentRow, 2)
Next col
End Sub
This is Sheet2 after the code:
This is a must-read - How to avoid using Select in Excel VBA

VBA. Replace a table cell content based on match from another table or delete entire row if match is not found

I am trying to make the following to work:
There are two tables in a separate worksheets. I want it to check each cell in worksheet2 column B and find a match from worksheet1 column A. If a match is found then replace the data in worksheet2 column B with a data from a matching row of worksheet1 column B.
If a match is not found from a worksheet1 column A then delete entire row in a worksheet2 column B.
Sub match_repl_del()
Dim r1 As Long, rfound, vfound
Dim w1, w2, v As Long
Set w1 = Sheets(3) ' data sheet
Set w2 = Sheets(2) ' target sheet
r1 = 2 'data starting from row 2
Do While Not IsEmpty(w1.Cells(r1, 1))
v = w1.Cells(r1, 1)
rfound = Application.Match(v, w2.Columns(2), 0) ' look for value
If Not IsError(rfound) Then ' found it?
vfound = w2.Cells(rfound, 2)
If w1.Cells(r1, 2) <> vfound Then ' if value does not match sheet1 column b
w2.Cells(rfound, 2) = w1.Cells(r1, 2) ' update based on origin sheet
lastC = w2.Cells(rfound, 1).End(xlToRight).Column
w2.Range(w2.Cells(rfound, 1), w2.Cells(rfound, lastC)).Interior.ColorIndex = 5
Else ' delete entire row on sheet2 if match is not found
w2.Rows(r1).EntireRow.Delete
End If
End If
r1 = r1 + 1
Loop
End Sub
Try this wat, it's work for me :
Option Explicit
Sub test()
' Active workbook
Dim wb As Workbook
Set wb = ThisWorkbook
Dim i As Long
Dim j As Long
'*******************************************
'Adapt this vars
'define your sheets
Dim ws_1 As Worksheet
Dim ws_2 As Worksheet
Set ws_1 = wb.Sheets("Sheet1") 'find a match in worksheet1 column A
Set ws_2 = wb.Sheets("sheet2") 'cell in worksheet2 column B
'definie the last Rows
Dim lastRow_ws1 As Long
Dim lastRow_ws2 As Long
lastRow_ws1 = ws_1.Range("A" & Rows.Count).End(xlUp).Row 'if you need, adjust column to find last row
lastRow_ws2 = ws_2.Range("B" & Rows.Count).End(xlUp).Row 'if you need, adjust column to find last row
'*******************************************
For i = lastRow_ws2 To 2 Step -1
For j = 1 To lastRow_ws1
Dim keySearch As String
Dim keyFind As String
keySearch = ws_2.Cells(i, 2).Value
keyFind = ws_1.Cells(j, 1).Value
If keySearch = keyFind Then
'MsgBox keySearch & " " & keyFind & " yes"
ws_2.Cells(i, 2).Value = ws_1.Cells(j, 2).Value
GoTo next_i
End If
Next j
ws_2.Rows(i).EntireRow.Delete
next_i:
Next i
End Sub

Getting cell value by looping through entire column

I would like to apply filter on a table based the values presented in cells A1:AG1. But the problem is when my data gets updated, sometimes i have values in other cells like AH,AI etc., The values available only on first row.
So i tried to add a loop for every cell, but it is not working. How to change my code to loop through every column in a single row. Help me
Dim ws As Worksheet
Dim str2 As Variant
Dim arr2() As String
Dim j As Long
Dim rng As Range
Set ws = Sheets("Main")
Set Tbl = Sheet2.ListObjects("DataTable")
Set rng = Range("A1:AG1") 'Need to change
j = 1
For Each cell In rng
str2 = cell.Value
ReDim Preserve arr2(j)
arr2(j) = str2
j = j + 1
Next cell
Tbl.Range.AutoFilter Field:=12, Criteria1:=arr2, Operator:=xlFilterValues
End sub
How about something like below:
Dim ws As Worksheet
Dim ws2 As Worksheet
Dim arr2() As String
Dim i As Long
Set ws = Sheets("Main")
Set ws2 = Sheets("Sheet2")
Set Tbl = ws2.ListObjects("DataTable")
LastCol = ws2.Cells(1, ws2.Columns.Count).End(xlToLeft).Column
'above will give you the last column number in row one of sheet ws2
ReDim Preserve arr2(1 To LastCol) 're-size the array
'Set rng = Range("A1:A" & LastCol) 'set your range from column 1 to last
For i = 1 To LastCol 'loop through columns
arr2(i) = ws2.Cells(1, i).Value 'add value to array
'above number 1 represents Row 1, and i will loop through columns
Next i
Tbl.Range.AutoFilter Field:=12, Criteria1:=arr2, Operator:=xlFilterValues
'above will filter table column 12 with array values?
I do not know if I understood your question well, but just replace:
Set rng = Range("A1:AG1")
With:
Set rng = ws.range(ws.cells(1 , 1) , ws.cells(1 , ws.Cells.Columns.Count).End(xlToLeft).Column

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.

copy a row of varied length, transpose it, and paste at the end of a column

I am working on a macro to copy a varied number of cells to a row, transpose and paste into a different sheet, in the next empty cell in a column. Then the idea is to match each transposed item with the ID from the row it originated from. The number of rows in the ID column will vary as well.
Looking at the example below, ID 1 is associated with Co D and Co R. Transposing would create the need for ID 1 to be copied into the two cells adjacent to the destination. This example I created has them on the same sheet, but for the code itself it will be on a different sheet.
The problem appears in copying the range to be transposed. I can't seem to figure out how to grab the whole row. The macro correctly pastes the value in the next available cell in the destination, but the version of the code I have now only copies the last result in the row, and not the whole row which is my intent. I haven't even gotten to the part of matching the ID to the Co in the Destination column, but I am dreading it already. The code I have is as follows;
Sub Testing()
Dim TearS As Worksheet: Set TearS = Worksheets(1)
Dim FeeS As Worksheet: Set FeeS = Worksheets(2)
Dim EntryS As Worksheet: Set EntryS = Worksheets(3)
Dim Stage2 As Worksheet: Set Stage2 = Worksheets(4)
Dim Stage3 As Worksheet: Set Stage3 = Worksheets(5)
Dim Bbg As Range: Set Bbg = EntryS.Range("F4:T199")
Dim TDest As Range: Set TDest = Stage2.Range("F5:T200")
Dim DateA As Range: Set DateA = Stage2.Range("G5:G200")
Dim DateB As Range: Set DateB = TearS.Range("E5:E200")
Dim DesA As Range: Set DesA = Stage2.Range("J5:J200")
Dim DesB As Range: Set DesB = TearS.Range("O5:O200")
Dim DesC As Range: Set DesC = Stage3.Range("C5:C200")
Dim CpnMatA As Range: Set CpnMatA = Stage2.Range("Y5:Y200")
Dim CpnMatB As Range: Set CpnMatB = TearS.Range("P5:P500")
Dim SettA As Range: Set SettA = Stage2.Range("I5:I200")
Dim SettB As Range: Set SettB = TearS.Range("Q5:Q200")
Dim MinA As Range: Set MinA = Stage2.Range("AA5:AA200")
Dim MinB As Range: Set MinB = Stage3.Range("D5:D200")
Dim MWOB As Range: Set MWOB = TearS.Range("N5:N200")
Dim Cel As Range
For Each Cel In DesC
If IsEmpty(Cel) = False Then
Cel.Offset(0, 1).End(xlToRight).Copy
TearS.Range("N3").End(xlDown).Offset(1).PasteSpecial Paste:=xlPasteAll, _
Operation:=xlNone, SkipBlanks:=False, Transpose:=True
End If
Next Cel
End Sub
Edit: Jeeped's solution that you can see in the answer below works swimmingly. Make sure that there are no errors in the source data, or you may get a run-time error 13.
Try transposing within a 2-D array before passing the values back to the worksheet.
Sub rewrite()
Dim lr As Long, a As Long, b As Long, val As Variant, vals As Variant
With Worksheets("sheet6")
.Range("F:G").Clear
lr = Application.Max(.Cells(.Rows.Count, "B").End(xlUp).Row, _
.Cells(.Rows.Count, "C").End(xlUp).Row, _
.Cells(.Rows.Count, "D").End(xlUp).Row, _
.Cells(.Rows.Count, "E").End(xlUp).Row)
vals = .Range(.Cells(2, "A"), .Cells(lr, "E")).Value2
For a = LBound(vals, 1) To UBound(vals, 1)
ReDim val(1 To UBound(vals, 2), 1 To 2)
For b = LBound(val, 1) To UBound(val, 1) - 1
If CBool(Len(vals(a, b + 1))) Then
val(b, 1) = vals(a, 1)
val(b, 2) = vals(a, b + 1)
End If
Next b
.Cells(.Rows.Count, "F").End(xlUp).Offset(1, 0).Resize(UBound(val, 1), UBound(val, 2)) = val
Next a
End With
End Sub