VBA Macro Concatenation for Excel - excel

I have a requirement like I have some values in column A and I have multiple values in column B,C,D. If my column contains value X then I want to column header and column A value to be concatenate.
For example
I have gone through lots of question on Stack Overflow and I didn't found anything helpful.
Thanks for your help!

please try this code.
Sub FindValues(ByVal WhereToFind As Range, ByVal WhereToPaste As Range)
'where to find should have the header and values
Dim col As Integer 'loop through columns
Dim row As Integer 'loop through rows
Dim a() As Variant
Dim b() As Variant
Dim i As Integer
a() = WhereToFind
For row = 2 To UBound(a, 1)
For col = 2 To UBound(a, 2)
If a(row, col) = "x" Then
i = i + 1
ReDim Preserve b(1 To i)
b(i) = a(1, col) & "=" & a(row, 1)
End If
Next
Next
WhereToPaste.Resize(UBound(b)).Value = Application.Transpose(b())
End Sub
that should be called like
Sub caller()
FindValues ThisWorkbook.Sheets("Sheet1").Range("A1:E4"), ThisWorkbook.Sheets("Sheet1").Range("F1")
End Sub
the output is like

Related

Excel/VBA: How do I convert a single row into an array of the cell values

I thought this would be simple, but apparently the geniuses at Microsoft think differently.
I am writing Excel VBA to loop through the rows in an Excel worksheet and for each row (a Range object), I want to extract the cell values as an array. I've done some searching and found some supposed solutions that are not at all intuitive, nor do they work.
Does anyone have suggestions on how to do this?
Private Sub Separate_By_DC()
Dim row_ As Range
Dim row_str As String
For Each row_ In ActiveSheet.UsedRange.Rows
Dim arr As Variant
arr = Row_To_Array(row_)
Debug.Print UBound(arr) - LBound(arr) + 1
' row_str = Concat_Row(row_)
' Debug.Print row_str
Next row_
End Sub
Private Function Row_To_Array(row_ As Range) As Variant
Row_To_Array = row_.Value
End Function
Think the most easiest way would be:
Sub test()
Dim mArr As Variant
Dim i As Long
mArr = Application.Transpose(Range("A1:A10")) 'Transpoose to make the array one dimensional
'loop through the array
For i = LBound(mArr, 1) To UBound(mArr, 1) 'For one dimensional you can remove ",1"
'do something with mArr value
Cells(i, "C").Value = mArr(i) 'Print the array value
Next i
End Sub
For 2 dimensional array you can loop through it by state the dimensional index:
Dim mArr As Variant
Dim i As Long
mArr = Range("A1:A10") 'Use 2 dimension
'loop through the array
For i = LBound(mArr, 1) To UBound(mArr, 1)
'do something with mArr value
Cells(i, "C").Value = mArr(i, 1)
Next i

Unique values two columns combobox vba

