Extract and Copy text between characters in excel - excel

I am looking for a way to extract text between 2 characters and copy them to a column at the end of the spreadsheet.
For example, my text looks like [abcdefg], all in Column A, I am looking to extract the text between the "[" and copy it to a new column at the end of the worksheet (as a new column)
Thanks

I would resort to functions since they're just the easiest. To pull the data between 2 characters, you'd use a mixture of MID and FIND functions.
So, assuming your data was in cell A1, you could put this formula in the cell where you want the parsed value:
=MID(A1,FIND("[",A1)+1,FIND("]",A1)-FIND("[",A1)-1)
If you wanted to automate it, you could put it into a macro then copy / paste-special values to remove the function and keep the values.

This is for text in Cell 1, 1 but you can toss some variables in there as you loop through your rows. It will grab the values within square brackets & trim it, additional text can be in front or behind the brackets.
Dim iPos1 As Integer
Dim iPos2 As Integer
iPos1 = InStr(Sheet1.Cells(1, 1), "[")
iPos2 = InStr(Sheet1.Cells(1, 1), "]")
Sheet1.Cells(1, 2) = Trim(Mid(Sheet1.Cells(1, 1), iPos1 + 1, iPos2 - iPos1 - 1))

If you want to write this into a vba module, looping through all entries in the A column and extracting the text inside the [] into an entry in column B, it might look something like:
Dim ws as Worksheet
Dim i as integer
Dim lastRow as Integer
Dim brack1pos as Integer
Dim brack2pos as Integer
Set ws = ThisWorkbook.Worksheets("My Sheet Name")
for i = 1 to lastRow 'I'll leave to you how to find the last row
brack1pos = InStr(ws.Range("A" & i), "[")
brack2pos = InStr(ws.Range("A" & i), "]")
ws.Range("B" & i) = Trim(Mid(ws.Range("A" & i), brack1pos + 1, brack2pos - brack1pos - 1))
next

Related

Sorting alphanumeric text in MS excel using VBA or manually

I have alphanumeric text in a column in Excel. I need to sort them in alphabetic order and also numerically.
The only way I could do this is by extract number to another column and then sort it, but it is not feasible for me as there same number may occur multiple times, there maybe multiple brackets and i need them to sort in alphabetic order too. I would like to know the VBA code also to automate this.
As you can see in the below image with A to Z sorting, "A05 [1][21]" came between "A05 [1][2]" & "A05 [1][3]", but I want it to be numerical order as shown in expected result..
Natural Sort via splitting
This approach
[1] assigns data to a "vertical" 2-dim array with two columns,
[2] enters a 2nd criteria column with numeric bracket values via Split() function and executes a bubble sort and
[3] writes the sorted data back to the original range.
Sub ExampleCall()
'[0]define data range (starting at 2nd row in column A:A)
With Sheet1 ' << change to project's sheet Code(Name)
Dim lastrow As Long: lastrow = .Cells(.Rows.Count, "A").End(xlUp).Row
Dim rng As Range: Set rng = .Range("A2:A" & lastrow)
End With
'[1]assign data to datafield array
Dim data: data = rng.Resize(, 2)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
'[2]calculate sort criteria and sort data
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
FillSortCriteria data ' << run procedure FillSortCriteria
NaturalSort data ' << run procedure NaturalSort
'[3]write results
'rng.Offset(, 1) = data ' (optional insertion into next column)
rng = data ' overwrite range
End Sub
Help procedure FillSortCriteria
Sub FillSortCriteria(arr)
'Purpose: calculate criteria in 2nd column of 2-dim array
Dim i As Long
For i = LBound(arr) To UBound(arr)
Dim tokens: tokens = Split(arr(i, 1) & "[[", "[")
arr(i, 2) = Left(tokens(0) & String(10, " "), 10) & _
Format(Val(tokens(1)), "000") & "." & _
Format(Val(tokens(2)), "000")
Next i
End Sub
Further hints
Splitting a string like "A05-i [1][21]" by delimiter "[" results in a zero-based array where the first token, i.e. token(0) equals "A05-i", the 2nd "1]" and the 3rd "21]". The Val() function converts the bracket items to a numeric value ignoring non-numeric characters to the right.
These tokens can be joined to a sortable criteria in the second column of the passed array; as the arr argument has been passed ByReference by default thus referring to the data array in the calling procedure, all entries change immediately the referring data entries.
Help procedure NaturalSort (modified Bubblesort)
Sub NaturalSort(arr)
'Purpose: natural sort of 2nd and 3rd token (numbers in brackets)
'Note: assumes "vertical" 2-dim array holding criteria in 2nd column
Dim cnt As Long, nxt As Long, temp, temp2
For cnt = LBound(arr) To UBound(arr) - 1
For nxt = cnt + 1 To UBound(arr)
If arr(cnt, 2) > arr(nxt, 2) Then
temp = arr(cnt, 1): temp2 = arr(cnt, 2)
arr(cnt, 1) = arr(nxt, 1): arr(cnt, 2) = arr(nxt, 2)
arr(nxt, 1) = temp: arr(nxt, 2) = temp2
End If
Next nxt
Next cnt
End Sub

