How can I get the text after the second bracket in VBA? - vba

Trying to get the value after the second bracket, I try to work with this one i created but it's not working properly and getting more complicated while I am having more brackets in each row.. Any idea. I uploaded couple images to understand the input and output.
Sub stringtest()
Dim text As String, i As Long, firstbracket As Long, secondbracket As Long
Dim extractTest As String, y As Long
y = 1
For i = 1 To 10
text = Worksheets("Sheet1").Cells(i, 1).Value
firstbracket = InStr(1, text, "[")
secondbracket = InStr(firstbracket + 1, text, "]")
extractTest = Mid(text, firstbracket + 1, secondbracket)
Worksheets("Sheet1").Cells(y, 2).Value = extractTest
y = y + 1
Next i
End Sub

Try splitting by the second bracket, then using Left() to determine the length of the string
Option Explicit
Public Sub GetStringAfter2ndBracketInSequentialColumns()
Dim i As Long, j As Long, nextRow As Long, ub As Long, lr As Long
Dim extract As Variant, found As Long, nextCol As Long
With Sheet1
lr = .UsedRange.Rows.Count
nextRow = 1
nextCol = 2
For i = 1 To lr
extract = Split(.Cells(i, 1).Value2, "]")
ub = UBound(extract)
If ub > 0 Then
For j = 0 To ub
If Len(extract(j)) > 0 Then
If Left(extract(j), 1) <> "[" Then
found = InStr(1, extract(j), "[")
If found = 0 Then found = Len(extract(j)) + 1
.Cells(nextRow, nextCol).Value2 = Left(extract(j), found - 1)
nextRow = nextRow + 1
If nextRow > lr Then
nextRow = 1
nextCol = nextCol + 1
End If
End If
End If
Next j
End If
Next i
End With
End Sub
Test results:
This is how one of the strings (cell A1) looks like after the split
Edit: measurements for all solutions provided so far:
Timers (with 100,000 rows)
0.824 secs - TextToColumns (0.81054, 0.82812) - Output: same row split to many cols
1.679 secs - Split cells (1.66796, 1.64453) - Output: sequentially by rows, then cols
3.757 secs - ArrayList (3.69140, 3.78125) - Output: sequentially in one column

here's a short and quite fast approach
Sub main()
With Range("A1", Cells(Rows.Count, 1).End(xlUp))
.Replace what:="[*]", replacement:="|", lookat:=xlPart
.TextToColumns DataType:=xlDelimited, Other:=True, OtherChar:="|"
.Columns(1).Delete xlToLeft
End With
End Sub

Here's little different approach using ArrayList
Dim rng As Range
Dim arl As Object
Dim strVal
Dim i As Long
Set arl = CreateObject("System.Collections.ArrayList")
For Each rng In Range("A1:A" & Range("A" & Rows.Count).End(xlUp).Row)
strVal = Split(Replace(rng.Value, "[", "]"), "]")
For i = 2 To UBound(strVal) Step 2
arl.Add CStr(strVal(i))
Next i
Next rng
For i = 0 To arl.Count - 1
Range("B" & i + 1).Value = arl.Item(i)
Next i
Set arl = Nothing

Related

Adapting VBA Code to Hide Columns based off values from multiple rows

