How to concatenate string of numbers in VBA - vba

[Updated] Thank you all for your guidance.
I have modified the code as below.
But the macro did not run, nor generated any errors.
I did not want to double quote each item in the variable "columns" either.
Thank you very much for any helps.
Thanks!
My code is as below:
I would like to generate 35 random columns index if the sample size is 80,
and if the sample size is 50 then generate 25 and if the sample size is 32 then generate 20 and if the sample size is 20 then generate 15.
And then created a average and sd columns for that.
`
Sub randomize1()
Dim wb As Workbook
Dim average As Range
Dim sd As Range
Dim columns As String
Dim values As Variant
Set wb = ActiveWorkbook
With wb
For i = 2 To 1730
Set average = Worksheets("CT").Range("CY2:CY1730")
Set sd = Worksheets("CT").Range("CZ2:CZ1730")
If WorksheetFunction.CountA(".Cells(i,11):.Cells(i,91)") = 80 Then
columns = "17 21 31 32 2 18 22 7 9 20 23 6 27 10 26 8 29 3 1 13 5 24 35 15 28 11 25 14 16 4 12 34 19 30 33"
values = Strings.Split(columns, " ")
.Cells(i, average).Value = Application.average(Range(i, columns).Offset(, 10).Select)
.Cells(i, sd).Value = Application.WorksheetFunction.StDev(Range(i, columns).Offset(, 10).Select)
ElseIf WorksheetFunction.CountA(".Cells(i,11):.Cells(i,91)") = 50 Then
columns = "1 22 17 5 18 8 20 9 10 6 25 14 13 7 2 3 19 16 4 12 15 11 24 23 21"
values = Strings.Split(columns, " ")
.Cells(i, average).Value = Application.average(Range(i, columns).Offset(, 10).Select)
.Cells(i, sd).Value = Application.WorksheetFunction.StDev(Range(i, columns).Offset(, 10).Select)
ElseIf WorksheetFunction.CountA(".Cells(i,11):.Cells(i,91)") = 32 Then
columns = "14 2 3 16 19 11 20 1 13 18 6 9 17 8 4 5 10 15 12 7"
values = Strings.Split(columns, " ")
.Cells(i, average).Value = Application.average(Range(i, columns).Offset(, 10).Select)
.Cells(i, sd).Value = Application.WorksheetFunction.StDev(Range(i, columns).Offset(, 10).Select)
ElseIf WorksheetFunction.CountA(".Cells(i,11):.Cells(i,91)") = 20 Then
columns = "13 8 7 2 1 12 11 6 14 15 3 10 4 5 9"
values = Strings.Split(columns, " ")
.Cells(i, average).Value = Application.average(Range(i, columns).Offset(, 10).Select)
.Cells(i, sd).Value = Application.WorksheetFunction.StDev(Range(i, columns).Offset(, 10).Select)
End If
Next i
End With
End Sub
``

Dim columns As String
columns = (17 21 31 32 2 18 22 7 9 20 23 6 27 10 26 8 29 3 1 13 5 24 35 15 28 11 25 14 16 4 12 34 19 30 33)`
The expression (17 21 31 ...) isn't a String literal. In VBA (as in many other languages), string literals are delimited with " double quotes, not parentheses.
The compile error is because the expression (integerLiteral integerLiteral integerLiteral ...) can't be evaluated, it means nothing to the compiler. I'm guessing it's choking at the 21 token, especting a , list separator or a ) closing parenthesis, since columns = (17) would be a valid expression.
This would compile:
Dim columns As String
columns = "17 21 31 32 2 18 22 7 9 20 23 6 27 10 26 8 29 3 1 13 5 24 35 15 28 11 25 14 16 4 12 34 19 30 33"
...but then, it's not an array of values, it's just a string. There's a Split function in the VBA.Strings module that you can use to split a string into an array, given a separator - like, a space character:
Dim values As Variant
values = Strings.Split(columns, " ")
That gives you an array of Variant/String items that you can later iterate and convert to Long values if you need to.
in R, I can use c(1,2,3) if I am going to define an object,column index as (1,2,3)
That statement is extremely unclear and hard to understand for an audience of VBA experts - how does (1,2,3) map to object, column index? Is 1 an "object"? Is 2,3 a column index? Or did you mean object, column, index? Doesn't make much sense, even to someone that knows what a value tuple is. Sounds like R lets you define value tuples inline. That's cool, but there's no concept of a value tuple in VBA, and no inline objects.
If you need an object, you add a class module to your project, and define it - at a bare minimum, using public fields to define its public interface:
'Class1
Option Explicit
Public Value As Long
Public Column As Long
Public Index As Long
Then you can create new instances of this class using New: Set foo = New Class1, and then assign and read its properties, invoke its methods: Debug.Print foo.Index, foo.Column, foo.Value.
About this:
columns = (num & num& ... &num)
Note that some tokens have multiple syntactical purposes; & is one such token. When surrounded with spaces as in num & num, it's the string concatenation operator.
But without a leading space, as in num&, it's a type hint character that tells the compiler num is a Long integer, hence the syntax error; num& num is just as illegal as (42 17).