In Excel VBA, extract range text and sum data

I have a spreadsheet in which there are multiple rows that have three columns (K, L M) that contain text (inserted manually from a dropdown). The inserted text includes a 'score'. For the row shown in the image that score is 3 + 2 + 2 = 7.
What I'd like to be able to do is to have that score automatically calculated and shown in column N. I'm happy to do the score extraction given the text, but I'm completey unfamiliar with Excel's object model, and how to write a VBA macro that can be triggered across all of the rows. I assume it would be passed a range somehow, or a string designating a range, but how to do that is beyond me just now. Perhaps I just need a formula? But one that calls a function to strip non-numerical data from the cell?
Any help appreciated.
Put this formula in N2 cell and drag it all the way down.
=LEFT(K2, FIND("-", K2) - 2) + LEFT(L2, FIND("-", L2) - 2) + LEFT(M2, FIND("-", M2) - 2)
For more information see reference. It sum all numbers, that are present before the hyphen (-) in a cell.
Try:
N2 = LEFT(TRIM(K2),1) + LEFT(TRIM(L2),1) + LEFT(TRIM(M2),1)
As I said in comments, this solution does not scale so well if it is more than three columns and / or the scores are more than single digit [0-9]
A VBA solution to do all of your rows and enter the values into Column N:
Sub foo()
Dim ws As Worksheet: Set ws = Sheets("Sheet1")
'declare and set your worksheet, amend as required
LastRow = ws.Cells(ws.Rows.Count, "K").End(xlUp).Row
'get the last row with data on Column A
For rownumber = 1 To LastRow 'loop through rows
For i = 11 To 13 'loop through columns
strValue = ws.Cells(rownumber, i).Value 'get text string from cell
pos = InStr(strValue, " -") 'find the dash - in cell
If pos > 0 Then 'if dash found
Value = Value + Val(Left(ws.Cells(rownumber, i).Value, pos - 1)) 'remove everything after number
End If
Next i
ws.Cells(rownumber, 14).Value = Value 'write value to column N
Value = 0
Next rownumber
End Sub

Excel 'Text to Columns' when there are no spaces

I have about 800 rows of data in a single column. The data in the column looks like this:
|Column A |
|195Marriott International127,500|
How can I have excel break this out into columns so it looks like as follows:
|Column A| |Column B| |Column C|
|195| |Marriott International| |127,500|
Thank you!
EDIT:
But you do have some patterns that you can use. Note that you have numbers in the first and third column items. Select column A, extract last character with =RIGHT(). You will only need 9 replacements to make (1 - 9), then concatenate the value. Do the same with the first number in the 3rd Column. You will then have delimiting values to use text to column.
====
you can use the Text to Column tool under the data tab on excel.
You can split the contents of one or more cells in a column, and then distribute those contents as individual parts across other cells in adjacent columns. For example, if your worksheet contains a column of full names, you can split that column into separate first name and last name columns.
I see you found a solution from Solar Mike's comment, but here's an alternative.
Below is a short piece of VBA that will preprocess the data, inserting a delimiter that will work. I tested it on a limited amount of data, so it may generate errors that need to be corrected.
The VBA ...
Option Explicit
Sub MakeDelimiters()
Dim LastRow As Long, iLoop As Long
Dim myDelim As String, myStr As String
Dim theRng As Range, theSht As Worksheet, theCell As Range
myDelim = ";"
Set theSht = Worksheets("Sheet1")
LastRow = theSht.Range("A" & theSht.Rows.Count).End(xlUp).Row
Set theRng = theSht.Range("A1:A" & LastRow)
For Each theCell In theRng
myStr = theCell.Value
For iLoop = 1 To Len(myStr)
If IsNumeric(Mid(myStr, iLoop, 1)) And _
Not IsNumeric(Mid(myStr, iLoop + 1, 1)) And _
Mid(myStr, iLoop + 1, 1) <> "," Then
myStr = Left(myStr, iLoop) & myDelim & Right(myStr, Len(myStr) - iLoop)
End If
If IsNumeric(Mid(myStr, iLoop + 1, 1)) And _
Not IsNumeric(Mid(myStr, iLoop, 1)) And _
Mid(myStr, iLoop, 1) <> "," And _
Mid(myStr, iLoop, 1) <> myDelim Then
myStr = Left(myStr, iLoop) & myDelim & Right(myStr, Len(myStr) - iLoop)
End If
Next iLoop
theCell.Value = myStr
Next theCell
End Sub
Converted this ...
to this ...
UPDATE:
The idea that a future visitor's data would look oversimplified ...
Is the lhs always 3 numbers, never 2 or 4? And the right 3,3? -SolarMike
... is laughable. So I came up with this solution. This answer is predicated on the comments:
Are columns A and C always going to be numeric? Or more precisely, will column A always end with a number, and will column C always start with a number? – OldUgly
#OldUgly - yes, that is the case here – BigMike
To answer your original question about "Text to Columns", I have no idea. However, this was tagged excel-formula, so I gave it a shot and came up with this ...
... which assumes you really want the data in columns A-C. The two helper array formulas (Ctrl+Shift+Enter)in E2 and F2 respectively are ...
=MIN(FIND(0,SUBSTITUTE(D2,CHAR(ROW(INDIRECT("65:90"))),0)&0))
=MIN(FIND(0,SUBSTITUTE(D2,CHAR(ROW(INDIRECT("48:57"))),0)&0,E2+1))
The formulas in A2, B2, and C2 respectively are (pretty obvious) ...
=LEFT(D2,E2-1)
=MID(D2,E2,F2-E2)
=RIGHT(D2,LEN(D2)-F2)
Ah, good old ASCII tables

