Merge values of column B based on common values on column A - excel

In Excel, how I can merge values of Column B based on common values on Column A?
Basically what I need is some thing like this

You can use this UDF:
Function TEXTJOINIFS(rng As Range, delim As String, ParamArray arr() As Variant)
Dim rngarr As Variant
rngarr = Intersect(rng, rng.Parent.UsedRange).Value
Dim condArr() As Boolean
ReDim condArr(1 To Intersect(rng, rng.Parent.UsedRange).Rows.Count) As Boolean
Dim i As Long
For i = LBound(arr) To UBound(arr) Step 2
Dim colArr() As Variant
colArr = Intersect(arr(i), arr(i).Parent.UsedRange).Value
Dim j As Long
For j = LBound(colArr, 1) To UBound(colArr, 1)
If Not condArr(j) Then
Dim charind As Long
charind = Application.Max(InStr(arr(i + 1), ">"), InStr(arr(i + 1), "<"), InStr(arr(i + 1), "="))
Dim opprnd As String
If charind = 0 Then
opprnd = "="
Else
opprnd = Left(arr(i + 1), charind)
End If
Dim t As String
t = """" & colArr(j, 1) & """" & opprnd & """" & Mid(arr(i + 1), charind + 1) & """"
If Not Application.Evaluate(t) Then condArr(j) = True
End If
Next j
Next i
For i = LBound(rngarr, 1) To UBound(rngarr, 1)
If Not condArr(i) Then
TEXTJOINIFS = TEXTJOINIFS & rngarr(i, 1) & delim
End If
Next i
TEXTJOINIFS = Left(TEXTJOINIFS, Len(TEXTJOINIFS) - Len(delim))
End Function
You would call it like this:
=IF(MATCH(A1,A:A,0)=ROW(A1),TEXTJOINIFS(B:B,", ",A:A,A1),"")
Now it does not matter if the data is sorted or not it will only put the output in column C where the value in Column A first appears.

Use an array formula:
=TEXTJOIN(", ",TRUE,IF(A$1:A$15=A1,B$1:B$15,""))
(Use CTRL-SHIFT-ENTER instead of ENTER to enter the formula)

Here is my untested code of course.
The code below uses 2 loops to add the information.
dim X as integer
dim X2 as integer
dim match as string
X = 1
do while sheets("sheet1").range("A" & X).value <> ""
sheets("sheet1").range("C" & X).value = sheets("sheet1").range("B" & X).value
match = sheets("sheet1").range("A" & X).value
X2 = X + 1
do while sheets("sheet1").range("A" & X2).value = match
sheets("sheet1").range("C" & X).value = sheets("sheet1").range("C" & X).value + ", " + sheets("sheet1").range("B" & X2).value
X2 = X2 + 1
loop
X = X2
X = X + 1
Loop

Related

Convert date list into date ranges [duplicate]

