Excel VBA: Compare Delimited String in Cell to Column of Values - excel

I have a list of (semi-colon delimited) genes within column B I want to create from that list, a list of the genes which are found in Column A.
| Keep | List | | Result |
|------------------|----------------------------|---|-------------|
| AASS;SESN1;SEPT5 | AASS | | AASS |
| | ARMC2;SESN1;ARMC2AS1;SEPT5 | | SESN1;SEPT5 |
| | | | |
I have a start on a code, but it only appears to work for some of the gene lists, but not all.
For example, the lists in cells B2 and B3 are extracted to Column C correctly, but cell B4 ends up with 7 extra terms (but running the VBA Script a second time results in the correct number & composition), and B5 results in a strange output "4;5;0;2;3;1;SNORD1161" in D5.
This the code that I have so far, and it was modified from: https://www.mrexcel.com/forum/excel-questions/654920-match-comma-delimited-values-cell-against-individual-values-column.html
Any help would be appreciated! Thanks!
Sub matchups2()
Dim regex_leading As New VBScript_RegExp_55.RegExp
Dim regex_middle As New VBScript_RegExp_55.RegExp
Dim regex_trailing As New VBScript_RegExp_55.RegExp
Set d = CreateObject("scripting.dictionary")
For Each gene In Range("A2", Cells(Rows.Count, "A").End(3)).Value
d(gene) = 1
Next gene
Stop
For Each genelist In Range("B2", Cells(Rows.Count, "B").End(3))
c = genelist.Value
k = genelist.Row
For Each q In Split(c, ";")
If d(q) <> 1 Then
c = Replace(c, q, ";")
End If
Next q
regex_leading.Pattern = "^;{1,}"
With regex_middle
.Pattern = ";{1,}"
.Global = True
End With
regex_trailing.Pattern = ";{1,}$"
c = regex_leading.Replace(c, "")
c = regex_middle.Replace(c, ";")
c = regex_trailing.Replace(c, "")
Cells(k, "D").Value = c
Next genelist
End Sub

I think this should work for you.
Sub GenesDict()
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
'add A genes to dictionary
Dim i As Long
For i = 2 To Cells(Rows.Count, "A").End(xlUp).Row
Dim temp As Variant
temp = Split(Cells(i, "A").Value2, ";")
Dim j As Long
For j = LBound(temp) To UBound(temp)
dict.Add Trim(temp(j)), "text"
Next j
Next i
'clear D
Range("D2:D" & Range("D" & Rows.Count).End(xlUp).Row).ClearContents
'transfer from B to D only genes in A
For i = 2 To Cells(Rows.Count, "B").End(xlUp).Row
temp = Split(Cells(i, "B").Value2, ";")
For j = LBound(temp) To UBound(temp)
If dict.exists(Trim(temp(j))) Then
Cells(i, "D").Value2 = Cells(i, "D").Value2 & Trim(temp(j)) & ";"
End If
Next j
'remove trailing ";"
If Right(Cells(i, "D").Value2, 1) = ";" Then
Cells(i, "D").Value2 = Left(Cells(i, "D").Value2, Len(Cells(i, "D").Value2) - 1)
End If
Next i
End Sub

Related

Generate Sum Total in form of "=xxx+xxx..."