excel vba split text

Please be aware that I am working with a series of ~1000 line medical information databases. Due to the size of the databases, manual manipulation of the data is too time consuming. As such, I have attempted to learn VBA and code an Excel 2010 macro using VBA to help me accomplish parsing certain data. The desired output is to split certain characters from a provided string on each line of the database as follows:
99204 - OFFICE/OUTPATIENT VISIT, NEW
will need to be split into
Active Row Active Column = 99204 ActiveRow Active Column+3 = OFFICE/OUTPATIENT VISIT, NEW
I have researched this topic using Walkenbach's "Excel 2013: Power Programming with VBA" and a fair amount of web resources, including this awesome site, but have been unable to develop a fully-workable solution using VBA in Excel. The code for my current macro is:
Sub EasySplit()
Dim text As String
Dim a As Integer
Dim name As Variant
text = ActiveCell.Value
name = Split(text, "-", 2)
For a = 0 To 1
Cells(1, a + 3).Value = Trim(name(a))
Next a
End Sub
The code uses the "-" character as a delimiter to split the input string into two substrings (I have limited the output strings to 2, as there exists in some input strings multiple "-" characters). I have trimmed the second string output to remove leading spaces.
The trouble that I am having is that the output is being presented at the top of the activesheet, instead of on the activerow.
Thank you in advance for any help. I have been working on this for 2 days and although I have made some progress, I feel that I have reached an impasse. I think that the issue is somewhere in the
Cells(1, a + 3).Value = Trim(name(a))
code, specifically with "Cells()".
Thank you Conrad Frix!
Yah.. funny enough. Just after I post I have a brainstorm.. and modify the code to read:
Sub EasySplit()
Dim text As String
Dim a As Integer
Dim name As Variant
text = ActiveCell.Value
name = Split(text, "-", 2)
For a = 0 To 1
ActiveCell.Offset(0, 3 + a).Value = Trim(name(a))
Next a
End Sub
Not quite the colkumn1,column4 output that I want (it outputs to column3,column4), but it will work for my purpose.
Now I need to incorporate a loop so that the code runs on each successive cell in the column (downwards, step 1) skipping all bolded cells, until it hits an empty cell.
Modified answer to modified request.
This will start on row 1 and continue until a blank cell is found in column A. If you would like to start on a different row, perhaps row 2 if you have headers, change the
i = 1
line to
i = 2
I added a check on the upper bound of our variant before doing the output writes, in case the macro is run again on already formatted cells. (Does nothing instead of erroring out)
Sub EasySplit()
Dim initialText As String
Dim i As Double
Dim name As Variant
i = 1
Do While Trim(Cells(i, 1)) <> ""
If Not Cells(i, 1).Font.Bold Then
initialText = Cells(i, 1).text
name = Split(initialText, "-", 2)
If Not UBound(name) < 1 Then
Cells(i, 1) = Trim(name(0))
Cells(i, 4) = Trim(name(1))
End If
End If
i = i + 1
Loop
End Sub
just add a variable to keep track of the active row and then use that in place of the constant 1.
e.g.
Dim iRow as Integer = ActiveCell.Row
For a = 0 To 1
Cells(iRow , a + 3).Value = Trim(name(a))
Next a
Alternate method utilizing TextToColumns. This code also avoids using a loop, making it more efficient and much faster. Comments have been added to assist with understanding the code.
EDIT: I have expanded the code to make it more versatile by using a temp worksheet. You can then output the two columns to wherever you'd like. As stated in your original question, the output is now to columns 1 and 4.
Sub tgr()
Const DataCol As String = "A" 'Change to the correct column letter
Const HeaderRow As Long = 1 'Change to be the correct header row
Dim rngOriginal As Range 'Use this variable to capture your original data
'Capture the original data, starting in Data column and the header row + 1
Set rngOriginal = Range(DataCol & HeaderRow + 1, Cells(Rows.Count, DataCol).End(xlUp))
If rngOriginal.Row < HeaderRow + 1 Then Exit Sub 'No data
'We will be using a temp worksheet, and to avoid a prompt when we delete the temp worksheet we turn off alerts
'We also turn off screenupdating to prevent "screen flickering"
Application.DisplayAlerts = False
Application.ScreenUpdating = False
'Move the original data to a temp worksheet to perform the split
'To avoid having leading/trailing spaces, replace all instances of " - " with simply "-"
'Lastly, move the split data to desired locations and remove the temp worksheet
With Sheets.Add.Range("A1").Resize(rngOriginal.Rows.Count)
.Value = rngOriginal.Value
.Replace " - ", "-"
.TextToColumns .Cells, xlDelimited, Other:=True, OtherChar:="-"
rngOriginal.Value = .Value
rngOriginal.Offset(, 3).Value = .Offset(, 1).Value
.Worksheet.Delete
End With
'Now that all operations have completed, turn alerts and screenupdating back on
Application.DisplayAlerts = True
Application.ScreenUpdating = True
End Sub
You can do this in a single shot without looping using the VBA equivalent of entering this formula, then taking values only
as a formula
=IF(NOT(ISERROR(FIND("-",A1))),RIGHT(A1,LEN(A1)-FIND("-",A1)-1 ),A1)
code
Sub Quicker()
Dim rng1 As Range
Set rng1 = Range([a1], Cells(Rows.Count, "A").End(xlUp))
With rng1.Offset(0, 3)
.FormulaR1C1 = "=IF(NOT(ISERROR(FIND(""-"",RC[-3]))),RIGHT(RC[-3],LEN(RC[-3])-FIND(""-"",RC[-3])-1 ),RC[-3])"
.Value = .Value
End With
End Sub