In VBA, everything between quotes "" is a String.
If you simply need to create a string containing those numbers, you can do this:
Dim columns As String
columns = "17 21 31 32 2 18 22 7 9 20 23 6 27 10 26 8 29 3 1 13 5 24 35 15 28 11 25 14 16 4 12 34 19 30 33"
If you need to process those numbers individually, you need an array, which you can initialize following multiple ways.
Classic Way
Declare your Array with a fixed size and then assingn it the values you want:
Dim myArray(1 to 34) As String 'Goes from 1 to 34
myArray(1) = 17 'First element
myArray(2) = 21 'Second element
'etc...
Use Array() Command
You can use the useful command Array() to initialize an Array with an inline command:
Dim myArray() As Variant
myArray = Array("17", "21", "31", "32", "2")
Note that your variable must be Variant to use this solution.
Hope this helps.

Related

Make all combinations two digit from single string

I have a Textbox1.Text like that: 1784397425
How can I get the 2 combinations or 1 combination out of here?
this is a model:
Like 17 78 84 43 39 97 74 42 25 1 7 8 4 3 9 7 4 2 5
That's another model:
It can also be like this: (first digit 1, we take 1 by 7, then 1 by 8 and so on.)
17 18 14 13 19 17 14 15
and so on...
78 74 73 79 77 74 72 75
84 83 89 87 84 82 85
43 49 47 44 42 45
39 37 34 32 35
and so on...
any such model algorithm would help me a lot either of these two.
Code 3: unfortunately this doesn't work very well.
Since they're strings to begin with you can use.
Left(str,n) n is the number of left characters you want.
Right(str,n) n is the number of Right characters you want.
Mid(str,n,nn) Mid is like substring where n is the starting char, and nn is the number from start you want from the str.
Dim line1 = "47"
Dim d1 As String = Left(line1,1) 'this is 4
Dim d2 As String = Right(line1,1) 'this is 7
now the math
Dim a1 As Integer = Int(d1) + Int(d2)
or
Dim a1 As Integer = CInt(d1) + CInt(d2)
As suggested by Andrew, use Substring to extract the portion of the string that you want.
I've wrapped that in an iterator that returns all the substrings:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
TextBox2.Text = String.Join(" ", Substrings(TextBox1.Text, 2))
End Sub
Public Iterator Function Substrings(ByVal source As String, ByVal length As Integer) As IEnumerable(Of String)
If length >= 1 AndAlso length <= source.Length Then
For i As Integer = 0 To (source.Length - length)
Yield source.Substring(i, length)
Next
End If
End Function
If you want the user to set the value of the length parameter, then drop a NumericUpDown control on your form and grab the .Value from that.

How to create a partially filled column in pandas