I have a problem for the below code - it cannot generate the result to "=XXX+XXX+XXX+XXX" (XXX = number)
is it anything wrong ?
There are 3 parts
Part A : Add comment <---run properly
Part B : Add username/editor's name into comment <---run properly
Part C : show the sum total but in form of "=xxx+xxx+xxx+xxx" not "=Sum(yyy:zzz)" <---no error but no result
Column F is Sum total and Row 9 is items name, Range I10 to last row and last column is a table, but last row and column is variable
Option Explicit
Public Sub AddComments()
Dim ws As Worksheet: Set ws = ActiveSheet 'sheet select
Dim lRow As Long: lRow = ws.Cells(ws.Rows.Count, "F").End(xlUp).Row
Dim lCol As Long: lCol = ws.Cells(9, ws.Columns.Count).End(xlToLeft).Column
Dim srg As Range: Set srg = ws.Range("A1").Resize(lRow, lCol) 'Used area
Dim Data As Variant: Data = srg.Value 'value in used area
Dim r As Long, c As Long, n As Long
Dim Comm As String
Dim fitcomment As Comment
For r = 22 To lRow
For c = 9 To lCol
If Len(Data(r, c)) > 0 Then
n = n + 1
Comm = Comm & n & ". " & Data(9, c) & " -" _
& Format(Data(r, c), "$#,##0") & vbLf
End If
Next c
If n > 0 Then
With srg.Cells(r, 6)
.ClearComments
.AddComment Left(Comm, Len(Comm) - 1)
End With
n = 0
Comm = ""
End If
Next r
For Each fitcomment In Application.ActiveSheet.Comments 'Add User Name
fitcomment.Text Text:=Environ$("Username") & vbLf, Start:=1, Overwrite:=False
fitcomment.Shape.TextFrame.Characters(1, Len(Environ$("UserName"))).Font.Bold = True
fitcomment.Shape.TextFrame.AutoSize = True
Next
For r = 22 To lRow
c = 9
Comm = "="
Do
If ws.Cells(r, c).Value <> "" And IsNumeric(ws.Cells(r, c)) And ws.Cells(r, c).Value <> 0 Then
Comm = Comm & "+" & ws.Cells(r, c).Value
End If
c = c + 1
Loop While Not (c > ws.UsedRange.Columns.Count)
If Comm <> "=" Then
ws.Cells(6, r).Value = Comm
Else
ws.Cells(6, r).Value = ""
End If
Next r
MsgBox "Comments added.", vbInformation
End Sub
It looks like you've transposed the row/column arguments for ws.Cells when writing the Comm result.
The code is generating a formula string, but it is being written to row 6 starting in column 22('V'). Cell V6 is probably beyond the columns displayed on you monitor - which explains why it looks like there is no output.
If Comm <> "=" Then
ws.Cells(6, r).Value = Comm
Else
ws.Cells(6, r).Value = ""
End If
Should be:
If Comm <> "=" Then
ws.Cells(r, 6).Value = Comm
Else
ws.Cells(r, 6).Value = ""
End If
BTW, the summation String that you are generating is of the form "=+XXX+XXX+XXX". Excel is fixing this up for you to be "=XXX+XXX+XXX".

Issue to delete a line in a FindNext loop