I need to display two columns A and B listed in a combobox with unique values. So if two rows have the same A but not the same B, it is not a duplicate, both column need to be duplicate. I found a code that list one column (A) with unique values but I don't know how to add the column B.
There's a picture of my data and how I want to display it in my ComboBox.
Here's the code:
Private Sub UserForm_Initialize()
Dim Cell As Range
Dim col As Variant
Dim Descending As Boolean
Dim Entries As Collection
Dim Items As Variant
Dim index As Long
Dim j As Long
Dim RngBeg As Range
Dim RngEnd As Range
Dim row As Long
Dim Sorted As Boolean
Dim temp As Variant
Dim test As Variant
Dim Wks As Worksheet
Set Wks = ThisWorkbook.Worksheets("Sheet1")
Set RngBeg = Wks.Range("A3")
col = RngBeg.Column
Set RngEnd = Wks.Cells(Rows.Count, col).End(xlUp)
Set Entries = New Collection
ReDim Items(0)
For row = RngBeg.row To RngEnd.row
Set Cell = Wks.Cells(row, col)
On Error Resume Next
test = Entries(Cell.Text)
If Err = 5 Then
Entries.Add index, Cell.Text
Items(index) = Cell.Text
index = index + 1
ReDim Preserve Items(index)
End If
On Error GoTo 0
Next row
index = index - 1
Descending = False
ReDim Preserve Items(index)
Do
Sorted = True
For j = 0 To index - 1
If Descending Xor StrComp(Items(j), Items(j + 1), vbTextCompare) = 1 Then
temp = Items(j + 1)
Items(j + 1) = Items(j)
Items(j) = temp
Sorted = False
End If
Next j
index = index - 1
Loop Until Sorted Or index < 1
ComboBox1.List = Items
End Sub
Any clue? Thanks!
Try this code, please. It assumes that unique definition means pairs of values from the two columns, on the same row, to be unique:
Sub UnicTwoValInTwoColumns()
Dim sh As Worksheet, arr As Variant, arrFin As Variant, countD As Long
Dim lastRow As Long, i As Long, j As Long, k As Long, boolDupl As Boolean
Set sh = ActiveSheet 'use here your sheet
'supposing that last row in column A:A is the same in column B:B
'If not, the last row for B:B will be calculated and then the higher will be chosen:
lastRow = sh.Range("A" & Rows.Count).End(xlUp).Row
ReDim arrFin(1 To 2, 1 To lastRow) 'redim the final array for maximum possible number of elements
arr = sh.Range("A3:B" & lastRow).value 'pun in array the range to be analized
k = 1 'initialize the first array element number
For i = 1 To UBound(arr, 1) 'iterate between the array elements
boolDupl = False 'initialize the variable proving that the pair of data already in arrFin
For j = 1 To k 'iterate between the arrFin elements in order to check for duplicates
If arr(i, 1) & arr(i, 2) = arrFin(1, j) & arrFin(2, j) Then
boolDupl = True: Exit For 'if a duplicate is found the loop is exited
End If
Next j
If Not boolDupl Then 'load the arrFin only if a duplicate has not been found
arrFin(1, k) = arr(i, 1): arrFin(2, k) = arr(i, 2)
k = k + 1 'increment the (real) array number of elements
End If
Next
ReDim Preserve arrFin(1 To 2, 1 To k - 1) 'redim array at the real dimension (preserving values)
With Me.ComboBox1
.ColumnCount = 2 'be sure that combo has 2 columns to receive values
.List = WorksheetFunction.Transpose(arrFin) 'fill the combo with the array elements
End With
End Sub
You can paste the code in the form Initialize event, or let the Sub like it is, copy it in the form module and only call it from the event in discussion. I would suggest you to proceed in this las way. If you have (or will have) something else in the event, it would be simpler to identify a problem if it occurs, I think,

How do I make a function recursive

