How convert sentences to column/row of single words - excel

Why is it not working?
Prints only the first word and I don't know how I can change "10" in "For" on something like "For each word in sentence"
Sub Change()
Dim S As String
Dim i As Integer
Dim x As String
S = InputBox("Sentence")
x = Split(S, " ")
For i = 1 To x
Cells(1, i).Value = Split(S, " ")
Next i
End Sub

Try this:
Public Sub Change()
Dim sentence As String: sentence = InputBox("Sentence")
Dim col As Long: col = 1
Dim word As Variant: For Each word In Split(sentence, " ")
ThisWorkbook.Worksheets("Sheet1").Cells(1, col).Value = word
col = col + 1
Next
End Sub

Split Sentence to Worksheet
Option Explicit
Sub SentenceToRow()
Const ProcTitle As String = "Sentence to Row"
Const First As String = "A1"
Dim S As Variant: S = InputBox("Input a Sentence", ProcTitle)
If Len(S) = 0 Then
MsgBox "You canceled.", vbExclamation, ProcTitle
Exit Sub
End If
Dim Strings() As String: Strings = Split(S)
Dim cCount As Long: cCount = UBound(Strings) + 1
Dim ws As Worksheet: Set ws = ActiveSheet
Dim rg As Range: Set rg = ws.Range(First).Resize(, cCount)
rg.Value = Strings
MsgBox "Sentence split to a row.", vbInformation, ProcTitle
End Sub
Sub SentenceToColumn()
Const ProcTitle As String = "Sentence to Column"
Const First As String = "A1"
Dim S As Variant: S = InputBox("Input a Sentence", ProcTitle)
If Len(S) = 0 Then
MsgBox "You canceled.", vbExclamation, ProcTitle
Exit Sub
End If
Dim Strings() As String: Strings = Split(S)
Dim rCount As Long: rCount = UBound(Strings) + 1
Dim ws As Worksheet: Set ws = ActiveSheet
Dim rg As Range: Set rg = ws.Range(First).Resize(rCount)
rg.Value = Application.Transpose(Strings)
MsgBox "Sentence split to a column.", vbInformation, ProcTitle
End Sub

this is fast and eficient:
Sub testSplit2Row()
Dim frase As String
frase = "las palabras de amor"
Dim ary As Variant
ary = Split(frase, " ")
Dim dest As Range
Dim start As Range
Set start = Range("B1")
Set dest = start.Resize(UBound(ary) + 1)
dest.Value = Application.Transpose(ary)
start.Resize(, UBound(ary) + 1).Value = ary
End Sub

If you dispose of the dynamic array functions of MSExcel 365, you might profit from the following function, usable as well as udf or via code.
The function words() accepts range values, array values or explicit string inputs as first argument s; the second argument IsVertical is optional and indicates that results will be returned as vertical array by default (instead of a "flat" array).
Public Function words(ByVal s As Variant, Optional IsVertical As Boolean = True)
'Debug.Print VarType(s)
If VarType(s) >= vbArray Then
s = Replace(Application.WorksheetFunction.ArrayToText(s), ",", "")
End If
words = Split(s)
If IsVertical Then
words = Application.WorksheetFunction.Transpose(Split(s))
End If
End Function
a) Example using a multi-row range input in B2
=words(A2:A4)
b) Example call via code
Option Explicit ' module head of code module
Sub ExampleCall
With Sheet1
Dim wds As Variant
wds = words(.Range("A2:A4"))
.Range("A10").Resize(UBound(wds), UBound(wds, 2)) = wds
End With
End Sub
If you intend, however to display results horizontally, just code as follows (note the dimension change!):
'...
wds = words(.Range("a2:a4"), False) ' False returns "flat" 1-dim array
.Range("A10").Resize(1, UBound(wds)) = wds

Related

Pick random number from a range of cells, some of which are empty cells