With this code I'm trying to search cells in a column where there is a comma character, and divide it into 2 new cells.
Next I want to Delete the original line, but it seems impossible as the value is used in FindNext operation.
What I have :
Column D Column E
Carrot Vegetable
Apple,Banana Fruit
What I need :
Column D Column E
Carrot Vegetable
Apple Fruit
Banana Fruit
What I've done :
Sub newentry()
'
' newentry Macro
'
Dim line
Dim col
Dim content
With Sheets("Feuil3").Columns("D")
Set c = .Find(",", LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address
Do
c.Select
line = ActiveCell.Row
col = ActiveCell.Column
content = ActiveCell
category = Cells(line, "E")
Dim Table() As String
Dim i As Integer
'split content in a table
Table = Split(content, ",")
'loop on table
For i = 0 To UBound(Table)
'copy result on next line
Rows(line + 1).Insert
Tableau(i) = Application.WorksheetFunction.Trim(Table(i))
Cells(line + 1, col).Value = Table(i)
Cells(line + 1, "E").Value = category
Next i
Set c = .FindNext(c)
If c Is Nothing Then
GoTo DoneFinding
End If
'where/how to do this ?
Rows(c.Row).Delete Shift:=xlUp
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
DoneFinding:
End With
End Sub
How can I delete the line that I just found ?
Thanks.
Say we have data in column D like:
Running this short macro:
Sub Restructure()
Dim N As Long, i As Long, j As Long
Dim arr1, arr2, arr3, a1, s As String
N = Cells(Rows.Count, "D").End(xlUp).Row
j = 1
arr1 = Range("D1:D" & N)
For Each a1 In arr1
s = Mid(a1, 2, Len(a1) - 2)
If InStr(s, ",") = 0 Then
Cells(j, "E").Value = "[" & s & "]"
j = j + 1
Else
arr2 = Split(s, ",")
For Each a2 In arr2
Cells(j, "E").Value = "[" & a2 & "]"
j = j + 1
Next a2
End If
Next a1
End Sub
will produce this in column E:
NOTE:
The original data is not disturbed.
insert as many lines as needed minus one below the found cell,
then simply write needed data including found cell row
don't rely on any ActiveCell, just use the c range object you found
Sub newentry()
'
' newentry Macro
'
Dim content As String, Category As String
Dim c As Range
Dim Table() As String
With Sheets("Feuil3").Columns("D")
Set c = .Find(",", LookIn:=xlValues)
If Not c Is Nothing Then
Do
content = c
Category = c.Offset(, 1).Value2
'split content in a table
Table = Split(content, ",")
c.Offset(1).EntireRow.Resize(UBound(Table)).Insert ' insert as many rows needed minus one below the found cell
c.Resize(UBound(Table) + 1).Value = Application.Transpose(Table) ' write contents in as many cells as needed, including the found one
c.Offset(, 1).Resize(UBound(Table) + 1).Value = Array(Category, Category) ' write category in as many cells as needed one column to the right of found one
Set c = .FindNext(c)
Loop While Not c Is Nothing
End If
End With
End Sub
Try this code
Sub Test()
Dim a, b, x, i As Long, j As Long, k As Long
a = Range("D1:E" & Cells(Rows.Count, 1).End(xlUp).Row).Value
ReDim b(1 To UBound(a) * 3, 1 To UBound(a, 2))
For i = LBound(a) To UBound(a)
If InStr(a(i, 1), ",") > 0 Then
x = Split(a(i, 1), ",")
For j = LBound(x) To UBound(x)
k = k + 1
b(k, 1) = Trim(x(j))
b(k, 2) = a(i, 2)
Next j
Else
k = k + 1
b(k, 1) = a(i, 1)
b(k, 2) = a(i, 2)
End If
Next i
Columns("D:E").ClearContents
Range("D1").Resize(k, UBound(b, 2)).Value = b
End Sub

How can I get a right order of data from previous sheets with VBA

Could someone help me out with the following code, I thought i figured it out but keep on stranding with the same problem:
Sub history()
nsheets = ActiveWorkbook.Worksheets.Count 'count sheets in workbook
nas_index = ActiveSheet.Index 'index of the activated sheet
nas_LR = Sheets(nas_index).Cells(Sheets(nas_index).Rows.Count, "A").End(xlUp).Row 'count rows of activesheet
For d = 1 To nsheets
If d < nas_index Then
pre_index = Sheets(nas_index - d).Index
pre_LR = Sheets(pre_index).Cells(Sheets(pre_index).Rows.Count, "A").End(xlUp).Row
oldtime = Sheets(d).Cells(1, 6).Value
newwknr = Sheets(nas_index).Cells(1, 7).Value
oldwknr = Sheets(pre_index).Cells(1, 7).Value
StrOldTime = Format(oldtime, "hh:mm:ss")
For n = 3 To nas_LR
prid_new = Sheets(nas_index).Cells(n, 1).Value
For o = 3 To pre_LR
prid_old = Sheets(pre_index).Cells(o, 1).Value
pre_am = Sheets(pre_index).Cells(o, 6).Value
pre_amw = CStr(pre_am) & "(" & StrOldTime & ")" & "(wk: " & oldwknr & ")"
If prid_new = prid_old Then
'Below is not working properly
'------------------------------
re = re & " " & pre_amw
Sheets(nas_index).Cells(n, 10).Value = re
'------------------------------
End If
Next o
Next n
Else
'MsgBox exit loop
Exit For
End If
Next d
'------------------nevermind below
Dim ntime As Date, nStrTime As String
If Not ThisWorkbook.ActiveSheet.Cells(1, 10).Value = "" Then
'-new time
ThisWorkbook.ActiveSheet.Cells(1, 12).Value = Time()
ntime = ThisWorkbook.ActiveSheet.Cells(1, 12).Value
mstrtime = Format(ntime, "hh:mm:ss:ms")
ThisWorkbook.ActiveSheet.Cells(1, 12).Value = mstrtime
'-old time
gettime = ThisWorkbook.ActiveSheet.Cells(1, 10).Value
ThisWorkbook.ActiveSheet.Cells(1, 11).Value = gettime
myStrTime = Format(gettime, "hh:mm:ss:ms")
ThisWorkbook.ActiveSheet.Cells(1, 11).Value = myStrTime
End If
End Sub
The image below is so far what I got (the text in red, is what i wish to have).
My goal is to have the following Check if I bought the same item before (ID). Collect data of this ID and store it in the column History. So that I can see if the product has been price changed over the previous weeks. I can't get the data properly of previous sheets. Instead of getting the following:
item: A B C D or
item: D C B A
I get something like this:
item: A A A A A A B B B B B B C C C C C C D D D D D D or
item: A B C D A B C D A B C D
I think I am failing here:
If prid_new = prid_old Then
'Below is not working properly
'------------------------------
re = re & " " & pre_amw
Sheets(nas_index).Cells(n, 10).Value = re
'------------------------------
End If
Can someone lent me a hand.
I tried looping the worksheets within a grocery item loop with some success.
Historical data is gathered from every worksheet prior to the current worksheet (ActiveSheet) onto the current worksheet.
Option Explicit
Sub costHistory()
Dim i As Long, w As Long, gro As Long, ndx As Long, g As Variant
Dim icost As Double, lcost As Double, dif As String
With ActiveSheet
ndx = ActiveSheet.Index
For i = 3 To .Cells(.Rows.Count, "A").End(xlUp).Row
gro = .Cells(i, "A").Value2
lcost = .Cells(i, "D").Value2
dif = vbNullString
For w = 1 To ndx - 1
With Worksheets(w)
g = Application.Match(gro, .Columns(1), 0)
If Not IsError(g) Then
If .Cells(g, "D").Value2 <> lcost Then
dif = Format(.Cells(g, "D").Value2, "0.00") & _
Format(.Cells(g, "F").Value2, " 0 ") & _
Format(.Cells(1, "F").Value2, "(hh:mm:ss)") & _
Format(.Cells(1, "G").Value2, " (\w\k\:0)") & _
Chr(124) & dif
End If
End If
End With
Next w
If CBool(Len(dif)) Then
.Cells(i, "J") = Left(dif, Len(dif) - 1)
End If
Next i
End With
End Sub

Unique values and CSV column

I would like to get the unique values from column A and all the corresponding values from column B in excel. So transform this:
Into that:
Is it possible in Excel?
With data like this in Sheet1:
running this macro:
Sub dural()
Dim s1 As Worksheet, s2 As Worksheet
Dim i As Long, j As Long, st As String
Set s1 = Sheets("Sheet1")
Set s2 = Sheets("Sheet2")
s1.Range("A:A").Copy s2.Range("A1")
s2.Range("A:A").RemoveDuplicates Columns:=1, Header:=xlNo
For Each r In s2.Range("A:A")
v = r.Value
If v = "" Then Exit Sub
For Each rr In s1.Range("A:A")
vv = rr.Value
If vv = "" Then Exit For
If v = vv Then
If r.Offset(0, 1).Value = "" Then
r.Offset(0, 1).Value = rr.Offset(0, 1).Value
Else
r.Offset(0, 1).Value = r.Offset(0, 1).Value & " ," & rr.Offset(0, 1).Value
End If
End If
Next rr
Next r
End Sub
Will produce this in Sheet2:
NOTE:
The data in Sheet1 is not required to be sorted.
Try this one:
Sub Test()
Dim objIds, arrData, i, strId
Set objIds = CreateObject("Scripting.Dictionary")
arrData = Range("A1:B8").Value ' put here your source range
For i = LBound(arrData, 1) To UBound(arrData, 1)
If IsEmpty(objIds(arrData(i, 1))) Then
objIds(arrData(i, 1)) = arrData(i, 2)
Else
objIds(arrData(i, 1)) = objIds(arrData(i, 1)) & ", " & arrData(i, 2)
End If
Next
i = 1 ' first row for output
For Each strId In objIds
Cells(i, 3) = strId ' first column for output
Cells(i, 4) = objIds(strId) ' second column for output
i = i + 1
Next
End Sub
This is all you need and nothing must be sorted:
Sub Sam()
Dim c&, i&, d$, s$, v, w
v = [a1].CurrentRegion.Resize(, 2)
ReDim w(1 To UBound(v), 1 To 2)
For i = 1 To UBound(v)
d = ", "
If s <> v(i, 1) Then d = "": c = c + 1: s = v(i, 1): w(c, 1) = s
w(c, 2) = w(c, 2) & d & v(i, 2)
Next
[d1:e1].Resize(UBound(w)) = w
End Sub
This code is extremely fast. If you were to process a large list, the efficiency here would be appreciated.
You can manage where the source data is and where the output should be written by adjusting the addresses in the square brackets at the top and bottom of the procedure.
Looking at how to solve this using Excel formulae only (I know there is a VBA tag in the OP), but here is another option.
Adding 2 additional columns with formulae we get this result:
By filtering on the finalList column where the value = 1 we get the desired result:
The formula that are required are as follows:
Cell C1 : =B2
Cell C2 (And copied down to all cells in Column C) : =IF(A3=A2,C2&","&B3,B3)
Cell D1 (And copied down to all cells in Column D) : =IF(A2=A3,0,1)
NOTE: This will only work when Column A is sorted.

Compare data in two columns and extract the value to another column

I have a column (B) that contains many cities. I want to search in every row of column (A). If it contains a value from column B this value should be written in column (C).
I made a code that searches a static value. I want this value to be the row of (column A).
Public Function searchColumn()
V_End_Of_Table = ActiveSheet.UsedRange.Rows.Count 'count the number of rows used'
Dim cell As Range
For Each cell In Range("A1:A" & V_End_Of_Table)
If InStr(1, cell.Value, "anfa", vbTextCompare) > 0 Then
Range("C" & cell.Row).Value = "anfa"
Else
Range("C" & cell.Row).Value = "No Match Found"
End If
Next 'move onto next cell'
End Function
Edit
Column A | Column B | Column C
------------+---------------+------------
casa anfa | omar | anfa
rabat hassan| hassan | hassan
casa maarouf| maarouf | maarouf
casa omar | anfa | omar
| sultan |
| driss |
Column C is the column that I want to create.
try this
For i = 1 To V_End_Of_Table 'loop for column A
For j = 1 To V_End_Of_Table 'loop for column B
If InStr(1, Cells(i, 1).Value, Cells(j, 2).Value) > 0 Then
Cells(i, 3).Value = Cells(j, 2).Value 'write found B value in c column
Exit For
Else
Cells(i, 3).Value = "no match found"
End If
If Cells(j + 1, 2).Value = "" Then
Exit For
End If
Next j
Next i
Maybe with a formula:
=IF(ISERROR(MATCH("*"&B1,A:A,0)),"",MID(A1,FIND(" ",A1)+1,LEN(A1)))
try this solution
Sub test()
Dim oCellSearch As Range, oCellSource As Range, KeySource, Key
Dim Source As Object: Set Source = CreateObject("Scripting.Dictionary")
Dim Search As Object: Set Search = CreateObject("Scripting.Dictionary")
'Grab the data from the WorkSheets into Dictionaries
n = Cells(Rows.Count, "B").End(xlUp).Row
For Each oCellSearch In ActiveSheet.Range("B1:B" & n)
If oCellSearch.Value <> "" Then
Search.Add oCellSearch.Row, oCellSearch.Value
End If
Next
n = Cells(Rows.Count, "A").End(xlUp).Row
For Each oCellSource In ActiveSheet.Range("A1:A" & n)
If oCellSource.Value <> "" Then
Source.Add oCellSource.Row, oCellSource.Value
End If
Next
'Match for contain
For Each Key In Search
For Each KeySource In Source
If UCase(Source(KeySource)) Like "*" & UCase(Search(Key)) & "*" Then
ActiveSheet.Cells(Key, "C").Value = Search(Key): Exit For
End If
Next
Next
End Sub

Resources