I have a piece of code I've used to hide columns based off of values being in that column, essentially it looks at all cells in that column underneath a certain row and if there's a value in there it'll keep it showing and if not it'll hide it.
Now I need it to also hide things based off values from a specific row. This is the code:
Sub HideCols()
Dim LC As Integer, j As Integer
Dim LR As Integer, curCnt As Integer
Dim k As Integer
Dim Data As Variant
Application.ScreenUpdating = False
LC = Cells(3, Columns.Count).End(xlToLeft).Column
For j = 6 To LC
LR = Cells(Rows.Count, j).End(xlUp).Row
curCnt = 0
Data = Range(Cells(1, 1), Cells(LR, LC))
For k = 3 To LR
If Rows(k).Hidden = False And Data(k, j) <> "" Then _
curCnt = curCnt + 1
Next k
Columns(j).Hidden = curCnt < 2
Next j
Application.ScreenUpdating = True
End Sub
I tried adding:
Dim i As Long
Dim c As Variant
Dim l As Integer
For i = 6 To j
For Each c In ActiveSheet.Cells(2, i)
If Columns(i).Hidden and c.Value Like "Tri-Annual" Then
ActiveSheet.Columns(i).Hidden = False
Else
ActiveSheet.Columns(i).Hidden = True
End If
Next c
Next i
This was added in following, so the hope was that it would only look at the columns that weren't hidden by the first macro and then hide all columns that don't also have "Tri-Annual" in that column in row 2. It does complete the task, but I have to run it twice. Is there any easier way of doing this?
Try this. I think I have it the right way round.
Sub HideCols()
Dim LC As Long, j As Long
Dim LR As Long, curCnt As Long
Dim k As Long
Dim Data As Variant
Application.ScreenUpdating = False
LC = Cells(3, Columns.Count).End(xlToLeft).Column
For j = 6 To LC
LR = Cells(Rows.Count, j).End(xlUp).Row
curCnt = 0
Data = Range(Cells(1, 1), Cells(LR, LC))
For k = 3 To LR
If Rows(k).Hidden = False And Data(k, j) <> "" Then _
curCnt = curCnt + 1
Next k
Columns(j).Hidden = curCnt < 2 Or Cells(2, j).Value <> "Tri-Annual"
Next j
Application.ScreenUpdating = True
End Sub

How to remove empty lines from cells in Excel (VBA)

Here's what I'm trying to achieve through all the cells in the worksheet containing a string, with limited success so far:
| EXAMPLE |
cell1_empty_line
cell1_text1
cell1_empty_line
+---------------------+
cell2_text1
cell2_emptyline
cell2_text2
+---------------------+
cell3_emptyline
cell3_emptyline
cell3_text1
+---------------------+
| EXPECTED RESULT |
cell1_text1
+---------------------+
cell2_text1
cell2_text2
+---------------------+
cell3_text1
+---------------------+
Any suggestion for such a macro?
Many thanks.
Use this macro to remove any empty lines inside all cells:
Sub TrimEmptyLines()
Dim cel As Range, s As String, len1 As Long, len2 As Long
For Each cel In ActiveSheet.UsedRange
If Not IsError(cel.Value2) Then
If InStr(1, cel.text, vbLf) > 0 Then
s = Trim(cel.Value2)
Do ' remove duplicate vbLf
len1 = Len(s)
s = Replace$(s, vbLf & vbLf, vbLf)
len2 = Len(s)
Loop Until len2 = len1
' remove vblf at beginning or at end
If Left$(s, 1) = vbLf Then s = Right$(s, Len(s) - 1)
If Right$(s, 1) = vbLf Then s = Left$(s, Len(s) - 1)
cel.value = Trim$(s)
End If
End If
Next
End Sub
This is general enough to handle any column of cells with any # of line feeds in each cell. It assumes all your values are in column "A" starting at row 1 of the active sheet:
Public Function RemoveDoubleLfs(str As String) As String
If InStr(str, vbLf & vbLf) > 0 Then
str = RemoveDoubleLfs(Replace(str, vbLf & vbLf, vbLf))
End If
RemoveDoubleLfs = str
End Function
Sub RemoveEmptyLines()
Dim i As Integer, lastRow As Integer
lastRow = ActiveSheet.Cells(Rows.Count, "A").End(xlUp).Row '
Dim val As String
For i = 1 To lastRow:
val = Cells(i, "A").Value
If InStr(1, val, vbLf) > 0 Then
val = RemoveDoubleLfs(val)
If Left(val, 1) = vbLf Then val = Right(val, Len(val) - 1)
If Right(val, 1) = vbLf Then val = Left(val, Len(val) - 1)
Cells(i, "A").Value = val
End If
Next
ActiveSheet.Rows.EntireRow.AutoFit
End Sub
The recursive replace function gets rid of double line feeds in the text of the cell. Once that's done there will be at most one VbLf at the beginning and end of the string. The last two if statements look for and remove the latter.
The autofit at the end is optional and is there purely to prettify the result; it just compacts the cells to their minimum height.
If you are working with just one cell and its blank lines within then one of these should work:
Cells.Replace what:=Chr(13), Replacement:="", LookAt:=xlPart
Cells.Replace what:=Chr(10), Replacement:="", LookAt:=xlPart
Before implementing this solution please set the values of the two variables at the top.
FirstDataColumn = 1
FirstDataRow = 2
This setting leaves starts with the first column but leaves out the first row which might contain column captions.
Sub RemoveBlanks()
Dim FirstDataColumn As Long, FirstDataRow As Long
Dim LastColumn As Long, LastRow As Long
Dim Tmp As Variant, Arr As Variant
Dim Counter As Integer
Dim C As Long, R As Long
FirstDataColumn = 1
FirstDataRow = 2
Application.ScreenUpdating = False
With ActiveSheet
With .UsedRange
LastColumn = .Columns.Count
LastRow = .Rows.Count
End With
For C = FirstDataColumn To LastColumn
ReDim Arr(LastRow, 0)
Counter = 0
For R = FirstDataRow To LastRow
Tmp = Trim(.Cells(R, C).Value)
If Len(Tmp) Then
Arr(Counter, 0) = Tmp
Counter = Counter + 1
End If
Next R
.Cells(FirstDataRow, C).Resize(LastRow, 1).Value = Arr
Next C
End With
Application.ScreenUpdating = True
End Sub