I have a huge set of data (almost 12k rows). I want t search column A for a keyword (Ex: name") and then move its corresponding value from column B to a new sheet. I have this working but can't figure out how to make it recursive so it looks at all 12k entries in column A. Please help.
See script below that works, but needs to be recursive
Sub Test()
With Sheets("original")
If .Range("A24").Value = "Name " Then
Sheets("new").Range("A1").Value = .Range("B24").Value
End If
End With
End Sub
you can just loop through the range of cells and use offset to get the value in column B to place in the new worksheet. It doesn't need to be recursive
Sub Test()
Dim c As Range
Dim iRow As Long
iRow = 1
For Each c In Sheets("original").Range("A:A")
If c.Value = "Name " Then
Sheets("new").Cells(iRow, 1).Value = c.Offset(0, 1).Value
'move to the next row
iRow = iRow + 1
End If
Next c
End Sub
Here bis an example using standard 2-D arrays. A dictionary is another array based option. An AutoFilter or Advanced Filter removes the need for arrays and/or iteration through the rows.
Note that this does not loop through 'all the rows in column A'. It stops looping when there are no more values in column B that could be returned.
Sub Test2()
'
'https://stackoverflow.com/questions/55928149
'
Dim i As Long, arr As Variant, bees As Variant
With Worksheets("original")
'collect source values
arr = .Range(.Cells(7, "A"), .Cells(.Rows.Count, "B").End(xlUp)).Value2
'prepare target array
ReDim bees(1 To 1, 1 To 1)
'loop through source value array and retain column B based on condition
For i = LBound(arr, 1) To UBound(arr, 1)
'case insensitive comparison
If LCase(arr(i, 1)) = LCase("Name ") Then
'assign column B value to target array
bees(1, UBound(bees, 2)) = arr(i, 2)
'make room for next matching value
ReDim Preserve bees(1 To 1, 1 To UBound(bees, 2) + 1)
End If
Next i
'trim off the last unused element of the target array
ReDim Preserve bees(1 To 1, 1 To UBound(bees, 2) - 1)
End With
'add new worksheet at end of worksheets queue
With Worksheets.Add(after:=Worksheets(Worksheets.Count))
'rename new worksheet
.Name = "bees"
'put target array in new worksheet starting at A2
.Cells(2, "A").Resize(UBound(bees, 2), UBound(bees, 1)) = _
Application.Transpose(bees)
End With
End Sub

Remove duplicates from column A based on existing values in column B using VBA

I need to input data in column A and column B and get the data that's in column A but not in column B written to column C.
Examples of what I need:
A slightly different and faster approach without looping through cells on the sheet would be this...
Private Sub CommandButton1_Click()
Dim x, y(), dict
Dim i As Long, j As Long
x = Range("A1").CurrentRegion
Set dict = CreateObject("Scripting.Dictionary")
Columns("C").ClearContents
For i = 1 To UBound(x, 1)
dict.Item(x(i, 2)) = ""
Next i
j = 1
For i = 1 To UBound(x, 1)
If Not dict.exists(x(i, 1)) Then
ReDim Preserve y(1 To j)
y(j) = x(i, 1)
j = j + 1
End If
Next i
Range("C1").Resize(UBound(y), 1) = Application.Transpose(y)
End Sub
Place this in the code file behind your sheet and change CommandButton1 to the name of your button.
Option Explicit
Private Sub CommandButton1_Click()
Dim r As Range, matched_ As Variant, counter_ As Long
'Loop in each cell in Column A
For Each r In Range("A1:A" & Cells(Rows.Count, 1).End(xlUp).Row)
If Not IsEmpty(r) Then
'Loop for a matching value in Column B
matched_ = Application.Match(r.Value, Columns(2), 0)
'If match not found, write the value in Column C
If IsError(matched_) Then
counter_ = counter_ + 1
Range("C" & counter_) = r.Value
End If
End If
Next r
End Sub

Match partial text string (90%) two column in two different sheet

I'm trying to match (90%) partial text string from a sheet column to another sheet column and bring end result to the master sheet column.
I found a VBA solution but I have some problems with that.
1) it's matching exact text
2) finding a problem to match two different sheet columns.
Please help me to sort this out.
Sub lookup()
Dim TotalRows As Long
Dim rng As Range
Dim i As Long
'Copy lookup values from sheet1 to sheet3
Sheets("BANK STATEMENT ENTRY").Select
TotalRows = ActiveSheet.UsedRange.Rows.Count
Range("F3:F" & TotalRows).Copy Destination:=Sheets("TEST").Range("A1")
'Go to the destination sheet
Sheets("TEST").Select
For i = 1 To TotalRows
'Search for the value on sheet2
Set rng = Sheets("INFO").UsedRange.Find(Cells(i, 1).Value)
'If it is found put its value on the destination sheet
If Not rng Is Nothing Then
Cells(i, 2).Value = rng.Value
End If
Next
End Sub
I have done a text mining project and I know you cannot use that approach, you have to break the strings into substrings and then analyze them. It will be a whole project, but you are lucky since I did it for you.
Let's simplify the problem and say that you have two ranges of strings and you want to find every similar strings between two groups. Also, you want to have a tolerance to minimize the matching pairs.
Assume ABCDE and 12BCD00. They have B, C, D, BC, CD and BCD in common. So the longest common substring is BCD which is 3 characters: 3/length of ABCDE(5) will be 60% similarity with the first string and 3/7=43% similarity. So if you can get a list of all those common substrings among all the strings in two ranges you can come up with a better list to filter and get what you want.
I wrote a bunch of functions. To use it easily, just copy and paste both groups of strings in one sheet and generate the final report on the same sheet too to understand how it works.
Function FuzzyFind, finds all of the common substrings and gives you 1st string from Group1/range1, 2nd string from group2/range2, common substring and percentages of similiarity for both strings. The good thing is you can tell the function how small you want your substrings e.g. in the previous example, if you say iMinCommonSubLength=3, it will only give you BCD, if you say iMinCommonSubLength=2 it will give you BC, CD and BCD and so on.
Use function Main. I also included a Test sub.
Functions:
Sub TestIt()
Call Main(ActiveSheet.Range("A1:A10"), ActiveSheet.Range("B1:B10"), 4, ActiveSheet.Range("D1"))
End Sub
Sub Main(rng1 As Range, rng2 As Range, iMinCommonSubLength As Integer, Optional rngReportUpperLeftCell As Range)
Dim arr() As Variant
Dim rngReport As Range
If rngReport Is Nothing Then Set rngReport = ActiveSheet.Range("A1")
arr = FuzzyFind(rng1, rng2, iMinCommonSubLength)
Set rngReport = rngReportUpperLeftCell.Resize(UBound(arr, 1), UBound(arr, 2))
rngReport.Value = arr
rngReport.Columns(1).NumberFormat = "#"
rngReport.Columns(2).NumberFormat = "#"
rngReport.Columns(3).NumberFormat = "#"
rngReport.Columns(4).NumberFormat = "0%"
rngReport.Columns(5).NumberFormat = "0%"
End Sub
Function GetCharacters(str As String) As Variant
Dim arr() As String
ReDim arr(Len(str) - 1)
For i = 1 To Len(str)
arr(i - 1) = Mid$(UCase(str), i, 1)
Next
GetCharacters = arr
End Function
Function GetIterations(iStringLength As Integer, iSubStringLength As Integer) As Integer
If iStringLength >= iSubStringLength Then
GetIterations = iStringLength - iSubStringLength + 1
Else
GetIterations = 0
End If
End Function
Function GetSubtrings(str As String, iSubLength As Integer) As Variant
Dim i As Integer
Dim count As Integer
Dim arr() As Variant
count = GetIterations(Len(str), iSubLength)
ReDim arr(1 To count)
For i = 1 To count
arr(i) = Mid(str, i, iSubLength)
Next i
GetSubtrings = arr()
End Function
Function GetLongestCommonSubStrings(str1 As String, str2 As String, iMinCommonSubLeng As Integer)
Dim i As Integer
Dim iLongestPossible As Integer
Dim iShortest As Integer
Dim arrSubs() As Variant
Dim arr1() As Variant
Dim arr2() As Variant
ReDim arrSubs(1 To 1)
'Longest possible common substring length is the smaller string's length
iLongestPossible = IIf(Len(str1) > Len(str2), Len(str2), Len(str1))
If iLongestPossible < iMinCommonSubLeng Then
'MsgBox "Minimum common substring length is larger than the shortest string." & _
' " You have to choose a smaller common length", , "Error"
Else
'We will try to find the first match of common substrings of two given strings, exit after the first match
For i = iLongestPossible To iMinCommonSubLeng Step -1
arr1 = GetSubtrings(str1, i)
arr2 = GetSubtrings(str2, i)
ReDim arrSubs(1 To 1)
arrSubs = GetCommonElement(arr1, arr2)
If arrSubs(1) <> "" Then Exit For 'if you want JUST THE LONGEST MATCH, comment out this line
Next i
End If
GetLongestCommonSubStrings = arrSubs
End Function
Function GetCommonElement(arr1() As Variant, arr2() As Variant) As Variant
Dim i As Integer
Dim j As Integer
Dim count As Integer
Dim arr() As Variant
count = 1
ReDim arr(1 To count)
For i = 1 To UBound(arr1)
For j = 1 To UBound(arr2)
If arr1(i) = arr2(j) Then
ReDim Preserve arr(1 To count)
arr(count) = arr1(i)
count = count + 1
End If
Next j
Next i
GetCommonElement = arr
End Function
Function FuzzyFind(rng1 As Range, rng2 As Range, iMinCommonSubLength As Integer) As Variant
Dim count As Integer
Dim i As Integer
Dim arrSubs As Variant
Dim str1 As String
Dim str2 As String
Dim cell1 As Range
Dim cell2 As Range
Dim rngReport As Range
Dim arr() As Variant 'array of all cells that are partially matching, str1, str2, common string, percentage
count = 1
ReDim arr(1 To 5, 1 To count)
For Each cell1 In rng1
str1 = UCase(CStr(cell1.Value))
If str1 <> "" Then
For Each cell2 In rng2
str2 = UCase(CStr(cell2.Value))
If str2 <> "" Then
ReDim arrSubs(1 To 1)
arrSubs = GetLongestCommonSubStrings(str1, str2, iMinCommonSubLength)
If arrSubs(1) <> "" Then
For i = 1 To UBound(arrSubs)
arr(1, count) = cell1.Value
arr(2, count) = cell2.Value
arr(3, count) = arrSubs(i)
arr(4, count) = Len(arrSubs(i)) / Len(str1)
arr(5, count) = Len(arrSubs(i)) / Len(str2)
count = count + 1
ReDim Preserve arr(1 To 5, 1 To count)
Next i
End If
End If
Next cell2
End If
Next cell1
FuzzyFind = TransposeArray(arr)
End Function
Function TransposeArray(arr As Variant) As Variant
Dim arrTemp() As Variant
ReDim arrTemp(LBound(arr, 2) To UBound(arr, 2), LBound(arr, 1) To UBound(arr, 1))
For a = LBound(arr, 2) To UBound(arr, 2)
For b = LBound(arr, 1) To UBound(arr, 1)
arrTemp(a, b) = arr(b, a)
Next b
Next a
TransposeArray = arrTemp
End Function
Don't forget to clear the sheet before generating new reports. Insert a table and use its autofilter to easily filter your stuff.
last but not least, don't forget to click on the check mark to announce this as the answer to your question.

Resources