I have a data like this :
A049
A050
A051
A053
A054
A055
A056
A062
A064
A065
A066
And I want the output like :
As you can see, I want the ranges which are in consecutive order
I am trying some thing like this:
Private Sub CommandButton1_Click()
Set wb = ThisWorkbook
lastRow = wb.Sheets("Sheet1").Range("A" & wb.Sheets("Sheet1").Rows.Count).End(xlUp).Row
For i = 2 To lastRow
r = wb.Sheets("Sheet1").Range("A" & i).Value
If wb.Sheets("Sheet1").Range("A" & i).Value = wb.Sheets("Sheet1").Range("A" & i+1).Value
Next i
End Sub
But not helping me
Am feeling charitable so have tried some code which should work. It assumes your starting values are in A1 down and puts results in C1 down.
Sub x()
Dim v1, v2(), i As Long, j As Long
v1 = Range("A1", Range("A" & Rows.Count).End(xlUp)).Value
ReDim v2(1 To UBound(v1, 1), 1 To 2)
For i = LBound(v1, 1) To UBound(v1, 1)
j = j + 1
v2(j, 1) = v1(i, 1)
If i <> UBound(v1, 1) Then
Do While Val(Right(v1(i + 1, 1), 3)) = Val(Right(v1(i, 1), 3)) + 1
i = i + 1
If i = UBound(v1, 1) Then
v2(j, 2) = v1(i, 1)
Exit Do
End If
Loop
End If
If v1(i, 1) <> v2(j, 1) Then v2(j, 2) = v1(i, 1)
Next i
Range("C1").Resize(j, 2) = v2
End Sub
Try the below code
Private Sub CommandButton1_Click()
Set wb = ThisWorkbook
lastRow = wb.Sheets("Sheet1").Range("A" & wb.Sheets("Sheet1").Rows.Count).End(xlUp).Row
Dim lastNum, Binsert As Integer
Dim firstCell, lastCell, currentCell As String
Binsert = 1
lastNum = getNum(wb.Sheets("Sheet1").Range("A1").Value)
firstCell = wb.Sheets("Sheet1").Range("A1").Value
For i = 2 To lastRow
activeNum = getNum(wb.Sheets("Sheet1").Range("A" & i).Value)
currentCell = wb.Sheets("Sheet1").Range("A" & i).Value
If (activeNum - lastNum) = 1 Then
'nothing
Else
lastCell = wb.Sheets("Sheet1").Range("A" & (i - 1)).Value
wb.Sheets("Sheet1").Range("B" & Binsert).FormulaR1C1() = firstCell
If (firstCell <> lastCell) Then
wb.Sheets("Sheet1").Range("C" & Binsert).FormulaR1C1() = lastCell
End If
Binsert = Binsert + 1
firstCell = wb.Sheets("Sheet1").Range("A" & i).Value
End If
lastNum = activeNum
Next i
'last entry
wb.Sheets("Sheet1").Range("B" & Binsert).FormulaR1C1() = firstCell
If (firstCell <> currentCell) Then
wb.Sheets("Sheet1").Range("C" & Binsert).FormulaR1C1() = currentCell
End If
End Sub
Public Function getNum(ByVal num As String) As Integer
getNum = Val(Mid(num, 2))
End Function
Another solution. It loops backwards from last row to first row.
Option Explicit
Public Sub FindConsecutiveValues()
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim lRow As Long 'find last row
lRow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
Dim lVal As String 'remember last value (stop value)
lVal = ws.Range("A" & lRow).Value
Const fRow As Long = 2 'define first data row
Dim i As Long
For i = lRow To fRow Step -1 'loop from last row to first row backwards
Dim iVal As Long
iVal = Val(Right(ws.Range("A" & i).Value, Len(ws.Range("A" & i).Value) - 1)) 'get value of row i without A so we can calculate
Dim bVal As Long
bVal = 0 'reset value
If i <> fRow Then 'if we are on the first row there is no value before
bVal = Val(Right(ws.Range("A" & i - 1).Value, Len(ws.Range("A" & i - 1).Value) - 1)) 'get value of row i-1 without A
End If
If iVal - 1 = bVal Then
ws.Rows(i).Delete 'delete current row
Else
If lVal <> ws.Range("A" & i).Value Then 'if start and stop value are not the same …
ws.Range("B" & i).Value = lVal 'write stop value in column B
End If
lVal = ws.Range("A" & i - 1).Value 'remember now stop value
End If
Next i
End Sub

Excel VBA inconsistent behaviour while trying to implement a tree view