Vba Excel - concatenate cell value and loop to all columns

I need help.
In a sheet I need concatenate with a loop the columns "a" + "b" + "c", next the columns "d" + "e" + "f", etc ... an go up to the last column.
My script is locked to the second loop...
The concatenated results are to appear in a second sheet.
this is my incorrect code:
Sub concatena()
Dim x As String
Dim Y As String
b = 1 'colonna selezionata
For c = 1 To 5 'colonne concatenate da riportare
For q = 1 To 10 'righe su cui effettuare l'operazione
For t = 1 To 3 'numero celle da concatenare
For Each cell In Worksheets(1).Cells(q, t)
If cell.Value = "" Then GoTo Line1
x = x & cell(1, b).Value & "" & ""
Next
Next t
Line1:
On Error GoTo Terminate
Worksheets(2).Cells(q, c).Value = Mid(x, 1, Len(x))
x = "" 'mantiene la formattazione
Next q
b = 3 + 1 ' sposta il concatena di 3 celle la selezione delle colonne
Next c
Terminate: 'error handler
End Sub
Thank you all for the help!
This one uses arrays to speed it up a little:
Sub concatena()
Dim inArr() As Variant
Dim oArr() As Variant
Dim i&, j&
Dim ws As Worksheet
Dim rng As Range
Set ws = Worksheets("Sheet9") ' change to your worksheet
With ws
Set rng = .Range(.Cells(1, 1), .Cells(.Cells(.Rows.Count, 1).End(xlUp).Row, .Cells(1, .Columns.Count).End(xlToLeft).Column))
inArr = rng.Value
ReDim oArr(1 To UBound(inArr, 1), 1 To UBound(inArr, 2) / 3)
For i = LBound(inArr, 1) To UBound(inArr, 1)
For j = LBound(inArr, 2) To UBound(inArr, 2) Step 3
oArr(i, Int((j - 1) / 3) + 1) = inArr(i, j) & inArr(i, j + 1) & inArr(i, j + 2)
Next j
Next i
rng.Clear
.Range("A1").Resize(UBound(oArr, 1), UBound(oArr, 2)).Value = oArr
End With
you can try this code:
Option Explicit
Sub concatena()
Dim iRow As Long, iCol As Long, iCol2 As Long
Dim arr As Variant
With Worksheets("numbers")
With .Cells(1, 1).CurrentRegion
ReDim arr(1 To .Rows.Count, 1 To .Columns.Count / 3 + .Columns.Count Mod 3)
For iRow = 1 To .Rows.Count
iCol2 = 1
For iCol = 1 To .Columns.Count Step 3
arr(iRow, iCol2) = Join(Application.Transpose(Application.Transpose(.Cells(iRow, iCol).Resize(, 3).Value)), "")
iCol2 = iCol2 + 1
Next iCol
Next iRow
Worksheets("results").Range("A1").Resize(.Rows.Count, UBound(arr, 2)).Value = arr
End With
End With
End Sub
This solution provides flexibility as it uses the variable bClls to hold the number of cells to be concatenated.
Assuming the source range is B2:M16 and you want to concatenate the value of every 3 cells for each row.
It avoids the use of redim.
Sub Range_Concatenate_Cells_TEST()
Dim rSel As Range
Dim bClls As Byte
Dim rCllOut As Range
bClls = 3 'change as required
Set rSel = ThisWorkbook.Sheets("Sht(0)").Range("B2:M16") 'change as required
Set rCllOut = ThisWorkbook.Sheets("Sht(1)").Cells(2, 2) 'change as required
Call Range_Concatenate_Cells(bClls, rSel, rCllOut)
End Sub
Sub Range_Concatenate_Cells(bClls As Byte, rSel As Range, rCllOut As Range)
Dim lRow As Long, iCol As Integer
Dim lRowOut As Long, iColOut As Integer
Dim vResult As Variant
With rSel
For lRow = 1 To .Rows.Count
lRowOut = 1 + lRowOut
iColOut = 0
For iCol = 1 To .Columns.Count Step 3
iColOut = 1 + iColOut
vResult = .Cells(lRow, iCol).Resize(1, 3).Value2
vResult = WorksheetFunction.Index(vResult, 0, 0)
vResult = Join(vResult, "")
rCllOut.Offset(-1 + lRowOut, -1 + iColOut).Value = vResult
Next: Next: End With
End Sub