I am trying to pick a random value from a range of values and output this value in Cell E6.
Some of the cells are blank so I need to pick from a cell that contains a value.
The range of which the values to choose from is H127:1127.
Sub Generate()
Dim i As Double
Dim ws As Worksheet
Set ws = Sheets("Upstream-Overall")
For Each Cell In ws.Range("H127:H1127")
If ActiveCell.Value <> "" Then
Range("E6") = Random_Number = Application.WorksheetFunction.RandBetween(0.1, 5)
End If
Next Cell
End Sub
Return the Number From a Random Cell
Sub Generate()
Const wsName As String = "Upstream-Overall"
Const sRangeAddress As String = "H127:H1127"
Const dCellAddress As String = "E6"
' Reference the worksheet in the workbook containing this code.
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets(wsName)
' Write the values from the first column of the range to an array.
Dim Data As Variant: Data = ws.Range(sRangeAddress).Columns(1).Value
Dim sr As Long, dr As Long
' Shift the numeric values (up) to the beginning of the array.
For sr = 1 To UBound(Data, 1)
If VarType(Data(sr, 1)) = 5 Then ' is a number
dr = dr + 1
Data(dr, 1) = Data(sr, 1)
'Else ' is not a number; do nothing
End If
Next sr
' Check if at least one number was found.
If dr = 0 Then
MsgBox "No numbers in the first column of the range.", vbCritical
Exit Sub
End If
' Write the number from a random element
' of the numeric part of the array to the cell.
ws.Range(dCellAddress).Value = Data(Int(dr * Rnd + 1), 1)
' Inform of success.
MsgBox "New random number generated.", vbInformation
End Sub
You could do something like this:
Sub Generate()
Dim ws As Worksheet, rng As Range, i As Long, v
Set ws = Sheets("Upstream-Overall")
Set rng = ws.Range("H127:H1127")
Do
i = Application.RandBetween(1, rng.Cells.Count)
v = rng.Cells(i).Value
Loop While Len(v) = 0 'loop until selected cell has a value
ws.Range("E6").Value = v
End Sub
(assuming the range will never be completely empty)

Error handling for Match inside a class - Excel VBA

I have a VBA class that I call to fetch column numbers for the required columns in a worksheet (15 of them). Users are allowed to move columns around and the match functionality works well. However if a user deletes a column, I get a runtime error. How do I trap an error and let the user know that 'X' column name has been deleted but still continue checking the rest of the columns.
Option Explicit
Public EmpName As Long, EmpID As Long, EmpDepartment As Long, EmpAddress As Long
Private Sub Class_Initialize()
Dim ws As Worksheet, r As Range, tStr As String, wsname As String
Set ws = ActiveSheet: Set r = ws.Range("1:1")
EmpName = Application.WorksheetFunction.Match("EmpName", r.value, 0)
EmpID = Application.WorksheetFunction.Match("EmpID", r.value, 0)
EmpDepartment = Application.WorksheetFunction.Match("EmpDepartment", r.value, 0)
EmpAddress = Application.WorksheetFunction.Match("EmpAddress", r.value, 0)
Set r = Nothing: Set ws = Nothing
End Sub
Original code updated
To avoid the run-time error you could use Application.Match instead of Application.WorksheetFunction.Match.
Option Explicit
Public EmpName As Long, EmpID As Long, EmpDepartment As Long, EmpAddress As Long
Private Sub Class_Initialize()
Dim ws As Worksheet, r As Range, tStr As String, wsname As String
Dim Res As Variant
Set ws = ActiveSheet
Set r = ws.Range("1:1")
Res = Application.Match("EmpName", r.Value, 0)
If Not IsError(Res) Then
EmpName = Res
Else
MsgBox "EmpName column not found!", vbInformation, "Missing Column"
End If
Res = Application.Match("EmpID", r.Value, 0)
If Not IsError(Res) Then
EmpID = Res
Else
MsgBox "EmpID column not found!", vbInformation, "Missing Column"
End If
Res = Application.Match("EmpDepartment", r.Value, 0)
If Not IsError(Res) Then
EmpName = Res
Else
MsgBox "EmpDepartment column not found!", vbInformation, "Missing Column"
End If
Res = Application.Match("EmpAddress", r.Value, 0)
If Not IsError(Res) Then
EmpAddress = Res
Else
MsgBox "EmpAddress column not found!", vbInformation, "Missing Column"
End If
End Sub
Using a dictionary
If you don't want all the repetition in the code you might want to look at using a dictionary to store the column names/numbers.
Option Explicit
Private Sub Class_Initialize()
Dim ws As Worksheet, r As Range, tStr As String, wsname As String
Dim dicCols As Object
Dim arrCols As Variant
Dim Res As Variant
Dim idx As Long
arrCols = Array("EmpName", "EmpID", "EmpDepartmen", "EmpAddress")
Set dicCols = CreateObject("Scripting.Dictionary")
Set ws = ActiveSheet
Set r = ws.Range("1:1")
For idx = LBound(arrCols) To UBound(arrCols)
Res = Application.Match(arrCols(idx), r.Value, 0)
If Not IsError(Res) Then
dicCols(arrCols(idx)) = Res
Else
dicCols(arrCols(idx)) = "Not Found"
MsgBox arrCols(idx) & " column not found!", vbInformation, "Missing Column"
End If
Next idx
End Sub
Once this code is executed you can use dicCols(ColumnName) to get the column number.
For example, wherever you refer to the variable EmpName in the rest of the code you can use dicCols("EmpName").
Using a dictionary populated from a function
Another refinement might be to use a function to create the dictionary.
This would allow you to pass different sets of column names when required.
Option Explicit
Public dicCols As Object
Private Sub Class_Initialize()
Dim ws As Worksheet, r As Range, tStr As String, wsname As String
Dim arrColNames As Variant
Dim arrNotFound() As Variant
Dim ky As Variant
Dim cnt As Long
arrColNames = Array("EmpName", "EmpID", "EmpDepartment", "EmpAddress")
Set ws = ActiveSheet
Set r = ws.Range("1:1")
Set dicCols = GetColNos(arrColNames, r)
For Each ky In dicCols.keys
If dicCols(ky) = "Not Found" Then
cnt = cnt + 1
ReDim Preserve arrNotFound(1 To cnt)
arrNotFound(cnt) = ky
End If
Next ky
If cnt > 0 Then
MsgBox "The following columns were not found:" & vbCrLf & vbCrLf & Join(arrNotFound, vbCrLf), vbInformation, "Missing Columna"
End If
End Sub
Function GetColNos(arrColNames, rngHdr As Range) As Object
Dim dic As Object
Dim idx As Long
Dim Res As Variant
Set dic = CreateObject("Scripting.Dictionary")
For idx = LBound(arrColNames) To UBound(arrColNames)
Res = Application.Match(arrColNames(idx), rngHdr.Value, 0)
If Not IsError(Res) Then
dic(arrColNames(idx)) = Res
Else
dic(arrColNames(idx)) = "Not Found"
End If
Next idx
Set GetColNos = dic
End Function