Sub myfunction()
Dim convert_i, convert_k As String
Dim i, j, k, l As Long
For i = 2 To 583
For k = i + 1 To 583
j = InStr(Range("F" & k).Text, Range("F" & i).Text)
If j > 0 Then
l = InStr(Range("F" & k).Text, " \ ")
If l > 1 Then
convert_i = Range("F" & i).Text & ""
convert_k = Range("F" & k).Text & ""
pos = InStrRev(convert_k, convert_i) - 1
Range("F" & k).Value = Right(convert_k, Len(convert_i) - pos)
Range("F" & k).Value = Range("F" & i).Text + Range("F" & k).Text
Else:
Range("F" & k).Value = Range("F" & i).Value + " \ " + Range("F" & k).Value
End If
End If
Next k
Next i
MsgBox ("Finished ")
End Sub
The code works for the most part however it's inconsistent and I'm baffled as to why. The desired result is like
CP \ CP01 \ CP0103
And through the document I think like at least a good 70 percent is of this format but I do not have the time to go trough remaining 30 percent manually. I would very much appreciate any help.
Please check images below:
The expected result:
enter image description here
Create a Tree
Adjust the values in the constants section.
Option Explicit
Sub createTree()
Const wsName As String = "Sheet1"
Const First As String = "F2"
Const len1 As Long = 2
Const len2 As Long = 4
Const Sep As String = " \ "
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim rg As Range
With wb.Worksheets(wsName).Range(First)
Dim lCell As Range
Set lCell = .Resize(.Worksheet.Rows.Count - .Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If lCell Is Nothing Then Exit Sub
Set rg = .Resize(lCell.Row - .Row + 1)
End With
Dim Data As Variant: Data = rg.Value ' assuming there is data in F3 at least
Dim sLen As Long: sLen = Len(Sep)
Dim tLen As Long: tLen = len1 + sLen + len2
Dim cString As String
Dim cPref1 As String
Dim cPref2 As String
Dim r As Long
For r = 1 To UBound(Data, 1)
cString = Trim(Data(r, 1))
Select Case Len(cString)
Case len1
cPref1 = cString
cPref2 = ""
Data(r, 1) = cString
Case len2
cPref2 = cPref1 & Sep & cString
Data(r, 1) = cPref2
Case Else
If Len(cPref2) = tLen Then
cPref2 = cPref2 & Sep & cString
Else
cPref2 = Left(cPref2, tLen) & Sep & cString
End If
Data(r, 1) = cPref2
End Select
Next r
rg.Value = Data
End Sub
Sub myfunction()
Dim convert_i, convert_k, last_part As String
Dim i, j, k, l As Long
For i = 2 To 583
For k = i + 1 To 583
j = InStr(Range("F" & k).Text, Range("F" & i).Text)
If j > 0 Then
l = InStrRev(Range("F" & k).Text, " \ ")
If l > 0 Then
convert_i = Range("F" & i).Value
convert_k = Range("F" & k).Value
last_part = Right(convert_k, Len(convert_k) - l - 2)
Range("F" & k).Value = Range("F" & i).Text & " \ " & last_part
Else:
Range("F" & k).Value = Range("F" & i).Value & " \ " & Range("F" & k).Value
End If
End If
Next k
Next i
MsgBox ("Finished ")
End Sub
I realised my implementation was terrible, I was confused by getting it almost 70 percent right. The above code got the job done. Might as well delete the question as I don't think it would be of help to anybody.

loop on the rows in excel vba

I need help in Excel. My question is: How can I get the first row of each row in this loop and print the output.
Input Column and Row value is like this:
col1 col2 col3
1 test abc
2 tests dfg
3 gtd gdd
Output like this.
(col1,col2,col3)('1','test','abc');
(col1,col2,col3)('2','tests','dfg');
(col1,col2,col3)('3','gtd','gdd');
The Code that I am working on is
For i = 1 To LastRow
For j = 3 To LastCol
If IsNumeric(Cells(i, j)) & Cells(i, j) > 0 = True Then
vaString = vaString & Cells(i, j)
End If
If j <> LastCol Then vaString = vaString & ","
If j = LastCol Then vaString = vaString
Next j
myString = myString
Next i
Thanks in advance
From your parameters I will assume your data starts from cell C1. Change the first few lines if otherwise.
Sub Testing()
Dim FirstRow As Integer, FirstCol As Integer, LastRow As Integer, LastCol As Integer
FirstRow = 1
FirstCol = 3
LastRow = 4
LastCol = 5
Dim arrStr() As String
Dim strFirstRow As String
Dim strPath As String
strPath = "C:\..." ' Path of your choice
Open strPath For Append As #1
ReDim arrStr(FirstCol To LastCol)
For j = FirstCol To LastCol
arrStr(j) = CStr(Cells(FirstRow, j))
Next j
strFirstRow = "(" & Join(arrStr, ",") & ")"
For i = FirstRow + 1 To LastRow
If IsNumeric(Cells(i, FirstCol).Value) Then
If Cells(i, FirstCol).Value > 0 Then
ReDim arrStr(FirstCol To LastCol)
For j = FirstCol To LastCol
arrStr(j) = "'" & CStr(Cells(i, j)) & "'"
Next j
Debug.Print strFirstRow & "(" & Join(arrStr, ",") & ");"
Print #1, strFirstRow & "(" & Join(arrStr, ",") & ");"
End If
End If
Next i
Close #1
End Sub
Here is a rather simple example making use of array variables:
Sub Test()
Dim x As Long
Dim str1 As String, str2 As String
Dim arr1() As Variant, arr2() As Variant
With ThisWorkbook.Sheets("Sheet1") 'Change according to your sheetname
arr1 = Application.Transpose(Application.Transpose(.Range("A1:C1").Value2))
arr2 = .Range("A2:C" & .Cells(.Rows.Count, "A").End(xlUp).Row).Value2
str1 = "(" & Join(arr1, ",") & ")"
For x = LBound(arr2) To UBound(arr2)
str2 = "(" & Join(Array(arr2(x, 1), arr2(x, 2), arr2(x, 3)), ",") & ")" & ";"
Debug.Print str1 & str2 'Do something with the full string
Next x
End With
End Sub

VBA concatenate two ranges

I'm trying to concatenate two ranges in excel using VBA so each cell in range1 concatenates all cells in range2 until cell A is null. Please see below:
Range1(column A): Range2(column B):
50703, 50702 52797, 52848
Concatenate(column C):
50703-52797, 50703-52848, 50702-52797, 50702-52848
This will insert all the combinations of values in column A and B into column C and concatenate them with a hyphen:
Sub combinations()
Dim i As Long, j As Long, n As Long
Dim valsColA As Variant, valsColB As Variant
With ThisWorkbook.Sheets("Combinations") ' change sheet name, if necessary
valsColA = .Range(.Cells(1, 1), .Range("A1").End(xlDown)).Value
valsColB = .Range(.Cells(1, 2), .Range("B1").End(xlDown)).Value
For i = LBound(valsColA) To UBound(valsColA)
For j = LBound(valsColB) To UBound(valsColB)
n = n + 1
.Cells(n, 3).Value = valsColA(i, 1) & "-" & valsColB(j, 1)
Next j
Next i
End With
End Sub
Here is what I came up with, although #Miqi180 got there first:
Sub ABPerm()
Dim Acol As Integer
Dim Bcol As Integer
Dim RowNumA As Integer
Dim RowNumB As Integer
Dim RowNumC As Integer
Acol = Range("A" & Rows.Count).End(xlUp).Row
Bcol = Range("B" & Rows.Count).End(xlUp).Row
RowNumA = 1
RowNumB = 1
RowNumC = 1
For a = 1 To Acol
For b = 1 To Bcol
Range("C" & RowNumC).Value = Range("A" & RowNumA).Value & "-" & Range("B" & RowNumB).Value
RowNumB = RowNumB + 1
RowNumC = RowNumC + 1
Next b
RowNumB = 1
RowNumA = RowNumA + 1
Next a
End Sub
Try this code
Sub Test()
Dim rng As Range
Set rng = Range("A1:A" & Cells(Rows.Count, 1).End(xlUp).Row)
rng.Offset(, 2).Value = Evaluate("If(Row(1:" & rng.Rows.Count & ")," & rng.Address(, , , True) & " & " & "-" & rng.Offset(, 1).Address(, , , True) & ")")
End Sub

concatenate cells when there are duplicates without using Transpose

I am using the following code - thanks #bonCodigo
Sub groupConcat()
Dim dc As Object
Dim inputArray As Variant
Dim i As Integer
Set dc = CreateObject("Scripting.Dictionary")
inputArray = WorksheetFunction.Transpose(Sheets(1).Range("A2:B7").Value)
'-- assuming you only have two columns - otherwise you need two loops
For i = LBound(inputArray, 2) To UBound(inputArray, 2)
If Not dc.Exists(inputArray(1, i)) Then
dc.Add inputArray(1, i), inputArray(2, i)
Else
dc.Item(inputArray(1, i)) = dc.Item(inputArray(1, i)) _
& "; " & inputArray(2, i)
End If
Next i
'--output into sheet
Sheets(1).Range("D2").Resize(UBound(dc.keys) + 1) = _
Application.Transpose(dc.keys)
Sheets(1).Range("E2").Resize(UBound(dc.items) + 1) = _
Application.Transpose(dc.items)
Set dc = Nothing
End Sub
A very elegant solution. Unfortunately, I am running into the limitation of using Transpose method. I have long strings that I would like to concatenate using the above code.
Any help will be appreciated.
Regards
This also uses a variant array but without the `Transpose`. It will ignore blank values to boot.
It runs by column, then by row
Sub Bagshaw()
Dim allPosts As Variant
Dim allPosts2 As Variant
Dim lngRow As Long
Dim lngCol As Long
Dim lngCnt As Long
Dim objDic As Object
Set objDic = CreateObject("Scripting.Dictionary")
allPosts = Range("A2:B5000").Value2
ReDim allPosts2(1 To UBound(allPosts, 1) * UBound(allPosts, 2), 1 To 1)
For lngCol = 1 To UBound(allPosts, 2)
For lngRow = 1 To UBound(allPosts, 1)
If Not objDic.exists(allPosts(lngRow, lngCol)) Then
If Len(allPosts(lngRow, lngCol)) > 0 Then
objDic.Add allPosts(lngRow, lngCol), 1
lngCnt = lngCnt + 1
allPosts2(lngCnt, 1) = allPosts(lngRow, lngCol)
End If
End If
Next
Next
Range("D2").Resize(UBound(allPosts2, 1)).Value2 = allPosts2
End Sub
Sub groupConcat()
Dim r As Range
Dim ro As Range
Dim myr As Range
Dim vcompt As Integer
vcompt = 0
Set ro = Range(Range("A2"), Range("A2").End(xlDown))
For i = Range("A2").Row To Range("A2").End(xlDown).Row
Debug.Print Range("A" & i).Address
Set myr = ro.Find(what:=Range("A" & i).Value, after:=Range("A2").End(xlDown), Lookat:=xlWhole, SearchDirection:=xlNext)
If myr Is Nothing Or myr.Address = Range("A" & i).Address Then
mystr = Range("A" & i).Offset(0, 1).Value
Set r = Range(Range("A" & i), Range("A2").End(xlDown))
Set myr = r.Find(what:=Range("A" & i).Value, Lookat:=xlWhole, SearchDirection:=xlNext)
If Not myr Is Nothing And r.Address <> Range("A2").End(xlDown).Address Then
Do While myr.Address <> Range("A" & i).Address
Debug.Print "r: " & r.Address
Debug.Print "myr: " & myr.Address
mystr = mystr & "; " & myr.Offset(0, 1).Value
Set myr = r.FindNext(myr)
Loop
End If
Range("D" & 2 + vcompt).Value = Range("A" & i).Value
Range("D" & 2 + vcompt).Offset(0, 1).Value = mystr
vcompt = vcompt + 1
End If
Next i
End Sub

Resources