Sort range without sorting it in a spreadsheet

Question is about sorting data in VBA. Suppose I have a Range("A1:A10") which I want to sort in ascending order. However, I do not want any changes in my spreadsheet (so all the calculations are made within a VBA code). The output of the operation should be a NewRange where all the numbers are sorted.
Has someone ideas about this problem?
Here is a very simple little routine to sort a two-dimensional array such as a range:
Option Base 1
Option Explicit
Function SortThisArray(aryToSort)
Dim i As Long
Dim j As Long
Dim strTemp As String
For i = LBound(aryToSort) To UBound(aryToSort) - 1
For j = i + 1 To UBound(aryToSort)
If aryToSort(i, 1) > aryToSort(j, 1) Then
strTemp = aryToSort(i, 1)
aryToSort(i, 1) = aryToSort(j, 1)
aryToSort(j, 1) = strTemp
End If
Next j
Next i
SortThisArray = aryToSort
End Function
How to use this sort function:
Sub tmpSO()
Dim aryToSort As Variant
aryToSort = Worksheets(1).Range("C3:D9").Value2 ' Input
aryToSort = SortThisArray(aryToSort) ' sort it
Worksheets(1).Range("G3:H9").Value2 = aryToSort ' Output
End Sub
Notes:
The range sorted here is on Worksheet(1) in the Range("C3:D9") and the output is going on the same sheet into Range("G3:H9")
The range will be sorted in ascending order.
The range will be sorted based on the first column (here column C). If you wish to sort for another column then you just have to change all the aryToSort(i, 1) and aryToSort(j, 1) to which ever column you wish to sort. For example by column 2: aryToSort(i, 2) and aryToSort(j, 2).
UPDATE:
If you prefer to use the above as a function then this is also possible like this:
Option Base 1
Option Explicit
Function SortThisArray(rngToSort As range)
Dim i As Long
Dim j As Long
Dim strTemp As String
Dim aryToSort As Variant
aryToSort = rngToSort.Value2
For i = LBound(aryToSort) To UBound(aryToSort) - 1
For j = i + 1 To UBound(aryToSort)
If aryToSort(i, 1) > aryToSort(j, 1) Then
strTemp = aryToSort(i, 1)
aryToSort(i, 1) = aryToSort(j, 1)
aryToSort(j, 1) = strTemp
End If
Next j
Next i
SortThisArray = aryToSort
End Function
And this is how you would use the function:
This is just a sample that you may adapt to your needs, it uses B11:B20 as NewRange:
Sub SortElseWhere()
Dim A As Range, NewRange As Range
Set A = Range("A1:A10")
Set NewRange = Range("B11:B20")
A.Copy NewRange
NewRange.Sort Key1:=NewRange(1, 1), Order1:=xlAscending, Header:=xlNo, _
OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _
DataOption1:=xlSortNormal
End Sub
The original cells are not sorted, they are merely copied to another location which is sorted.
EDIT#1:
In this version, NewRange is not a range of cells, but an internal array:
Sub SortElseWhere2()
Dim A As Range, NewRange(1 To 10) As Variant
Dim i As Long, strng As String
i = 1
Set A = Range("A1:A10")
For Each aa In A
NewRange(i) = aa
i = i + 1
Next aa
Call aSort(NewRange)
strng = Join(NewRange, " ")
MsgBox strng
End Sub
Public Sub aSort(ByRef InOut)
Dim i As Long, J As Long, Low As Long
Dim Hi As Long, Temp As Variant
Low = LBound(InOut)
Hi = UBound(InOut)
J = (Hi - Low + 1) \ 2
Do While J > 0
For i = Low To Hi - J
If InOut(i) > InOut(i + J) Then
Temp = InOut(i)
InOut(i) = InOut(i + J)
InOut(i + J) = Temp
End If
Next i
For i = Hi - J To Low Step -1
If InOut(i) > InOut(i + J) Then
Temp = InOut(i)
InOut(i) = InOut(i + J)
InOut(i + J) = Temp
End If
Next i
J = J \ 2
Loop
End Sub
Here I am submitting slightly different sort routine.It sorts the 2nd column first then 1st column.
Function BubbleSort(TempArray() As Variant, SortIndex As Long)
Dim blnNoSwaps As Boolean
Dim lngItem As Long
Dim vntTemp(1 To 2) As Variant
Dim lngCol As Long
Do
blnNoSwaps = True
For lngItem = LBound(TempArray) To UBound(TempArray) - 1
If TempArray(lngItem, SortIndex) > TempArray(lngItem + 1, SortIndex) Then
blnNoSwaps = False
For lngCol = 1 To 2
vntTemp(lngCol) = TempArray(lngItem, lngCol)
TempArray(lngItem, lngCol) = TempArray(lngItem + 1, lngCol)
TempArray(lngItem + 1, lngCol) = vntTemp(lngCol)
Next
End If
Next
Loop While Not blnNoSwaps
End Function
Sub Test()
Dim vntData() As Variant
vntData = range("C3:D9")
BubbleSort vntData, 2
BubbleSort vntData, 1
range("G3:H9") = vntData
End Sub
Results obtained from this routine are shown below.

