Extract Text/Numbers before a specific character - excel

So I've been playing around with Excel VBA to see what I can do with it. Currently, I'm stuck on one problem. My code is this:
Sub Validate_Input_Click()
Dim temp As String
For Row = 7 To 250
If Application.WorksheetFunction.CountBlank(Range(Cells(Row, 2), Cells(Row, 12))) = 0 Then
temp = ""
For col = 2 To 12
If Cells(Row, col) <> "" Then
If temp <> "" Then temp = temp & "_"
temp = temp & Cells(Row, col)
End If
Next col
Cells(Row, 1) = temp
End If
Next Row
End Sub
This works exactly as I want it to. What I'm trying to do now is, lets say in a few cells of columns B through E have Text with a dash then more text, for example:
Test - Testing
What I want to do along with concatenating is, Grab everything to the left of that dash in each individual cell. So it would look something like,
Running_This_Test_In_Some_Kind_Of_Format
instead of:
Running_This_Test - Testing_In_Some_Kind_Of_Format
I've tried creating an integer and creating a Left statement but keeps giving me not enough memory errors or using wrong argument, not sure what I'm doing incorrectly. So any help would be much appreciated!

You can replace
temp = temp & Cells(Row, col)
with
pos = InStr(1, Cells(Row, col), "-", vbTextCompare) 'find the position of dash
If pos Then 'if dash position exists
temp = temp & Trim(Left(Cells(Row, col), pos - 1)) 'take left part of that string and trim to get rid of spaces
Else
temp = temp & Cells(Row, col) 'else do it as you did it before
End If

There is no need to check for empty cell again as you are already checking them with CountBlank.
What about this?
Sub Validate_Input_Click()
Dim temp As String, str As String
Dim iRow As Long, Col As Long
For iRow = 7 To 250
If Application.WorksheetFunction.CountBlank(Range(Cells(iRow, 2), Cells(iRow, 12))) = 0 Then
temp = ""
For Col = 2 To 12
str = Trim(Split(Cells(iRow, Col), "-")(0))
If temp = "" Then
temp = str
Else
temp = temp & "_" & str
End If
Next Col
Cells(iRow, 1) = temp
End If
Next iRow
End Sub

Some slight alterations made... probably not the cleanest solution, but a solution nonetheless:
Sub Validate_Input_Click()
Dim temp As String, nextstring As String
Dim i As Long
For Row = 7 To 250
If Application.WorksheetFunction.CountBlank(Range(Cells(Row, 2), Cells(Row, 12))) = 0 Then
temp = ""
For col = 2 To 12
If Cells(Row, col) <> "" Then
If InStr(Cells(Row, col), "-") > 0 Then
For i = 1 To Len(Cells(Row, col))
If Mid(Cells(Row, col), i, 1) = "-" Then
nextstring = Left(Cells(Row, col), i - 2)
Exit For
End If
Next i
Else
nextstring = Cells(Row, col)
End If
If temp <> "" Then temp = temp & "_"
temp = temp & nextstring
End If
Next col
Cells(Row, 1) = temp
End If
Next Row
End Sub

In messing around with the code I think I found another solution to my own problem. The code looks like:
Sub Validate_Input_Click()
Dim temp As String
Dim s As String
For Row = 7 To 250
If Application.WorksheetFunction.CountBlank(Range(Cells(Row, 2), Cells(Row, 12))) = 0 Then
temp = ""
For col = 2 To 12
If Cells(Row, col) <> "" Then
s = temp
If temp <> "" Then temp = Split(s, " - ")(0) & "_"
temp = temp & Cells(Row, col)
End If
Next col
Cells(Row, 1) = temp
End If
Next Row
End Sub
Would this be a viable solution as well? Or would something else work better like the answer above from #dwirony?

