The TextToColumns method of the Range object automatically converts strings to numbers, but it would be nice to suppress this feature. The method has a TextQualifier parameter, but it doesn't seem to do what I'm looking for. The following data illustrates the issue. Strings in column A are delimited with semicolons that separate a text part from a number part. Note that the numbers all begin with zero, and that numbers in row 4-6 are prefixed with an apostrophe:
Column A
StringRow1;01000
StringRow2;02000
StringRow3;03000
StringRow4;'01000
StringRow5;'02000
StringRow6;'03000
The following macro splits the strings into a text part in Column B and a number part in Column C.
Sub TTC()
Application.DisplayAlerts = False
Dim rToSplit As Range
Set rToSplit = ThisWorkbook.Worksheets(1).Range("A1:A6")
rToSplit.TextToColumns _
Destination:=Range("B1"), _
DataType:=xlDelimited, _
Semicolon:=True, _
TextQualifier:=xlTextQualifierNone
End Sub
The last column illustrates the desired output:
Column A Column B Column C
Data Output Output Desired Output
StringRow1;01000 StringRow1 1000 01000
StringRow2;02000 StringRow2 2000 02000
StringRow3;03000 StringRow3 3000 03000
StringRow4;'01000 StringRow4 '01000 01000
StringRow5;'02000 StringRow5 '02000 02000
StringRow6;'03000 StringRow6 '03000 03000
I have tried formatting column C ahead of the split, like this:
rToSplit.Offset(, 3).NumberFormat = "#", but it has no effect. Switching the TextQualifier parameter to xlTextQualifierSingleQuote has the effect of treating rows 4-6 in the same way as rows 1-3.
Am I asking for the impossible? Or is there maybe some application level setting I'm not aware of? Or could I do something smart with the strings in column A?
(I could of course loop through Column C after the split and fix the issue, but for large data sets that's not attractive. For my particular application, the strings in column A are generated by code that searches for patterns in tens of thousands of text rows in several different text files; each match is added to a dictionary and then I use array() = Dictionary.Items and DestinationRange.Value = Application.WorksheetFunction.Transpose(array) to read the data to the worksheet. This is very fast. My workaround to deal with the issue described here is to save the number strings in a separate dictionary which is read to column C after the split. This works well, so I posted this out of curiosity to see what I can learn...)
You can use the FieldInfo property to set the data type for each column. You will need to know how many columns you have beforehand though, or know which column will contain the numbers.
The FieldInfo parameter takes an array of arrays, with each of the sub-arrays having 2 values. The first value represents the column number (starting at 1), and the second number is the XLColumnDataType you would like that column to be formatted as.
In this case, you'd like everything to be formatted as text (instead of a number, like it's currently doing), so you would use xlTextFormat (this is just a system defined constant equal to 2).
x.TextToColumns _
Destination:=Range("B1"), _
DataType:=xlDelimited, _
Semicolon:=True, _
TextQualifier:=xlTextQualifierNone, _
FieldInfo:=Array(Array(1, xlTextFormat), Array(2, xlTextFormat)) 'Format columns 1 and 2 as text
If you are willing to do it with a loop:
Sub TTC()
Dim row As Long, lastRow As Long, splitSpot As Integer, cellValue As String
Application.ScreenUpdating = False
With ThisWorkbook.Worksheets(1)
lastRow = .Cells(.Rows.Count, "A").End(xlUp).row
For row = 1 To lastRow
cellValue = CStr(.Range("A" & row).Value)
splitSpot = InStr(cellValue, ";")
.Range("B" & row & ":C" & row).NumberFormat = "#"
.Range("B" & row).Value = Left(cellValue, splitSpot - 1)
If Mid(cellValue, splitSpot, 1) = "'" Then
.Range("C" & row).Value = Right(cellValue, Len(cellValue) - splitSpot + 1)
Else
.Range("C" & row).Value = Right(cellValue, Len(cellValue) - splitSpot)
End If
Next
End With
End Sub
ss:
Related
I am a bit struggling with a way to variably adding lines to table according to capex category, partner number and period.
Mu final table looks like (more sections, but for example), it is not a listobject item:
my source looks like (more pivot tables, but for example):
I am indexing data from source to final table. But when combination do not exist, like the orange field in data source, in final table it should be Capex O - 3to5 years, and partner 1115, I don't know how to add it.
I thought of find function in case I would create unique ID for every line (combination of Capex letter, position (period ID) and partner), but in this case I am a bit stuck with different treatment of periods in final table and source. In final table 1-2 years and 2-3 years are combined as well as 3-4 years and 4-5 years. And I don't think transposing the source will be useful (you can prove me wrong).
Also I would need add lines in final table only for situations where value is <> 0.
I would need to add the new row in A-Z order for every capex category and period (except if its 999999, which should always be in first position; and 999999 is default value, so it is already in every capex category and period)
Every green column has its own calculated workbook where source is listed on separate sheet (Pivot)
Hope I described it clear, in any doubts, please let me know.
EDIT 18 Mar 22:
So, I almost figured it out, except couple issues.
First I created support columns in pivot sheet using concatenate on partner ID and position (there is such concat in original table, not presented before)
My code (wsSUM is declared in Public section of the code, as it is used accross modules; also you don't see whole code, only part regarding this issue):
Dim wbname As String
Dim wsp As Worksheet, wspsqa As Worksheet
Dim lcol As Long, lrow As Long
Dim i As Integer, n As Integer, x As Integer, r As Integer, RowCount As Integer, FR As Integer, LR As Integer, z As Integer, t As Integer
Dim catID As Variant
Dim sInt As Range
Rows("3:3").Find(What:="Total to IKOS").Select
Range(Selection, Selection.End(xlDown)).Select
i = Selection.Rows.Count
.
.
.
z = 0 'row number in pivot - wsp
n = 0 'column number in pivot - wsp
t = 0 'row number of left,9 for catID - wsSUM
'i is declared above, rows count in wsSUM for current quarter
For z = 2 To lrow
For n = 5 To 8
If wsp.Cells(z, lcol + n).Value <> "" Then
catID = wsp.Cells(z, lcol + n).Value
'sInt = CInt(wsSUM.Range("D4:D" & i))
If wsSUM.Range("A4:A" & i).Find(What:=catID) Is Nothing Then
t = wsSUM.Range("D4:D" & i).Find(What:=Left(catID, 9)).Row
'Debug.Print Left(catID, 9)
'declare row number
wsSUM.Rows(t + 1 & ":" & t + 1).Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
wsSUM.Range("D" & t + 1).FillDown
wsSUM.Range("P" & t + 1).FillDown
wsSUM.Range("A" & t + 1).FillDown
With wsSUM.Range("E" & t + 1)
.FormulaR1C1 = wsp.Cells(z, 2).Value
.NumberFormat = "#"
End With
'get row number for period merged cells
RowCount = wsSUM.Range("C" & t).MergeArea.Rows.Count
FR = wsSUM.Range("C" & t).MergeArea.Row
LR = wsSUM.Range("C" & t).MergeArea.Row + wsSUM.Range("C" & t).MergeArea.Rows.Count - 1
'sort data
With wsSUM.Sort
.SortFields.Add2 Key:=Range("E" & FR + 1 & ":E" & LR), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
.SetRange Range("E" & FR + 1 & ":Q" & LR)
.header = xlYes
.MatchCase = False
.Orientation = xlTopToBottom
.SortMethod = xlPinYin
.Apply
End With
i = i + 1 'with every added line is needed to broaden count
' Debug.Print "added to row " & FR & "; value is " & wsp.Cells(z, 2).Value & " " & catID
' Debug.Print "A4:A" & i
End If
End If
Next n
Next z
issue 1 - better declaration of i - I have one block of data (current quarter) starting on row 3 (header), after the first block, there are couple blank lines and another block (previous Q); I would need only rows count for current block of data, I am not able to do it without selection
issue 2 - Partner IDs in final table are stored as text, in Pivot as number, so I have an issue as some IDs are starting with 0 and find part of the code won't find them; this is only issue for Partner IDs starting with 0; I tried CInt, but was not able to apply this on Range
sInt = CInt(wsSUM.Range("D4:D" & i))
issue 3 - given the fact, I need 999999 on the first place and I am sorting the other data in A-Z, I don't know how to stretch merged cells in case data are added in that manner, the merged cell won't merge automatically
The code above is working, issue 2 will be asked in separate thread and thread will be posted here
issue 1 is working - even though I would prefer without select, it is working...
issue 3 - possibly same as issue 2 if I won't find any better solution, in that case I will post here the solution for issue 3 too
Edit:
thread for issue 2 Find number in text with leading zero - SOLVED
thread for issue 3 Adding rows and adjusting/creating merged cells
I have a list of Tyres form the internet, the list is 5,000 lines long in one column.
I need to extract from each line the data in BOLD ideally into the next column
EXAMPLE of TYRES
LS388 (145/70 R13 71T)
LS388 (155/65 R13 73T)
LS388 (155/65 R14 75T)
4-Seasons (155/65 R14 75T)
CT6 (155/70 R12 104N) 72EE
LS388 (155/70 R13 75T)
The problem is that the number can be between 59 and 120 and the letter could be H T V R N X Z and so on. Also the text could be anywhere within the line of data not always towards the end as shown.
There could be 100 variations to look for and
Rather than having one line of code to search for a LIKE 71T for each line of tyres, can I use a source table of these variations and reference them one by one in the code is some sort of loop? or other suggestions if in VBA appreciated
At the moment I have this VBA code for each possible variation, one line for each variant.
ElseIf ActiveCell.Value Like "*79S*" Then
ActiveCell.offset(0,1).Value = "79S"
Insert this formula in a cell it is assuming your string is present in column A, you can change it if it is not the case and check how many it extracts.
=MID(A1,SEARCH(" ",A1,SEARCH("R1?",A1))+1,SEARCH(")",A1)-SEARCH(" ",A1,SEARCH("R1?",A1))-1)
filter out the remaining ones, find some thing common in them and let me know and we can build another formula for those cells.
I recommend to use Regular Expressions for that if you need to do it with VBA. There is a pretty good explanation at
How to use Regular Expressions (Regex) in Microsoft Excel both in-cell and loops.
As pattern you could use something like .+\(.+ (.+)\).* (see https://regex101.com/r/jK1zKc/1/)
For a manual solution
Use Split text into different columns with the Convert Text to Columns Wizard to split into columns by the spaces.
Then do a simple replace ")" by "" in column D.
Or do the manual solution with VBA (assuming your data in column A):
Option Explicit
Sub SplitAndDelet()
Range("A:A").TextToColumns Destination:=Range("A1"), DataType:=xlDelimited, _
TextQualifier:=xlDoubleQuote, ConsecutiveDelimiter:=True, Tab:=False, _
Semicolon:=False, Comma:=False, Space:=True, Other:=True, OtherChar:= _
")", FieldInfo:=Array(Array(1, 1), Array(2, 1), Array(3, 1), Array(4, 1), Array(5, 1)) _
, TrailingMinusNumbers:=True
Range("A:C,E:E").Delete Shift:=xlToLeft
End Sub
If you want to do this in vba you could set up an array of tyres and loop through them for each cell. for example if you had this on your sheet;
you could do something like this;
Public Sub FindTyres()
' Column to Loop
Dim col As String
col = "B"
' rows to Loop
Dim startRow As String
Dim endRow As String
startRow = "2"
endRow = "7"
' Get list of Tyres
Dim tyresArr()
tyresArr = getTyresArr()
' Set Range to Loop
Dim rng As Range, cell As Range
Set rng = Range(col & startRow & ":" & col & endRow)
' Looping through Array params
Dim tyre As Variant
' Loop through Cells
For Each cell In rng
currentCellVal = cell.Value
' Loop through tyres
For Each tyre In tyresArr
Debug.Print tyre
' if you find it do something
If InStr(1, currentCellVal, CStr(tyre)) <> 0 Then
MsgBox "Value " & CStr(tyre) & " Contained in Cell " & cell.Address
Exit For
End If
Next tyre
Next cell
End Sub
Private Function getTyresArr()
Dim tyresArr(3)
tyresArr(0) = "71T"
tyresArr(1) = "73T"
tyresArr(2) = "75T"
tyresArr(3) = "104N"
getTyresArr = tyresArr
End Function
Please note this assumes you will only ever have one tyre code per line.
you may get some false positives if these strings exist for other reasons.
you would need to enter all the codes into the function that returns the array.
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
I have a txt file containing two columns. The rows look like this:
20160119,101000
Which means "19 January 2016 10:10:00"
What is the easiest way to convert this date to excel's date-time data?
I.e. eventually I want to get one column with date-time values.
ADD:
Of cause, I can do this:
DATEVALUE(MID(A11;5;2) & "/" &MID(A11;7;2) & "/" & MID(A11;1;4)) + TIMEVALUE(MID(A11;10;2) & ":" & MID(A11;12;2) & ":" & MID(A11;14;2))
But what I'm looking for is some automatic way to format it on the very export step. I can imagine it as follows: I create custom date-yime "yyyymmdd, hhmmss" format and tell Excel to recognize the data I import as such.
One way to do this is to open a blank Excel file and use Data, From Text to import the textfile. Use the comma as the delimiter. Make sure you set both columns as text in the third step of the text import wizard to avoid loosing leading zeroes. See jkp-ads.com/articles/importtext.asp for some pointers on this process.
You should get 20160119 in column A and 101000 in column B.
Then convert those values in columns A and B into dates and times using these two formulas:
=DATE(LEFT(A1,4),MID(A1,5,2),RIGHT(A1,2))
and
=TIMEVALUE(LEFT(B1,2)&":"&MID(B1,3,2)&":"&RIGHT(B1,2))
Note that this may have to be adjusted for time values less than 10:00:00 if they appear as 90000 for 9:00:00, rather than as 090000. In that case, use this formula for the time:
=TIMEVALUE(LEFT(B1,IF(LEN(B1)=5,1,2))&":"&MID(B1,3,2)&":"&RIGHT(B1,2))
A quick Range.TextToColumns method can convert the date in column A from YMD correctly but a little cell-by-cell manipulation would be necessary to add the times from column B.
Sub repairDatesYMD()
Dim rw As Long
With Worksheets("sheet2")
With .Cells(1, 1).CurrentRegion
.Columns(1).TextToColumns Destination:=.Cells(1), DataType:=xlFixedWidth, _
FieldInfo:=Array(0, 5)
For rw = 2 To .Cells(Rows.Count, 2).End(xlUp).Row
.Cells(rw, 1) = .Cells(rw, 1).Value2 + _
TimeValue(Left(.Cells(rw, 2).Text, 2) & Chr(58) & _
Mid(.Cells(rw, 2).Text, 3, 2) & Chr(58) & _
Right(.Cells(rw, 2).Text, 2))
Next rw
.Range(.Cells(rw - 1, 1), .Cells(rw - 1, 1).End(xlUp)).NumberFormat = _
"dd mmmm yyyyy hh:mm:ss"
End With
End With
End Sub
I have left the times in column B. If you are satisfied with the results, there should be no problem deleting the column.
Before repairDatesYMD processing
After repairDatesYMD processing
Good afternoon all,
I have an issue where I have users who have multiple bank account details. I need to try and create a new row for each employee who has more than one bank account, with the second bank account being allocated a new row.
Employee Number User ID BSB Account number
10000591 WOODSP0 306089,116879 343509,041145273
10000592 THOMSOS0 037125 317166
I need it to look something like this:
Employee Number User ID BSB Account number
10000591 WOODSP0 306089 343509
10000591 WOODSP0 116879 041145273
10000592 THOMSOS0 037125 317166
Any thoughts? Your input is greatly appreciated!
Screenshots are here to demonstrate:
Right click on the tab and choose "View Code"
Paste this code in:
Sub SplitOnAccount()
Dim X As Long, Y As Long, EmpNo As String, UserID As String, BSB As Variant, AccNo As Variant
Range("F1:I1") = Application.Transpose(Application.Transpose(Array(Range("A1:D1"))))
For X = 2 To Range("A" & Rows.Count).End(xlUp).Row
EmpNo = Range("A" & X).Text
UserID = Range("B" & X).Text
BSB = Split(Range("C" & X).Text, ",")
AccNo = Split(Range("D" & X).Text, ",")
For Y = LBound(AccNo) To UBound(AccNo)
Range("F" & Range("F" & Rows.Count).End(xlUp).Row).Offset(1, 0).Formula = EmpNo
Range("G" & Range("G" & Rows.Count).End(xlUp).Row).Offset(1, 0).Formula = UserID
Range("H" & Range("H" & Rows.Count).End(xlUp).Row).Offset(1, 0).Formula = BSB(Y)
Range("I" & Range("I" & Rows.Count).End(xlUp).Row).Offset(1, 0).Formula = AccNo(Y)
Next
Next
End Sub
Close the window to go back to excel
Press ALT-F8
Choose SplitOnAccount and click run.
Note, this is going to populate the split data to rows F to I, make sure there is nothing in there. If there is post back and we can change it.
Also format columns F - I as text before you run it or Excel will strip leading zeros off as it will interpret it as a number.
Here is another sub that appears to perform what you are looking for.
Sub stack_accounts()
Dim rw As Long, b As Long
Dim vVALs As Variant, vBSBs As Variant, vACTs As Variant
With ActiveSheet '<-define this worksheet properly!
For rw = .Cells(Rows.Count, 1).End(xlUp).Row To 2 Step -1
vVALs = .Cells(rw, 1).Resize(1, 4).Value
vBSBs = Split(vVALs(1, 3), Chr(44))
vACTs = Split(vVALs(1, 4), Chr(44))
If UBound(vBSBs) = UBound(vBSBs) Then
For b = UBound(vBSBs) To LBound(vBSBs) Step -1
If b > LBound(vBSBs) Then _
.Rows(rw + 1).Insert
.Cells(rw - (b > LBound(vBSBs)), 1).Resize(1, 4) = vVALs
.Cells(rw - (b > LBound(vBSBs)), 3).Resize(1, 2).NumberFormat = "#"
.Cells(rw - (b > LBound(vBSBs)), 3) = CStr(vBSBs(b))
.Cells(rw - (b > LBound(vBSBs)), 4) = CStr(vACTs(b))
Next b
End If
Next rw
End With
End Sub
I was originally only going to process the rows that had comma delimited values in columns C and D but I thought that processing all of them would allow the macro to set the Text number format and get rid of the Number as text error warnings and keep the leading zero in 041145273.
You Can definitely use Power Query to transform the data to generate new rows using split column option.
Check this article it explains the process in detail.
Load Data in Power Query section of excel.
Create an Index (Not required step)
Use Split column function with advance options and split them into new rows.
Save this result into new table for your use.
I did it myself and it worked like a charm.
A formula solution:
Delimiter: Can be a real delimiter or an absolute reference to a cell containing only the delimiter.
HelperCol: I have to use a helper column to make it work. You need to give the column letter.
StartCol: The column letter of the first column containing data.
SplitCol: The column letter of the column to be splitted.
Formula1: Used to generate the formula for the first column not to be splitted. You can fill this formula down and then fill to right.
Formula2: Used to generate the formula for the column to be splitted(only support split one column).
Formula3: Used to generate the formula for the Helper column.
(If the title of the column to be splitted contains the delimiter, you must change the first value of the helper column to 1 manually.)
Formula1:=SUBSTITUTE(SUBSTITUTE("=LOOKUP(ROW(1:1),$J:$J,A:A)&""""","$J:$J","$"&B2&":$"&B2),"A:A",B3&":"&B3)
Formula2:=SUBSTITUTE(SUBSTITUTE(SUBSTITUTE("=MID($M$1&LOOKUP(ROW(A1),$J:$J,F:F)&$M$1,FIND(""艹"",SUBSTITUTE($M$1&LOOKUP(ROW(A1),$J:$J,F:F)&$M$1,$M$1,"&"""艹"",ROW(A2)-LOOKUP(ROW(A1),$J:$J)))+1,FIND(""艹"",SUBSTITUTE($M$1&LOOKUP(ROW(A1),$J:$J,F:F)&$M$1,$M$1,""艹"",ROW(A2)-LOOKUP(ROW(A1),$J:$J)+1))-FIND(""艹"",SUBSTITUTE($M$1&LOOKUP(ROW(A1),$J:$J,F:F)&$M$1,$M$1,""艹"",ROW(A2)-LOOKUP(ROW(A1),$J:$J)))-1)&""""","$M$1",IF(ISERROR(INDIRECT(B1)),""""&B1&"""",B1)),"$J:$J","$"&B2&":$"&B2),"F:F",B4&":"&B4)
Formula3:=SUBSTITUTE(SUBSTITUTE(SUBSTITUTE("=SUM(E1,LEN(B1)-LEN(SUBSTITUTE(B1,$H$1,"""")))+1","B1",B4&1),"$H$1",IF(ISERROR(INDIRECT(B1)),""""&B1&"""",B1)),"E1",B2&1)
Helper must filled one row more than the data.
How to use:
Copy the formula generated by the above three formula.
Use Paste Special only paste the value.
Make the formula into effect.
Fill the formula.
Bug:
Numbers will be converted to Text. Of course you can remove the &"" at the end of the formula, but blank cells will be filled with 0.
ps. This method may by very hard to comprehend. But once you master it, it can be very useful to solve relative problems.