Macro to export all n set of values combinations of x>n values of range

I need a macro to exports combinations from a range of many sets of values .
The sets of exporting combs will be smaler than the data range sets .
For examble lets say that i need all 2 set of values combinations of a 3 set of values in a data range .
DATA____ EXPORT
A B C____ AB AC BC
B B A____ BB BA BA
-
All the values of the data will be in different cels each one but the combs values must be in one cell each time.
Also the exports must be in horizontial as the example .
This is a code that ifound on web little close for me , but i cannot edit this to use it .
enter code here
Sub comb()
Dim vElements As Variant, vresult As Variant
Dim lRow As Long, i As Long
vElements = Application.Transpose(Range("A1", Range("A1").End(xlDown)))
Columns("C:Z").Clear
lRow = 1
For i = 1 To UBound(vElements)
ReDim vresult(1 To i)
Call CombinationsNP(vElements, i, vresult, lRow, 1, 1)
Next i
End Sub
Sub CombinationsNP(vElements As Variant, p As Long, vresult As Variant, lRow As Long,
iElement As Integer, iIndex As Integer)
Dim i As Long
For i = iElement To UBound(vElements)
vresult(iIndex) = vElements(i)
If iIndex = p Then
lRow = lRow + 1
Range("C" & lRow).Resize(, p) = vresult
Else
Call CombinationsNP(vElements, p, vresult, lRow, i + 1, iIndex + 1)
End If
Next i
End Sub
Thank you very much and sorry for my english .
I wonder if it was more convenient to use a new Sheet/ Range with cell reference
((= Sheet1! $A1 & Sheet1! B1)) this is three lines then copy
Sub Sub export_01()
Dim aStart, aExport
Dim aRow As Integer
aRow = ActiveSheet.Range("A65536").End(xlUp).Row
aStart = 1
aExport = 5
For i = 1 To aRow
Cells(i, aExport).Value = Cells(i, aStart) & Cells(i, aStart + 1)
Cells(i, aExport + 1).Value = Cells(i, aStart) & Cells(i, aStart + 2)
Cells(i, aExport + 2).Value = Cells(i, aStart + 1) & Cells(i, aStart + 2)
Next i
End Sub()
This seems to me simply use a second for loop
dim aStartend = 1
For i = 1 To aRow
For ii = 0 To 5 ' starts whist 0 to 5 = 6 time
Cells(i, aExport+ii).Value = Cells(i, aStart) & Cells(i,aStartend + ii)
--
--
next ii
next i