Extract cell addresses from within formula

I am looking for a way to extract addresses / ranges from a formulae. I have created an example formula below.
=SUMIFS(Worksheet_Name!$C$3:$C$20, Worksheet_Name!$A$3:$A$20, "Blue", Worksheet_Name!$B$3:$B$20, "Green")
I am trying to get some sort VBA routine which I can pick apart the formulae.
I would like to get the ranges as follows:
Worksheet_Name!$C$3:$C$20
Worksheet_Name!$A$3:$A$20
Worksheet_Name!$B$3:$B$20
So I can access these separately.
How about the following, this will take a cell as input, then it will strip out anything outside the brackets and split the remainder of the formula by commas into an array, and then it will display then in a Msgbox, but you can adapt that to your needs:
Sub Get_Ranges_In_Formula()
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
'declare the worksheet you are working with
Dim rngs As String
Dim arrayofRngs
cellvalue = ws.Range("A1").Formula
'get the formula from the cell
openingParen = InStr(cellvalue, "(")
closingParen = InStrRev(cellvalue, ")")
rngs = Mid(cellvalue, openingParen + 1, closingParen - openingParen - 1)
'strip anything outside the brackets
arrayofRngs = Split(rngs, ",")
'split by comma into array
For i = LBound(arrayofRngs) To UBound(arrayofRngs)
If InStr(arrayofRngs(i), "!") > 0 Then MsgBox arrayofRngs(i)
Next
End Sub
A solution using RegEx to extract cell references from formulas:
Sub Get_Ranges_In_Formula()
Dim ws As Worksheet: Set ws = ThisWorkbook.Worksheets("Sheet1")
Dim xRetList As Object
Dim xRegEx As Object
Dim I As Long
Dim xRet As String
Dim Rg As Range
Set Rg = ws.Range("A1")
Application.Volatile
Set xRegEx = CreateObject("VBSCRIPT.REGEXP")
With xRegEx
.Pattern = "('?[a-zA-Z0-9\s\[\]\.]{1,99})?'?!?\$?[A-Z]{1,3}\$?[0-9]{1,7}(:\$?[A-Z]{1,3}\$?[0-9]{1,7})?"
.Global = True
.MultiLine = True
.IgnoreCase = False
End With
Set xRetList = xRegEx.Execute(Rg.Formula)
If xRetList.Count > 0 Then
For I = 0 To xRetList.Count - 1
MsgBox xRetList.Item(I)
Next
End If
End Sub
Try this
Sub Test()
Dim e, s As String
s = MyArguments(Range("A1"))
For Each e In Split(s, ",")
If InStr(e, "!") Then Debug.Print Trim(e)
Next e
End Sub
Function MyArguments(rng As Range) As String
MyArguments = Split(Split(rng.Formula, "(")(1), ")")(0)
End Function

cells.text returns value in 1 instance and nothing in second instance