Or the following. It will be fast as uses array, typed functions, used range and compared with vbNullString.
Option Explicit
Public Sub Concat()
Application.ScreenUpdating = False
Dim arr(), wb As Workbook, ws As Worksheet, i As Long, j As Long, concatString As String
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Sheet9") 'Change as required
With ws
arr = Intersect(.Range("B:E"), .UsedRange)
For i = LBound(arr, 1) To UBound(arr, 1)
concatString = vbNullString
For j = LBound(arr, 2) To UBound(arr, 2)
If InStr(1, arr(i, j), "-") > 0 Then concatString = concatString & Left$(arr(i, j), InStr(1, arr(i, j), "-") - 1)
Next j
.Cells(i, 1) = Join$(Split(Trim$(concatString), Chr$(32)), "_")
Next i
End With
Application.ScreenUpdating = True
End Sub
Data:

Related

How to use multiple function/formula on VBA

I'm new using VBA and I'm trying to code into VBA but it didn't work so far, my timestamp data is not common and I got 10000+ rows to do the same formula (sometime excel just crash so i would like to try VBA)
timestamp that I tried split
Edit : add code
Sub Split_text_3()
Dim p As String
For x = 1 To 6 '---How do it until last cell?
Cells(x, 2).Value = Mid(Cells(x, 1).Value, 9, 2) 'combind in same cell
Cells(x, 3).Value = Mid(Cells(x, 1).Value, 5, 3) 'combind in same cell
Cells(x, 4).Value = Mid(Cells(x, 1).Value, 21, 4) 'combind in same cell
Cells(x, 5).Value = Mid(Cells(x, 1).Value, 12, 8)
Next x End Sub
and the data look like this (I tried to separate it first and then might try to combine them later)
image
Please, try the next function:
Function extractDateTime(strTime As String) As Variant
Dim arrD, d As Date, t As Date
arrD = Split(strTime, " ")
d = CDate(arrD(2) & "/" & arrD(1) & "/" & arrD(4))
t = CDate(arrD(3))
extractDateTime = Array(d, t)
End Function
It can be tested in the next way:
Sub testExtractDate()
Dim x As String, arrDate
x = "WED SEP 08 08:13:52 2021"
arrDate = extractDateTime(x)
Debug.Print arrDate(0), arrDate(1)
End Sub
If it returns as you need (I think, yes...), you can use the next function to process the range. It assumes that the column keeping the strings are A:A, and returns in C:D:
Sub useFunction()
Dim sh As Worksheet, lastR As Long, Arr, arrDate, arrFin, i As Long
Set sh = ActiveSheet
lastR = sh.Range("A" & sh.rows.count).End(xlUp).row
Arr = sh.Range("A2:A" & lastR).Value
If IsArray(Arr) Then
ReDim arrFin(1 To UBound(Arr), 1 To 2)
For i = 1 To UBound(Arr)
If Arr(i, 1) <> "" Then
arrDate = extractDateTime(CStr(Arr(i, 1)))
arrFin(i, 1) = arrDate(0): arrFin(i, 2) = arrDate(1)
End If
Next i
sh.Range("C2").Resize(UBound(arrFin), 2).Value = arrFin
Else
sh.Range("C2:D2").Value = extractDateTime(CStr(sh.Range("A2").Value))
End If
End Sub
I think I have another solution (not bulletproof) but it is simplier, quicker and code less solution (no offense FraneDuru!):
Sub DateStamp()
Dim arr, arr_temp, arr_new() As Variant
Dim i As long
'Take cells from selected all the way down to 1st blank cell
'and assign values to an array
arr = ThisWorkbook.ActiveSheet.Range(Selection, Selection.End(xlDown)).Value
ReDim Preserve arr_new(1 To UBound(arr), 1 To 2)
For i = 1 To UBound(arr)
'Make another array by spliting input string by whitespace delimiter (default)
arr_temp = Split(arr(i, 1))
'Construct values in desired "format"
arr_new(i, 1) = "'" & arr_temp(2) & "/" & arr_temp(1) & "/" & arr_temp(4)
arr_new(i, 2) = arr_temp(3)
Next i
'Paste result into Excel
Selection.Offset(0, 1).Resize(UBound(arr), 2) = arr_new
End Sub
All you have to do is to select the cell toy want to start with and run the macro! :)
Bellow also a picture with watches, so you can catch-up what is going on:

joining all cell value between ascending range

I am trying to figure out how to join every cell (normal text) in the D column between the range I have set up in the A column. While searching I came across variations of the concatenate, textjoin and other functions but could not seem to find the right option.
There are around 8000 values in my file(ID value in column A) so it should be automatically filled and not manual. since it would take weeks to complete...
i've added a visual representation of the problem
The solution would be to have all cells selected in column D between the value '1' and '2' in column A and joined togheter in the E column in the row where the the cell in the A column isn't blank.
I hope someone can help me with this problem.
Try this code, please. It should be very fost, using arrays and returns the built strings at once at the end:
Sub testJoinBetweenLimits()
Dim sh As Worksheet, arrInit As Variant, arrFin As Variant
Dim strInit As String, i As Long, j As Long, refRow As Long
Set sh = ActiveSheet 'use here your sheet
arrInit = sh.Range("A2:D" & sh.Range("D" & Cells.Rows.Count).End(xlUp).Row).value
ReDim arrFin(1 To 1, 1 To UBound(arrInit, 1))
For i = 1 To UBound(arrInit, 1)
If arrInit(i, 1) <> "" Then strInit = arrInit(i, 4): refRow = i: j = i + 1
Do While arrInit(j, 1) = ""
If arrInit(j, 4) <> "" Then
strInit = strInit & ", " & arrInit(j, 4)
Else
arrFin(1, j) = Empty
End If
j = j + 1
If j >= sh.Range("D" & Cells.Rows.Count).End(xlUp).Row Then
arrFin(1, refRow) = strInit
ReDim Preserve arrFin(1 To 1, 1 To refRow)
GoTo Ending
End If
Loop
i = j - 1
arrFin(1, refRow) = strInit: strInit = "": j = 0
Next i
Ending:
sh.Range("E2").Resize(UBound(arrFin, 2), 1).value = WorksheetFunction.Transpose(arrFin)
End Sub

Grouping two columns to shrink row count by comparing | code optimization

