I have variables el1, el2,....el16 and c1, c2,....c16 and Counter
Would like to use in a counting loop
For Counter = 1 to 16
If el1 = "x" Then
el1 = 0
c1 = 0
Else
c1 = 1
End If
Next
The If statement works fine with defined variables. How can I combine the "Counter" variable with the "el" and "c" in the loop to recognize as el1, c1, el2, c2,...el16, c16
Here is the final version of the script. It worked fine in Windows 7 version of ArcPad, but the If statement blew up on the mobile device using mobile vbscript runtime libraries. The variable array method might work better, but I wasn't quite sure how to patch it in. Any help would be appreciated.
Sub findelement
Dim el1, el2, el3, el4, el5, el6, el7, el8, el9, el10, el11, el12, el13, el14, el15, el16, scoreA, scoreB, calc
el1 = EDITFORM.Pages.Item("page4").Controls.Item("element1")
el2 = EDITFORM.Pages.Item("page4").Controls.Item("element2")
el3 = EDITFORM.Pages.Item("page4").Controls.Item("element3")
el4 = EDITFORM.Pages.Item("page4").Controls.Item("element4")
el5 = EDITFORM.Pages.Item("page4").Controls.Item("element5")
el6 = EDITFORM.Pages.Item("page4").Controls.Item("element6")
el7 = EDITFORM.Pages.Item("page4").Controls.Item("element7")
el8 = EDITFORM.Pages.Item("page4").Controls.Item("element8")
el9 = EDITFORM.Pages.Item("page5").Controls.Item("element9")
el10 = EDITFORM.Pages.Item("page5").Controls.Item("element10")
el11 = EDITFORM.Pages.Item("page5").Controls.Item("element11")
el12 = EDITFORM.Pages.Item("page5").Controls.Item("element12")
el13 = EDITFORM.Pages.Item("page5").Controls.Item("element13")
el14 = EDITFORM.Pages.Item("page5").Controls.Item("element14")
el15 = EDITFORM.Pages.Item("page5").Controls.Item("element15")
el16 = EDITFORM.Pages.Item("page5").Controls.Item("element16")
Dim c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, Counter
For Counter = 1 to 16
If "x" = eval ("el" & Counter) Then
execute "el" & Counter & " = 0"
execute "c" & Counter & " = 0"
Else
execute "c" & Counter & " = 1"
End If
Next
'do math
scoreA = CInt(el1) + CInt(el2) + CInt(el3) + CInt(el4) + CInt(el5) + CInt(el6) + CInt(el7) + CInt(el8) + CInt(e9) + CInt(el10) + CInt(el11) + CInt(el12) + CInt(el13) + CInt(el14) + CInt(el15) + CInt(el16)
scoreB = CInt(c1) + CInt(c2) + CInt(c3) + CInt(c4) + CInt(c5) + CInt(c6) + CInt(c7) + CInt(c8) + CInt(c9) + CInt(c10) + CInt(c11) + CInt(c12) + CInt(c13) + CInt(c14) + CInt(c15) + CInt(c16)
calc = CDbl(scoreA) / CDbl(scoreB)
'vbscript assumes these are strings and appends them, to get the sum like you want cast them to a double (CDbl) or integer (CInt)
'MsgBox (scoreA)
'MsgBox (scoreB)
'MsgBox (calc)
'Return calculated data to form.....
EDITFORM.Pages("Results").Controls("sumEl").Value = (scoreA)
EDITFORM.Pages("Results").Controls("scoreB").Value = (scoreB)
EDITFORM.Pages("Results").Controls("calc").Value = (calc)
End Sub
You can use Execute, ExecuteGlobal and Eval to dynamically form variable names for expressions to execute:
Execute "el" & Counter & " = 42"
In general, Eval/Execute/ExecuteGlobal are dangerous, inefficient, and
unnecessary. There are exception (e.g. library/module import, (quick and dirty)
calculations based on user input), but I doubt that your (real) problem can't be
solved without such Voodoo.
Compare:
Dim el1, el2, el3
el1 = "a"
el2 = "x"
el3 = "b"
WScript.Echo "el:", el1, el2, el3
Dim c1, c2, c3
Dim i
For i = 1 to 3
If "x" = Eval("el" & i) Then
Execute "el" & i & " = 0"
Execute "c" & i & " = 0"
Else
Execute "c" & i & " = 1"
End If
Next
WScript.Echo "el:", el1, el2, el3
WScript.Echo " c:", c1, c2, c3
output:
el: a x b
el: a 0 b
c: 1 0 1
(idea stolen from Helen)
With:
Dim sEL : sEL = "a x b"
Dim oWAN : Set oWAN = WScript.Arguments.Named
If oWAN.Exists("el") Then sEL = oWAN("el")
Dim aEL : aEL = Split(sEL)
WScript.Echo "aEL:", Join(aEL)
ReDim aC(UBound(aEL))
Dim i
For i = 0 to UBound(aEL)
If "x" = aEL(i) Then
aEL(i) = 0
aC(i) = 0
Else
aC(i) = 1
End If
Next
WScript.Echo "aEL:", Join(aEL)
WScript.Echo " aC:", Join(aC)
output (no arg):
aEL: a x b
aEL: a 0 b
aC: 1 0 1
output (arg: /el:"a a a x b x x c x"):
aEL: a a a x b x x c x
aEL: a a a 0 b 0 0 c 0
aC: 1 1 1 0 1 0 0 1 0
I think, you'll agree that using a collection/array gives you as much flexibility
as the eval/execute approach.
Related
I'm doing a school project in Visual Basic (using visual studio 2015) and i'm kinda stuck.
My goal is to create a lottery, where player chooses 6 numbers from checkboxes, then he generates six random numbers (1 - 49) and finally, those two sets should be compared and needed result is the number of correctly guessed numbers.
I have both results (guessed numbers, generated numbers) saved in two different labels.
The checkboxes itself are genereted like this:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
lev = 20
tt = 0
For j = 1 To 50
tt = tt + 1
n = n + 1
box(j) = New CheckBox
box(j).Name = "box(" & Str(j) & ")"
If n = 11 Then lev = lev + 110 : n = 1 : tt = 1
box(j).Left = lev
box(j).Parent = Me
box(j).Top = tt * 20
box(j).Tag = j
box(j).Text = j
box(j).Visible = True
Next
box(50).Enabled = False
End Sub
First label (guessed numbers) is filled this way (i'm not posting whole code)
For j = 1 To 50
If box(j).Checked = True Then Label9.Text = Label9.Text + " " + box(j).Text
Next
and the second one (generated numbers) like this:
Do
rn = rg.Next(1, 50)
If Not r.Contains(rn) Then
r.Add(rn)
End If
Loop Until r.Count = 6
Label1.Text = r(0).ToString + " " + r(1).ToString + " " + r(2).ToString + " " + r(3).ToString + " " + r(4).ToString + " " + r(5).ToString
any idea how to compare numbers stored in those labels and get the result (number of correctly guessed numbers).
thanks in advance
You can compare numbers in the labels by splitting the Text properties of the labels into arrays of strings and converting them to integer arrays. First though there is a tiny problem with your code that adds the guessed numbers to the label.
For j = 1 To 50
If box(j).Checked = True Then Label9.Text = Label9.Text + " " + box(j).Text
Next
The " " should be moved to the end of the line because at the moment, the label will always start with a space and that messes with the function below. So you should have -
For j = 1 To 50
If box(j).Checked = True Then Label9.Text = Label9.Text + box(j).Text + " "
Next
Ok. The function below splits the two text labels into their own array and loops through the guesses and checks if any number is contained in the generated numbers. It then returns the number of matches.
Private Function ComparePicks() As Integer
Dim numbersMatched As Integer
Dim picks(5) As Integer
Dim generatedNumbers(5) As Integer
For i As Integer = 0 To 5
picks(i) = CInt(Split(Label9.Text, " "c)(i))
Next
For i As Integer = 0 To 5
generatedNumbers(i) = CInt(Split(Label1.Text, " "c)(i))
Next
For i As Integer = 0 To 5
If generatedNumbers.Contains(picks(i)) Then
numbersMatched += 1
End If
Next
Return numbersMatched
End Function
I am currently programming a sheet which visualizes data sets in graphs. Because the user of this sheet will not need all the graphs, I would like to let them choose the ones needed through a UserForm. Since the amount of data sets is variable, the UserForm will have the same amount of checkboxes as there are datasets.
The Userform code is as follows.
Private Sub UserForm_Initialize()
Dim chkBoxA As MSForms.CheckBox
Dim chkBoxB As MSForms.CheckBox
Dim lblBox As MSForms.Label
Dim cnt As Control
Amount = Sheet4.Range("C4").Value 'Amount of datasets
For i = 1 To Amount
Set lblBox = Me.Controls.Add("Forms.label.1", "Label" & i)
lblBox.Caption = "Set" & i
lblBox.Left = 5
lblBox.Top = 8 + ((i - 1) * 40)
Set chkBoxA = Me.Controls.Add("Forms.CheckBox.1", "A" & i)
chkBoxA.Caption = "Graph a"
chkBoxA.Left = 55
chkBoxA.Top = 5 + ((i - 1) * 40)
Set chkBoxB = Me.Controls.Add("Forms.CheckBox.1", "B" & i)
chkBoxB.Caption = "Graph b"
chkBoxB.Left = 55
chkBoxB.Top = 20 + ((i - 1) * 40)
Next
CommandButton1.Left = 20
CommandButton1.Top = 40 + ((Amount - 1) * 40)
CommandButton1.TabIndex = Amount * 3 + 1
Me.Height = 220
Me.ScrollBars = fmScrollBarsVertical
Me.ScrollWidth = Me.InsideWidth * 9
For Each cnt In Me.Controls
If cnt.Top + cnt.Height > Me.ScrollHeight Then
Me.ScrollHeight = cnt.Top + cnt.Height + 5
End If
Next
End Sub
When the UserForm is filled in (graphs are chosen by clicking on the options), the user will press CommandButton1. An event should then be run to show the correct graph, but for the simplicity I am first testing if a MsgBox will show up. Unfortunately the MsgBox does not show up.
Private Sub CommandButton1_Click()
'Will fix this with a loop
If A1 = True Then
MsgBox ("TestA1")
End If
If B1 = True then
MsgBox ("TestB1")
End If
If A2 = True then
MsgBox ("TestA2")
End If
Unload Me
End Sub
I am stuck on this part. The checkboxes do show up on the UserForm and they are clickable, but the commandbutton only shuts down the sub (Unload Me). I would like to see the MsgBox show up when I select the corresponding option and click the commandbutton. Any help on getting this to work is appreciated!
You are referencing 'A1' in the sub, but that variable does not exitst at compile time, because you add them dynamically. What you need to do is loop the controls, to check the names. Best practice is to put the checkboxes in a frame, to be able to group them.
Add a frame to the userform and name it 'checkboxframe'
And then instead of:
For i = 1 To Amount
Set lblBox = Me.Controls.Add("Forms.label.1", "Label" & i)
lblBox.Caption = "Set" & i
lblBox.Left = 5
lblBox.Top = 8 + ((i - 1) * 40)
Set chkBoxA = Me.Controls.Add("Forms.CheckBox.1", "A" & i)
chkBoxA.Caption = "Graph a"
chkBoxA.Left = 55
chkBoxA.Top = 5 + ((i - 1) * 40)
Set chkBoxB = Me.Controls.Add("Forms.CheckBox.1", "B" & i)
chkBoxB.Caption = "Graph b"
chkBoxB.Left = 55
chkBoxB.Top = 20 + ((i - 1) * 40)
Next
you would need to do:
With Me.checkboxframe
For i = 1 To Amount
Set lblBox = .Controls.Add("Forms.label.1", "Label" & i)
lblBox.Caption = "Set" & i
lblBox.Left = 5
lblBox.Top = 8 + ((i - 1) * 40)
Set chkBoxA = .Controls.Add("Forms.CheckBox.1", "A" & i)
chkBoxA.Caption = "Graph a"
chkBoxA.Left = 55
chkBoxA.Top = 5 + ((i - 1) * 40)
Set chkBoxB = .Controls.Add("Forms.CheckBox.1", "B" & i)
chkBoxB.Caption = "Graph b"
chkBoxB.Left = 55
chkBoxB.Top = 20 + ((i - 1) * 40)
Next
End With
And to add the checkboxes to the frame, use something like:
For Each ctr In UserForm1.frame("checkboxframe").Controls
If TypeName(ctr) = "CheckBox" Then
If ctr.Value = True Then
'do something usefull here
msgbox ctr.name
End If
End If
Next ctr
The reason nothing appears is because there is no object "A1" manually defined as a variable.
To get the value of the box you Dynamically named "A1" you would have to refer to it as such:
If Me.Controls.Item("A1").Value = True then
Hope this helps!
I have a textbox on a form where the user types some text. Each letter is assigned a different value like a = 1, b = 2, c = 3 and so forth. For example, if the user types "aa bb ccc" the output on a label should be like:
aa = 2
bb = 4
dd = 6
Total value is (12)
I was able to get the total value by looping through the textbox string, but how do I display the total for each word. This is what I have so far:
For letter_counter = 1 To word_length
letter = Mid(txtBox1.Text, letter_counter, 1)
If letter.ToUpper = "A" Then
letter_value = 1
End If
If letter.ToUpper = "B" Then
letter_value = 2
End If
If letter.ToUpper = "C" Then
letter_value = 3
End If
If letter.ToUpper = "D" Then
letter_value = 4
End If
If letter.ToUpper = "E" Then
letter_value = 5
End If
If letter.ToUpper = " " Then
letter_value = 0
End If
totalletter = totalletter + letter_value
Label1.Text = Label1.Text & letter_value & " "
txtBox2.Text = txtBox2.Text & letter_value & " "
Next letter_counter
This simple little routine should do the trick:
Private Sub CountLetters(Input As String)
Label1.Text = ""
Dim total As Integer = 0
Dim dicLetters As New Dictionary(Of Char, Integer)
dicLetters.Add("a"c, 1)
dicLetters.Add("b"c, 5)
dicLetters.Add("c"c, 7)
For Each word As String In Input.Split
Dim wordtotal As Integer = 0
For Each c As Char In word
wordtotal += dicLetters(Char.ToLower(c))
Next
total += wordtotal
'Display word totals here
Label1.Text += word.PadRight(12) + "=" + wordtotal.ToString.PadLeft(5) + vbNewLine
Next
'Display total here
Label1.Text += "Total".PadRight(12) + "=" + total.ToString.PadLeft(5)
End Sub
This should give you an idea:
Dim listOfWordValues As New List(Of Integer)
For letter_counter = 1 To word_length
letter = Mid(txtBox1.Text, letter_counter, 1)
If letter = " " Then
totalletter= totalletter + letter_value
listOfWordValues.Add(letter_value)
letter_value = 0
Else
letter_value += Asc(letter.ToUpper) - 64
End If
Next letter_counter
totalletter = totalletter + letter_value
If Not txtBox1.Text.EndsWith(" ") Then listOfWordValues.Add(letter_value)
txtBox2.Text = txtBox2.Text & string.Join(", ", listOFWordValues);
You can try something like this. Assuming txtBox1 is the string the user enters and " " (space) is the word delimiter:
Dim words As String() = txtBox1.Text.Split(New Char() {" "}, StringSplitOptions.RemoveEmptyEntries)
Dim totalValue As Integer = 0
Dim wordValue As Integer = 0
For Each word As String In words
wordValue = 0
For letter_counter = 1 To word.Length
Dim letter As String = Mid(txtBox1.Text, letter_counter, 1)
Select letter.ToUpper()
Case "A":
wordValue = wordValue + 1
Case "B":
wordValue = wordValue + 2
' And so on
End Select
Next
totalValue = toalValue + wordValue
Next
The above code first takes the entered text from the user and splits it on " " (space).
Next it sets two variables - one for the total value and one for the individual word values, and initializes them to 0.
The outer loop goes through each word in the array from the Split performed on the user entered text. At the start of this loop, it resets the wordValue counter to 0.
The inner loop goes through the current word, and totals up the values of the letter via a Select statement.
Once the inner loop exits, the total value for that word is added to the running totalValue, and the next word is evaluated.
At the end of these two loops you will have calculated the values for each word as well as the total for all the worlds.
The only thing not included in my sample is updating your label(s).
Try this ..
Dim s As String = TextBox1.Text
Dim c As String = "ABCDE"
Dim s0 As String
Dim totalletter As Integer
For x As Integer = 0 To s.Length - 1
s0 = s.Substring(x, 1).ToUpper
If c.Contains(s0) Then
totalletter += c.IndexOf(s0) + 1
End If
Next
MsgBox(totalletter)
I would solve this problem using a dictionary that maps each letter to a number.
Private Shared ReadOnly LetterValues As Dictionary(Of Char, Integer) = GetValues()
Private Shared Function GetValues() As IEnumerable(Of KeyValuePair(Of Char, Integer))
Dim values As New Dictionary(Of Char, Integer)
Dim value As Integer = 0
For Each letter As Char In "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
value += 1
values.Add(letter, value)
Next
Return values
End Function
Public Function CalculateValue(input As String) As Integer
Dim sum As Integer = 0
For Each letter As Char In input.ToUpperInvariant()
If LetterValues.ContainsKey(letter) Then
sum += LetterValues.Item(letter)
End If
Next
Return sum
End Function
Usage example:
Dim sum As Integer = 0
For Each segment As String In "aa bb ccc".Split()
Dim value = CalculateValue(segment)
Console.WriteLine("{0} = {1}", segment, value)
sum += value
Next
Console.WriteLine("Total value is {0}", sum)
' Output
' aa = 2
' bb = 4
' ccc = 9
' Total value is 15
Here is my entire code and I will explain it and what I want to add.
The first function is calling two other functions.
The second function is used to calculate JMBG, which is unique number of citizen in my country. The third one is calculating PIB, which is registered number for companies.
Those two functions are OK and they don't need to be moved or anything like that.
We need to change this first function. As you can see, in the first function I am checking whether the length of the input string is OK. If the length is 13 numbers I call JMBG and if it is 8 I call PIB function. That is OK.
But I must check other types of validation in this first function. As I said, my Excel cell contains 13 numbers or 8 numbers. I want to make some rules in this first function that will tell me if my cell is filled with anything else except those 8 numbers or 13, then send me msg telling me that there is error in the cell and those 2 other functions then won't be called. As you can see, I need validation.
Example: Cell A1: 1234567891234...there is 13 numbers and JMBG will be called
08058808...there is 8 numbers and PIB will be called
1234567890123aSdf~...error because small and big letters and other characters are in the field.
As sum of all this, I need for 8 numbers to call PIB, for 13 numbers to call JMBG and for anything else except that to send me error.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function ProvjeraID(ID As String) As String
If Len(ID) = 13 Then
ProvjeraID = Provjeri_JMBG(ID)
'Exit Function
ElseIf Len(ID) = 8 Then
ProvjeraID = ProvjeriPIB(ID)
'Exit Function
Else
ProvjeraID = "Duzina je razlicita od 8 i od 13"
'Exit Function
End If
End Function
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function Provjeri_JMBG(JMBG As String) As String
' Funkcija vraca tekst sa opisom ispravnosti JMBG
' Primijeniti na radnom listu uz pomoc komande: =Proveri_JMBG(adresa)
' Inicijalizacija promenljivih koje se koriste prilikom izrade koda
Dim duzina As Integer, zbir As Integer
Dim cifra(1 To 13) As Integer
Dim dan As Integer, mesec As Integer, godina As String
' Inicijalizacija konstanti
Const ERR_dan = "GREŠKA: podatak o datumu neispravan!"
Const ERR_mesec = "GREŠKA: podatak o mesecu neispravan!"
Const ERR_godina = "GREŠKA: podatak o godini neispravan!"
Const ERR_duzina = "GREŠKA: dužina razlicita od 13!"
Const ERR_kont = "GREŠKA: neispravan kontrolni broj!"
Const OK_JMBG = "JMBG je ispravan"
' Preuzimanje ulaznih vrednosti sa kojima ce se vrsiti operacije
duzina = Len(JMBG)
dan = Int(Left(JMBG, 2))
mesec = Int(Mid$(JMBG, 3, 2))
godina = Mid$(JMBG, 5, 3)
' Provjera dužine JMBG
If (duzina <> 13) Then
Provjeri_JMBG = "GREŠKA: dužina razlicita od 13!"
Exit Function
End If
' Provjera datuma
If dan < 1 Then
Provjeri_JMBG = "GREŠKA: podatak o datumu neispravan!"
Exit Function
End If
' Provjera mjeseca i dana u mjesecu
Select Case mesec
Case 1, 3, 5, 7, 8, 10, 12
If dan > 31 Then
Provjeri_JMBG = "GREŠKA: podatak o datumu neispravan!"
Exit Function
End If
Case 4, 6, 9, 11
If dan > 30 Then
Provjeri_JMBG = "GREŠKA: podatak o datumu neispravan!"
Exit Function
End If
Case 2
If ((godina Mod 4 = 0) And dan > 29) Or _
((godina Mod 4 <> 0) And dan > 28) Then
Provjeri_JMBG = "GREŠKA: podatak o datumu neispravan!"
Exit Function
End If
Case Else
Provjeri_JMBG = "GREŠKA: podatak o mesecu neispravan!"
Exit Function
End Select
' Provjera godine: ispravne su od 1899 do tekuce godine
If (godina > Right(Str(Year(Now)), 3)) And (godina < "899") Then
Provjeri_JMBG = "GREŠKA: podatak o godini neispravan!"
Exit Function
End If
' Provjera kontrolnog broja
For i = 1 To 13
cifra(i) = Int(Mid$(JMBG, i, 1))
Next i
zbir = cifra(13) + cifra(1) * 7 + cifra(2) * 6
zbir = zbir + cifra(3) * 5 + cifra(4) * 4
zbir = zbir + cifra(5) * 3 + cifra(6) * 2
zbir = zbir + cifra(7) * 7 + cifra(8) * 6
zbir = zbir + cifra(9) * 5 + cifra(10) * 4
zbir = zbir + cifra(11) * 3 + cifra(12) * 2
If (zbir Mod 11) <> 0 Then
Provjeri_JMBG = "GREŠKA: neispravan kontrolni broj!"
Else
Provjeri_JMBG = "JMBG je ispravan"
End If
End Function
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Function ProvjeriPIB(PIB As String)
Dim c0 As Integer
Dim c1 As Integer
Dim c2 As Integer
Dim c3 As Integer
Dim c4 As Integer
Dim c5 As Integer
Dim c6 As Integer
Dim c7 As Integer
Dim c8 As Integer
Dim zadnji As String
zadnji = Right(PIB, 1)
PIB = Left(PIB, 8)
If Len(PIB) <> 8 Then
ProvjeriPIB = "PIB je OK"
Else
c8 = (CInt(Mid(PIB, 1, 1)) + 10) Mod 10
If c8 = 0 Then
c8 = 10
End If
c8 = (c8 * 2) Mod 11
c7 = (CInt(Mid(PIB, 2, 1)) + c8) Mod 10
If c7 = 0 Then
c7 = 10
End If
c7 = (c7 * 2) Mod 11
c6 = (CInt(Mid(PIB, 3, 1)) + c7) Mod 10
If c6 = 0 Then
c6 = 10
End If
c6 = (c6 * 2) Mod 11
c5 = (CInt(Mid(PIB, 4, 1)) + c6) Mod 10
If c5 = 0 Then
c5 = 10
End If
c5 = (c5 * 2) Mod 11
c4 = (CInt(Mid(PIB, 5, 1)) + c5) Mod 10
If c4 = 0 Then
c4 = 10
End If
c4 = (c4 * 2) Mod 11
c3 = (CInt(Mid(PIB, 6, 1)) + c4) Mod 10
If c3 = 0 Then
c3 = 10
End If
c3 = (c3 * 2) Mod 11
c2 = (CInt(Mid(PIB, 7, 1)) + c3) Mod 10
If c2 = 0 Then
c2 = 10
End If
c2 = (c2 * 2) Mod 11
c1 = (CInt(Mid(PIB, 8, 1)) + c2) Mod 10
If c1 = 0 Then
c1 = 10
End If
c1 = (c1 * 2) Mod 11
c0 = (11 - c1) Mod 10
If c0 <> zadnji Then
ProvjeriPIB = "PIB je OK"
Else
ProvjeriPIB = "PIB nije OK"
End If
'return(pib || to_char(c0));
End If
End Function
This solution is based on regex from Scripting library. I have used 3 objects, but code definitely be trimmed to use just one object to check for all three conditions that you required. Since you wanted information about the text that you are inserting I have merely used 3 different regex rules.
Option Explicit
Sub TextNature()
Dim str As String
Dim strMsg As String
Dim objRegEx1 As Object, objRegEx2 As Object
Dim objRegEx3 As Object
str = Sheets(1).Range("A2").Value
'--check length
If Len(str) <> 13 Then
Exit Sub
strMsg = "Too lengthy...limit should be 13"
End If
Set objRegEx1 = CreateObject("VBScript.RegExp")
Set objRegEx2 = CreateObject("VBScript.RegExp")
Set objRegEx3 = CreateObject("VBScript.RegExp")
objRegEx1.IgnoreCase = False
objRegEx1.Global = True
objRegEx2.IgnoreCase = False
objRegEx2.Global = True
objRegEx3.IgnoreCase = False
objRegEx3.Global = True
objRegEx1.Pattern = "^\d+$" '-- only numbers
objRegEx2.Pattern = "^[a-zA-Z]+$" '-- only lower upper letters
objRegEx3.Pattern = "^[a-zA-Z\d]+$" '-- numbers and lower upper letters
If objRegEx1.Test(str) Then
strMsg = "Contain only numbers"
ElseIf objRegEx2.Test(str) Then
strMsg = "Contain only lower upper letters"
ElseIf objRegEx3.Test(str) Then
strMsg = "Contain numbers and lower upper letters"
Else
strMsg = "not satisfying"
End If
End Sub
Results : used the sub as a function:
OP requests for a function, and length limit to be 8:
Option Explicit
Function TextNature(ByRef rng As Range) As String
Dim str As String, strMsg As String
Dim objRegEx1 As Object, objRegEx2 As Object, objRegEx3 As Object
str = rng.Value
If Len(str) <> 8 Then
TextNature = "Limit is not correct. It should be 8."
Exit Function
End If
Set objRegEx1 = CreateObject("VBScript.RegExp")
Set objRegEx2 = CreateObject("VBScript.RegExp")
Set objRegEx3 = CreateObject("VBScript.RegExp")
objRegEx1.IgnoreCase = False
objRegEx1.Global = True
objRegEx2.IgnoreCase = False
objRegEx2.Global = True
objRegEx3.IgnoreCase = False
objRegEx3.Global = True
objRegEx1.Pattern = "^\d+$" '-- only numbers
objRegEx2.Pattern = "^[a-zA-Z]+$" '-- only lower/upper letters
objRegEx3.Pattern = "^[a-zA-Z\d]+$" '-- numbers and lower/upper letters
If objRegEx1.Test(str) Then
strMsg = "Contain only numbers"
ElseIf objRegEx2.Test(str) Then
strMsg = "Contain only lower upper letters"
ElseIf objRegEx3.Test(str) Then
strMsg = "Contain numbers and lower upper letters"
Else
strMsg = "Not Satisfying"
End If
TextNature = strMsg
End Function
Something like this should help - you can define the criteria in the select statement. It's a UDF so put the code into a module and enter =checkcell(A1) into a cell.
Public Function CheckCell(ByVal CheckRange As Range) As String
Dim strChr As String, rngCheck As Range
Dim i As Integer, NPC As Integer, UC As Integer, LC As Integer, OT As Integer
Set rngCheck = Range("A1")
For i = 1 To rngCheck.Characters.Count
strChr = rngCheck.Characters(i, 1).Text
Select Case Asc(strChr)
Case 0 To 31
NPC = NPC + 1
Case 96 To 122
LC = LC + 1
Case 65 To 90
UC = UC + 1
Case Else
OT = OT + 1
End Select
Next
CheckCell = "NPC: " & NPC & " UC: " & UC & " LC: " & LC & " Others: " & OT
End Function
In case formula-based solution is OK - use this ARRAY formula (assuming string for checking is in A1):
=IF(OR(NOT(ISERROR(SEARCH(ROW($1:$10)-1,A1)))),"Has digits","No digits")
and press CTRL+SHIFT+ENTER instead of usual ENTER - this will define an ARRAY formula and will result in {} brackets around it (but do NOT type them manually!).
String length and any other chars do not matter. Hope that was helpful)
Replace your first function with something like the following, and call it in a cell using =ProvjeraID2(A1) to evaluate the contents of cell A1:
Function ProvjeraID2(oRng As Range) As String
Dim sRet As String
If Not oRng Is Nothing Then
If IsNumeric(oRng.Value) Then
If Len(oRng.Value) = 13 Then
sRet = Provjeri_JMBG(CStr(oRng.Value))
ElseIf Len(oRng.Value) = 8 Then
sRet = ProvjeriPIB(CStr(oRng.Value))
Else
sRet = "Numeric but wrong length (" & Len(oRng.Value) & ")"
End If
Else
sRet = "Not a number"
End If
End If
ProvjeraID2 = sRet
End Function
This function goes through all integers and picks out binary values with only five ones and writes them to the spreadsheet.
To run this For x = 1 To 134217728 would take 2.5 days!!!! Help!
How could I speed this up?
Function D2B(ByVal n As Long) As String
n = Abs(n)
D2B = ""
Do While n > 0
If n = (n \ 2) * 2 Then
D2B = "0" & D2B
Else
D2B = "1" & D2B
n = n - 1
End If
n = n / 2
Loop
End Function
Sub mixtures()
Dim x As Long
Dim y As Integer
Dim fill As String
Dim mask As String
Dim RowOffset As Integer
Dim t As Date
t = Now
fill = ""
For x = 1 To 134217728
mask = Right(fill & CStr(D2B(x)), Len(fill & CStr(D2B(x))))
Debug.Print mask
If x > 100000 Then Exit For
If Len(mask) - Len(WorksheetFunction.Substitute(mask, "1", "")) = 5 Then _
RowOffset = RowOffset + 1
For y = 1 To Len(mask)
If Len(mask) - Len(WorksheetFunction.Substitute(mask, "1", "")) = 5 Then _
Range("mix").Offset(RowOffset).Cells(y) = Mid(mask, y, 1)
Next
Next
Debug.Print DateDiff("s", Now, t)
End Sub
By first sight guess, I think the problem lies in the fact that you do that cell by cell, which causes many read and write accesses.
You should do it range by range, like
vArr = Range("A1:C1000").Value
' it is array now, do something here effeciently
Range("A1:C1000").Value = vArr
You want find all 28bit numbers with 5 1s
There are 28*27*26*25*24/5/4/3/2=98280 such numbers
The following code took ~10 seconds on my PC:
lineno = 1
For b1 = 0 To 27
For b2 = b1 + 1 To 27
For b3 = b2 + 1 To 27
For b4 = b3 + 1 To 27
For b5 = b4 + 1 To 27
Cells(lineno, 1) = 2 ^ b1 + 2 ^ b2 + 2 ^ b3 + 2 ^ b4 + 2 ^ b5
lineno = lineno + 1
Next
Next
Next
Next
Next
mask = Right(fill & CStr(D2B(x)), Len(fill & CStr(D2B(x))))
The above line of code does the same thing (CStr(D2B(x))) twice.
Store the result of CStr(D2B(x)) in a variable & use that variable in the above line of code.
I've got 2 suggestions:
Get rid of the substitution command by counting the ones/zeroes in D2B and return an empty string if the count does not equal 5
Write these pre-filtered bitstrings to an array first and copy the array directly to the cells when finished.
Something like
ws.Range(ws.cells(1, 1), ws.cells(UBound(dstArr, 1) + 1, UBound(dstArr, 2) + 1)) = dstArr
The array-copy-trick greatly improves performance!