I've got the following question:
This line wsMotiv.Range("motiv_naam") = Cells(iRijnummer, iKolomnrNaam).Text gives a name (string) value. Why doesn't the line s = ThisWorkbook.Worksheets(1).Cells(iRijnummer, iKolomnrNaam).Text do the same? It passes " ".
Sub (whatev)
AantalZichtbareRows
Dim rng As Range
Dim row As Range
Dim StrFileName As String
'If Aantalregels > 1 Then
Set rng = Selection.SpecialCells(xlCellTypeVisible)
For Each row In rng.Rows
iRijnummer = row.row
If iRijnummer > 1 Then
Windows(c_SourceDump).Activate
wsMotiv.Range("motiv_cid") = Cells(iRijnummer, iKolomnrCorpID).Text
wsMotiv.Range("motiv_naam") = Cells(iRijnummer, iKolomnrNaam).Text
wsMotiv.Range("motiv_ldg") = Cells(iRijnummer, iKolomnrHuidigeLeidingGevende).Text
Worksheets("stambestand").Activate
'naamOpmaken
Dim s As String: s = ThisWorkbook.Worksheets(1).Cells(iRijnummer, iKolomnrNaam).Text
Dim Position As Long, Length As Long
Dim n As String
Position = InStrRev(s, " ")
Length = Len(s)
n = Right(s, Length - Position)
End If
Next row
End Sub
You are referring to the ActiveSheet in Cells(iRijnummer, iKolomnrNaam).Text and to the first worksheet in ThisWorkbook.Worksheets(1).Cells(iRijnummer, iKolomnrNaam).Text. Probably they are different. Try this to see:
Sub TestMe
Debug.Print wsMotiv.Name
Debug.Print Worksheets(1).Name
Debug.Print ActiveSheet.Name
End Sub

Export selected rows and columns to CSV-file

I want to be able to export a selected range of cells to a .csv file using VBA. What I have come up with so far does the job excellently for cohering selections, but fails misearably when multiple columns are selected.
Here is the code I managed to put together from snippets found on the internet: It also fiddles around with some UI and since my Excel speaks German and I need to have "." as decimal separator instead of "," it tweaks that.
Sub Range_Nach_CSV_()
Dim vntFileName As Variant
Dim lngFN As Long
Dim rngRow As Excel.Range
Dim rngCell As Excel.Range
Dim strDelimiter As String
Dim strText As String
Dim strTextCell As String
Dim strTextCelll As String
Dim bolErsteSpalte As Boolean
Dim rngColumn As Excel.Range
Dim wksQuelle As Excel.Worksheet
Dim continue As Boolean
strDelimiter = vbtab
continue = True
Do While continue = True
vntFileName = Application.GetSaveAsFilename("Test.txt", _
FileFilter:="TXT-File (*.TXT),*.txt")
If vntFileName = False Then
Exit Sub
End If
If Len(Dir(vntFileName)) > 0 Then
Dim ans As Integer
ans = MsgBox("Datei existiert bereits. Überschreiben?", vbYesNo)
If ans = vbYes Then
continue = False
ElseIf ans = vbNo Then
continue = True
Else
continue = False
End If
Else
continue = False
End If
Loop
Set wksQuelle = ActiveSheet
lngFN = FreeFile
Open vntFileName For Output As lngFN
For Each rngRow In Selection.Rows
strText = ""
bolErsteSpalte = True
For Each rngCell In rngRow.Columns
strTextCelll = rngCell.Text
strTextCell = Replace(strTextCelll, ",", ".")
If bolErsteSpalte Then
strText = strTextCell
bolErsteSpalte = False
Else
strText = strText & strDelimiter & strTextCell
End If
Next
Print #lngFN, strText
Next
Close lngFN
End Sub
As I already mentioned the sub works well with coherent selections and also with multiple selected lines, but fails when it comes to multiple columns.
The current output of the sub can be seen on this here picture:
multiple columns failed
As one would expect, I want the .csv-file (or respective .txt-file) to look like this:
multiple columns desired output
How can I achieve the desired behaviour for the last case?
And would someone be so kind to include the links as images? If perceived appropriate, of course.
This might seem a little complex, but your use case isn't very simple...
It does assume that each of the selected areas is the same size, and that they all line up (as either rows or columns)
Sub Tester()
Dim s As String, srow As String, sep As String
Dim a1 As Range, rw As Range, c As Range, rCount As Long
Dim areaCount As Long, x As Long
Dim bColumnsSelected As Boolean
Dim sel As Range
bColumnsSelected = False
Set sel = Selection
areaCount = Selection.Areas.Count
Set a1 = Selection.Areas(1)
If areaCount > 1 Then
If a1.Cells(1).Column <> Selection.Areas(2).Cells(1).Column Then
'areas represent different columns (not different rows)
bColumnsSelected = True
Set sel = a1
End If
End If
rCount = 0
For Each rw In sel.Rows
rCount = rCount + 1
srow = ""
sep = ""
For Each c In rw.Cells
srow = srow & sep & Replace(c.Text, ",", ".")
sep = ","
Next c
'if there are multiple areas selected (as columns), then include those
If bColumnsSelected Then
For x = 2 To areaCount
For Each c In Selection.Areas(x).Rows(rCount).Cells
srow = srow & sep & Replace(c.Text, ",", ".")
Next c
Next x
End If
s = s & IIf(Len(s) > 0, vbCrLf, "") & srow
Next rw
Debug.Print s
End Sub

Resources