I try to find a vba solution for the following problem:
I have two columns and try to group column1 in a comma separate way to have less rows.
e.g.
example:
I tried this, and it worked - but It take too long (about 300.000 Rows). Is there any better solution that task?
*Its just one part of my macro
For Each r In fr
If st = "" Then
st = Application.WorksheetFunction.Clean(Trim(ws.Cells(r.row, "L").Value))
Else
If Not IsInArray(Split(st, ","), ws.Cells(r.row, "L").Value) Then
st = st & ", " & Application.WorksheetFunction.Clean(Trim(ws.Cells(r.row, "L").Value))
End If
End If
If usrCheck = True Then
If str = "" Then
str = Application.WorksheetFunction.Clean(Trim(ws.Cells(r.row, "A").Value))
Else
If Not IsInArray(Split(str, ","), ws.Cells(r.row, "A").Value) Then
str = str & ", " & Application.WorksheetFunction.Clean(Trim(ws.Cells(r.row, "A").Value))
End If
End If
End If
Next
Maybe using Dictionary would be fast. What about:
Sub Test()
Dim x As Long, lr As Long, arr As Variant
Dim dict1 As Object: Set dict1 = CreateObject("Scripting.Dictionary")
Dim dict2 As Object: Set dict2 = CreateObject("Scripting.Dictionary")
With Sheet1 'Change accordingly
'Return your last row from column A
lr = .Cells(.Rows.Count, 1).End(xlUp).Row
'Get array and loop through it
arr = .Range("A2:B" & lr).Value
For x = LBound(arr) To UBound(arr)
dict1(arr(x, 2)) = arr(x, 2)
Next
'Loop through dictionary filling a second one
For Each Key In dict1.keys
For x = LBound(arr) To UBound(arr)
If arr(x, 2) = Key Then dict2(arr(x, 1)) = arr(x, 1)
Next x
.Range("F" & .Cells(.Rows.Count, 6).End(xlUp).Row + 1) = Key
.Range("G" & .Cells(.Rows.Count, 7).End(xlUp).Row + 1) = Join(dict2.Items, ", ")
dict2.RemoveAll
Next
End With
End Sub
This will get you all unique items from column A though, so if there can be duplicates and you want to keep them, this is not for you =)
Try also this, please. It works only in memory and on my computer takes less then 3 seconds for 300000 rows. The range must be filtered, like in your picture. If not, the filtering can also be easily automated.
Private Sub CondensData()
Dim sh As Worksheet, arrInit As Variant, arrIn As Variant, i As Long
Dim arrFinal() As Variant, lastRow As Long, Nr As Long, El As Variant
Dim strTemp As String, k As Long
Set sh = ActiveSheet
lastRow = sh.Cells(sh.Rows.count, "A").End(xlUp).Row
arrIn = sh.Range("B2:B" & lastRow + 1).Value
'Determine the number of the same accurrences:
For Each El In arrIn
i = i + 1
If i >= 2 Then
If arrIn(i, 1) <> arrIn(i - 1, 1) Then Nr = Nr + 1
End If
Next
ReDim arrFinal(Nr, 1)
arrInit = sh.Range("A2:B" & lastRow).Value
For i = 2 To UBound(arrInit, 1)
If i = 1 Then
strTemp = arrInit(1, 1)
Else
If arrInit(i, 2) = arrInit(i - 1, 2) Then
If strTemp = "" Then
strTemp = arrInit(i, 1)
Else
strTemp = strTemp & ", " & arrInit(i, 1)
End If
Else
arrFinal(k, 0) = arrInit(i - 1, 2)
arrFinal(k, 1) = strTemp
k = k + 1: strTemp = ""
End If
End If
Next i
sh.Range("C2:D" & lastRow).Clear
sh.Range("C2:D" & k - 1).Value = arrFinal
sh.Range("C:D").EntireColumn.AutoFit
MsgBox "Solved..."
End Sub
It will return the result in columns C:D

How to check whether the first array entry is empty in VBA

The below VBA code sets a range of cells as commentArray, removes any blanks from the array and creates a new, blank free array, called commentResults. I then want to declare the array.
There is a possibility, depending on my source data, that the array could then still be empty so the below doesn't work to declare
thisws.Cells(i, 19).Resize(columnsize:=UBound(commentResults) - LBound(commentResults) + 1).Value = commentResults
So I thought I would add a check (the if statement after the debug.print), that only declared the array if array(0) wasn't empty but I continuously get an error 9 which I can't resolve.
Dim commentArray(4) As Variant
commentArray(0) = Cells(24, 4).Value
commentArray(1) = Cells(25, 3).Value
commentArray(2) = Cells(26, 3).Value
commentArray(3) = Cells(27, 3).Value
'a and b as array loops
Dim a As Long, b As Long
Dim commentResults() As Variant
'loops through the array to remove blanks - rewrites array without blanks into commentArray
For a = LBound(commentArray) To UBound(commentArray)
If commentArray(a) <> vbNullString Then
ReDim Preserve commentResults(b)
commentResults(b) = commentArray(a)
b = b + 1
End If
Next a
Debug.Print b
If IsError(Application.Match("*", (commentResults), 0)) Then
Else
thisws.Cells(i, 19).Resize(columnsize:=UBound(commentResults) - LBound(commentResults) + 1).Value = commentResults
b = 0
End If
Any thoughts on why this might not work?
I have also tried:
If commentResults(0) <> vbNullString Then
thisws.Cells(i, 27).Resize(columnsize:=UBound(commentResults) - LBound(commentResults) + 1).Value = commentResults
End If
Sub CommentArray()
Dim Comments As Range, c As Range
Set Comments = Union(Cells(24, 4), Range(Cells(25, 3), Cells(27, 3)))
Dim commentResults() As Variant
Dim i As Long
i = 0
For Each cell In Comments
If cell.Value <> "" Then
ReDim Preserve commentResults(i)
commentResults(i) = cell.Value
i = i + 1
End If
Next cell
Dim debugStr As String
For i = LBound(commentResults) To UBound(commentResults)
debugStr = debugStr & commentResults(i) & Chr(10)
Next i
MsgBox debugStr
End Sub