I have a df_trg with, say 10 rows numbered 0-9.
I get from various sources values for an additional column foo which contains only a subset of rows, e.g. S1 has 0-3, 7, 9 and S2 has 4, 6.
I would like to get a data frame with a single new column foo where some rows may remain NaN.
Is there a "nicer" way other than:
df_trg['foo'] = np.nan
for src in sources:
df_trg['foo'][df_trg.index.isin(src.index)] = src
for example, using join or merge?
Let's create the source DataFrame (df), s1 and s2 (Series objects with
updating data) and a list of them (sources):
df = pd.DataFrame(np.arange(1, 51).reshape((5, -1)).T)
s1 = pd.Series([11, 12, 13, 14, 15, 16], index=[0, 1, 2, 3, 7, 9])
s2 = pd.Series([27, 28], index=[4, 6])
sources = [s1, s2]
Start the computation from adding foo column, initially filled with
an empty string:
df = df.assign(foo='')
Then run the following "updating" loop:
for src in sources:
df.foo.update(other=src)
The result is:
0 1 2 3 4 foo
0 1 11 21 31 41 11
1 2 12 22 32 42 12
2 3 13 23 33 43 13
3 4 14 24 34 44 14
4 5 15 25 35 45 27
5 6 16 26 36 46
6 7 17 27 37 47 28
7 8 18 28 38 48 15
8 9 19 29 39 49
9 10 20 30 40 50 16
In my opinion, this solution is (at least a little) nicer than yours and
shorter.
Alternative: Fill foo column initially with NaN, but this time
updating values will be converted to float (side effect of using NaN).

Code if then statement by only using $ utility