Excel Macro that loops within cells and writes to new file

I need a macro to get some data from an Excel spreadsheet prior to importing it into MySql linking table.
There is a column of charity names and a column with a list of id's separated by commas (these represent charity types)
To for example
Column A
CharityName1
CharityName2
CharityName3
CharityName4
Column B
100, 101,104
(empty)
104
100,105
I would like this to write a new csv file as follows
1,100
1,101
1,104
3,104
4,100
4,105
Thanks in advance for any help
This code will quickly create a csv file c:\temp\dump.csv with this format
[Updated to handle your format
I note that you may have lost data as Excel has applied scientific notation to your fields. For now I have added an ugly workaround to pad out the 0's. Should B2 be a 30 digit field?]
Sub GetEm()
Dim x()
Dim lngCnt As Long
Dim lngCnt2 As Long
Dim lngE As Long
Dim objFSO As Object
Dim objTF As Object
Dim vArr
Dim vArrElem
Set objFSO = CreateObject("scripting.filesystemobject")
Set objTF = objFSO.createtextfile("c:\temp\dump.csv", 2)
x = Application.Transpose(Range("B1", Cells(Rows.Count, "B").End(xlUp)))
For lngCnt = 1 To UBound(x)
lngE = InStr(x(lngCnt), "E")
If lngE > 0 Then
x(lngCnt) = CStr(Replace(Replace(x(lngCnt), ".", vbNullString), "E+", vbNullString) & Application.Rept("0", Right$(x(lngCnt), 2) - lngE + 1))
End If
If Len(x(lngCnt)) > 0 Then
If Len(x(lngCnt)) Mod 3 = 0 Then
For lngCnt2 = 1 To Len(x(lngCnt)) Step 3
objTF.writeline lngCnt & ",'" & Mid$(x(lngCnt), lngCnt2, 3)
Next
End If
End If
Next
objTF.Close
End Sub
I would iterate through the second column and take the values from each cell into an array, lets call it mainArray. (This iterates rows and cols, be warned: How to iterate through a variable-column-length range in Excel using VBA)
Then I would parse until the delimiting ',' and store them in a an array called cellArray with the first value as the numbered cell they were taken from. Then, replace the original cell value in mainArray with the new cellArray. ( String-Manipulation: Split this String delimited by a - character? )
So cell B1 would become cellArray = { 1, 100, 101, 104 } which would be the first value in mainArray. Do this for each cell in column B for the used range.
Then I would create a new csv ( How to create a separate CSV file from VBA? ) and then input the data into it.
To input the data I would loop through each of my saved arrays and store as CellValue = array[0] + ", " + array[i]
Lastly, I would save my new CSV file.

Resources