How to create a nested loop to check if a value exists in a second list

I am trying to compare values in two lists. I want my code to compare a value in the first list and check all the entries in the second list. If there is a match then the code will print true next to the value in the first list and if not it will print false.
The problem I am having is that my code only compares values that are in the same row.
The code runs and I have tried it on a two smaller lists to make sure the data types are to same and there aren't any extra spaces or commas in the lists that would lead to a "False" output. I have also tried changing the order of the for and if statements but this doesn't work either.
Sub findvalues()
For i = 2 To 16
For j = 2 To 16
If Cells(i, 3).Value = Cells(i, 1).Value Then
Cells(i, 4).Value = "TRUE"
ElseIf Cells(i, 3).Value = Cells(j + 1, 1).Value Then
Cells(i, 4).Value = "TRUE"
Else
Cells(i, 4).Value = "FALSE"
End If
Next j
Next i
End Sub
Here are the two lists I am testing the code on
Slight mods to your code based on the data you provided in columns 1 & 3. As always, things could be improved but this should get you going ...
Sub findvalues()
Dim i As Long, j As Long, bResult As Boolean
For i = 2 To 16
strValueToLookFor = Cells(i, 1)
For j = 2 To 16
bResult = False
If strValueToLookFor = Cells(j, 3).Value Then
bResult = True
Exit For
End If
Next j
Cells(i, 6).Value = bResult
Next i
End Sub
... you may just need to flick the columns over so the first list searches on the second list or vice versa.
I don't see any need for VBA - formulas are the way to go - but to avoid two loops one could do this:
Sub findvalues()
Dim i As Long
For i = 2 To 130
Cells(i, 4).Value = IsNumeric(Application.Match(Cells(i, 1).Value, Range("C2:C130"), 0))
Next i
End Sub
Update: this does not cater for multiple matches.
There are many was to achieve that. one of them is by using IF & COUNTIF
Formula
=IF(COUNTIF($E$2:$E$6,A2)>0,"TRUE","FALSE")
Results:
VBA CODE
Option Explicit
Sub findvalues()
Dim i As Long
Dim rng As Range
With ThisWorkbook.Worksheets("Sheet1") 'Change if needed
Set rng = .Range("A2:A130") 'set rng to includes values from column A, rows 2:130
For i = 2 To 130 'Loop from row 2 to 130
'Check if the values in column C includes in the rng
If Application.WorksheetFunction.CountIf(rng, .Range("C" & i).Value) > 0 Then
.Range("D" & i).Value = "TRUE"
Else
.Range("D" & i).Value = "FALSE"
End If
Next i
End With
End Sub
VBA code to reconcile two lists.
Sub Reconciliation()
Dim endRow As Long
Dim ICount As Long
Dim Match1() As Variant
Dim Match2() As Variant
Dim ws As Worksheet
Set ws = Worksheets("Recon")
ICount = 0
endRow = ws.Cells(ws.Rows.Count, 2).End(xlUp).Row
endRow1 = ws.Cells(ws.Rows.Count, 11).End(xlUp).Row
Match1 = Sheet1.Range("b2:b" & endRow)
Match2 = Sheet1.Range("K2:K" & endRow1)
For i = LBound(Match1) To UBound(Match1)
For j = LBound(Match2) To UBound(Match2)
If Match1(i, 1) = Match2(j, 1) Then
ICount = ICount + 1
Sheet1.Range("C" & i + 1).Value = ICount
Sheet1.Range("L" & j + 1).Value = ICount
Else
End If
Next j
Next i
End Sub

Resources