How can I code this 'if' conditions in GAMS?
Set j/1*10/
S/1*6/;
Parameter
b(s,j) export this from excel
U(s,j) export from excel
M(s)/1 100,2 250,3 140,4 120,5 132/ export from excel
;
table b(s,j)
1 2 3 4 5 6 7 8 9 10
1 3 40 23 12 9 52 9 14 89 33
2 0 0 42 0 11 32 11 15 3 7
3 10 20 12 9 5 30 14 5 14 5
4 0 0 0 9 0 3 8 0 13 5
5 0 10 11 32 11 0 3 1 12 1
6 12 20 2 9 15 3 14 5 14 5
;
u(s,j)=0;
u(s,j)$(b(s,j))=1;
Variable delta(j); "binary"
After solving a model I got the value of delta ( suppose delta(1)=1, delta(5)=1). Then Set A is
A(j)$(delta.l(j)=1)=Yes; (A={1,5})
I want to calculate parameter R(s) according to the following :
If there is no j in A(j) s.t. j in u(s,j) then R(s)=M(s)
Else if there is a j in A(j) s.t. j in u(s,j) then R(s)=min{b(s,j): j in A(j) , j in u(s,j) }
Then R(1)=3, R(2)=11,R(3)=5, R(4)=120, R(5)=11,R(6)=12.
Is it possible to code this ' if then ' statement only by $ utility?
Thanks
Following on from the comments, I think this should work for you.
(Create a parameter that mimics your variable delta just for demonstration:)
parameter delta(j);
delta('1') = 1;
delta('5') = 1;
With loop and if/else:
Create parameter R(s). Then, looping over s , pick the minimum of b(s,A) across set A where b(s,A) is defined if the sum of b(s,A) is not zero (i.e. if one of the set is non-zero. Else, set R(s) equal to M(s).
Note, the loop is one solution to the issue you were having with mixed dimensions. And the $(b(s,A)) needs to be on the first argument of smin(.), not on the second argument.
parameter R(s);
loop(s,
if (sum(A, b(s,A)) ne 0,
R(s) = smin(A$b(s,A), b(s,A));
else
R(s) = M(s);
);
);
With $ command only (#Lutz in comments):
R(s)$(sum(A, b(s,A)) <> 0) = smin(A$b(s,A), b(s,A));
R(s)$(sum(A, b(s,A)) = 0) = M(s);
Gives:
---- 56 PARAMETER R
1 3.000, 2 11.000, 3 5.000, 4 120.000, 5 11.000, 6 12.000

Why are some of my ranges insane?

I tried parsing a common string depiction of ranges (e.g. 1-9) into actual ranges (e.g. 1 .. 9), but often got weird results when including two digit numbers. For example, 1-10 results in the single value 1 instead of a list of ten values and 11-20 gave me four values (11 10 21 20), half of which aren't even in the expected numerical range:
put get_range_for('1-9');
put get_range_for('1-10');
put get_range_for('11-20');
sub get_range_for ( $string ) {
my ($start, $stop) = $string.split('-');
my #values = ($start .. $stop).flat;
return #values;
}
This prints:
1 2 3 4 5 6 7 8 9
1
11 10 21 20
Instead of the expected:
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
(I figured this out before posting this question, so I have answered below. Feel free to add your own answer if you'd like to elaborate).
The problem is indeed that .split returns Str rather than Int, which the original answer solves. However, I would rather implement my "get_range_for" like this:
sub get_range_for($string) {
Range.new( |$string.split("-")>>.Int )
}
This would return a Range object rather than an Array. But for iteration (which is what you most likely would use this for), this wouldn't make any difference. Also, for larger ranges the other implementation of "get_range_for" could potentially eat a lot of memory because it vivifies the Range into an Array. This doesn't matter much for "3-10", but it would for "1-10000000".
Note that this implementation uses >>.Int to call the Int method on all values returned from the .split, and then slips them as separate parameters with | to Range.new. This will then also bomb should the .split return 1 value (if it couldn't split) or more than 2 values (if multiple hyphens occurred in the string).
The result of split is a Str, so you are accidentally creating a range of strings instead of a range of integers. Try converting $start and $stop to Int before creating the range:
put get_range_for('1-9');
put get_range_for('1-10');
put get_range_for('11-20');
sub get_range_for ( $string ) {
my ($start, $stop) = $string.split('-');
my #values = ($start.Int .. $stop.Int).flat; # Simply added .Int here
return #values;
}
Giving you what you expect:
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20

VBA macro to find if a value in a cell is within the range of the values of two cells

I have 2 sheets. Sheet1 has 3 columns A, B & C with values of range_start_number (A), range_end_number (B) and range_name (C).
Sheet2 has 1 column A with the list of numbers.
How to find which numbers in sheet2 belong to which ranges in sheet1? In addition, also, the number's position within a corrosponding range. The output should be:
'range name'-'position in range'
see below for example data inputs (sheet1 & sheet2) and expected output (in sheet2).
Sheet1 (input)
A B C
10 17 rangeA
17 15 rangeB
30 12 rangeC
8 9 rangeD
11 9 rangeE
36 50 rangeF
40 45 rangeG
31 32 rangeH
5 25 rangeI
25 26 rangeJ
Sheet2 (input)
A
10
11
12
13
14
15
16
17
18
19
22
23
24
25
Sheet2 (output)
A B C D E
10 rangeA-1 rangeI-6 rangeE-2
11 rangeA-2 rangeI-7 rangeE-1
12 rangeA-3 rangeC-19 rangeI-8
13 rangeA-4 rangeC-18 rangeI-9
14 rangeA-5 rangeC-17 rangeI-10
15 rangeA-6 rangeB-3 rangeC-16 rangeI-11
16 rangeA-7 rangeB-2 rangeC-15 rangeI-12
17 rangeA-8 rangeB-1 rangeC-14 rangeI-13
18 rangeC-13 rangeI-14
19 rangeC-12 rangeI-15
22 rangeC-9 rangeI-18
23 rangeC-8 rangeI-19
24 rangeC-7 rangeI-20
25 rangeC-6 rangeI-21 rangeJ-1
The number's position within the range is calculated subtracting the lesser of the ranges' numbers from the input value
Sub RangeSub()
Row = 1
Do While True
Column = 6
If Not IsEmpty(Cells(Row, 5)) Then
rRow = 1
Do While True
If Not IsEmpty(Cells(rRow, 1)) And Not IsEmpty(Cells(rRow, 2)) And Not IsEmpty(Cells(rRow, 3)) Then
If (Cells(rRow, 1).Value <= Cells(Row, 5).Value And Cells(Row, 5).Value <= Cells(rRow, 2).Value) Or (Cells(rRow, 1).Value >= Cells(Row, 5).Value And Cells(Row, 5).Value >= Cells(rRow, 2).Value) Then
Position = Abs(Cells(Row, 5) - Cells(rRow, 1)) + 1
Cells(Row, Column).Value = Cells(rRow, 3).Value + "-" + Str(Position)
Column = Column + 1
End If
Else
Exit Do
End If
rRow = rRow + 1
Loop
Row = Row + 1
Else
Exit Do
End If
Loop
End Sub
The code works with the ranges and input in the same sheet, you could change it to work in different sheets.
A B C D E
10 17 rangeA 10
17 15 rangeB 11
30 12 rangeC 12
8 9 rangeD 13
11 9 rangeE 14
36 50 rangeF 15
40 45 rangeG 16
31 32 rangeH 17
5 25 rangeI 18
25 26 rangeJ 19
22
23
24
25
D is blank.