Table with 4 variable columns - excel

I'm sure this is simple for all of you, but I'm new here. How do I create a formula or code that can output all of the potential scenarios for this type of array below? Basically, max is 60, min is 0, but I'm unsure how to make Excel spit out a table that represents this.

Solution:
Doing it with Excel formulas alone, while theoretically possible, is incredibly computationally demanding and crashed Excel when I was trying to do so. You can do this fairly easy with VBA though.
Create a VBA module, drop these two snippets in, press play, and wait for a few seconds to a minute while the code runs. The code is not the most efficient, but it is probably the simplest algorithm to understand.
Public Sub comb4()
Dim a, b, c, d, n, r, x As Integer
x = 60
a = x
Do While a >= n
b = x - a
Do While b >= n
c = x - a - b
Do While c >= n
d = x - a - b - c
Do While d >= n
If sumToZero(-x, a, b, c, d) Then
r = r + 1
Cells(r, 1).Value = a
Cells(r, 2).Value = b
Cells(r, 3).Value = c
Cells(r, 4).Value = d
End If
d = d - 1
Loop
c = c - 1
Loop
b = b - 1
Loop
a = a - 1
Loop
End Sub
Public Function sumToZero(ParamArray intNums())
For x = LBound(intNums) To UBound(intNums)
y = y + intNums(x)
Next x
If y = 0 Then sumToZero = True
End Function
Code explanation:
x is the max that you defined, while n is the min you defined.
r is a counter which helps us track what row to print to.
Since we always want to count down to 0 for each column's sub-permutations, each of our Do While loops will count from the theoretical maximum value down to 0.
The loop structure is nested so that each time we hit -1 in a column N we instead leave the loop, go into the loop one level higher, and decrease the value in column M by 1. The value in N is reset when we start the next instance of the loop for M.
Between each value change we need to check to see if the result is a valid solution. We pass the (negative) max and all 4 variables we are looping to a function which sums the values and returns true if the variables are equal to the max. When the function is true, we go to the next row in Excel and print the four values.

Related

For Loop Counter in VBA increments by one extra

I have a simple loop counter which I expected to msg me 1,2,3...10. Instead I get 1,3,5,7,9and I can't figure out where this extra increment is coming from. I'm a bit rusty after layoffs so bear with me if this seems simple.
here is my code:
Dim x As Integer
For x = 1 To 10
MsgBox x
x = x + 1
Next x
It is typical that a for loop in most programming languages will automatically increment or iterate through something. In most languages, it's also common to provide a way to specify the increment amount. This is the case in VBA—see VBA For...Next documentation. For example:
For i = 1 to 20 Step 2
'do something
next i
Your code is incrementing x, and For is also incrementing x. There are times when you want to increment the counter within the loop if some special condition is met, but not on every loop.
If you want to manually increment, then you should use a while loop instead.
The Next x line, when referring to an integer (x) in a For loop, will increment x by 1
If you would like to increment by more than one, or increment in a negative direction you could use step at the end of the For line, which provides direction to the Next portion. Example:
For x = 10 to 0 step -1
msgbox x
next x
will result in 11 consecutive msgbox's displaying:
10
9
8
7
6
5
4
3
2
1
0
Depending on how you would like to control X you have different options:
Option 1:
Let the for-loop control everything and use "step".
In this case x will raise by y at each iteration. And you should not alter x within the loop.
Dim y As Integer
y = 1
For x = 1 To 10 step y
MsgBox x
Next x
Option 2:
If X depends on something happening within the loop and you don't want the for loop to alter x you could set step to 0.
For x = 1 To 10 step 0
MsgBox x
If x Mod 2 = 0 Then
x = x + 3
Else
x = x - 1
End If
Next x
Alternativ option
Having a for loop as in option 2 not altering the x would be bad practice. It is not wrong as the code is working just fine but a while loop would be a better solution.
Dim x As Integer
x = 1
Do While x < 10
MsgBox x
If x Mod 2 = 0 Then
x = x + 3
Else
x = x - 1
End If
Loop

array from continuous data?

I have a quite difficult problem that i cant wrap my head around.. hope you can help me!
Lets say my data is in A1:G1 for example:
A1 B1 C1 D1 E1 F1 G1
X X 0 X X X 0
or
Y X X X X Z X
The thing i would need to come up with is how to get array from this data according to the X, BUT if like in example 1 there is 2 times X in the beginning and 3 x next to each other so the array should come out like {2;2;0;3;3;3;0} so i want the array to be 7 long and the array should show the x as number how many are next to each other.
example 2 should come out like {0;4;4;4;4;0;1}
if you can figure this out would really help me alot!
Edit:
Trying to give out better, more bigger picture of what i mean..
if data is :
A B C
1 X X
2 X X
3 X
it should come out as
A B C
1: 2 4 0
2: 0 4 2
3: 1 0 0
or in array {2\4\0;0\4\2;1\0\0}
on B1 and B2 there should be 4 because the formula should count horizontal but also vertical continuum. I tried to use usmanhaqs formula but i was not able to modify it so the count resets on every line.
Real size of the table is 7 times 7 cells.
I will use the array with another array (scoreboard which is also 7 times 7 cells, and has numbers 1, 2 or 3 on every cell) using sumproduct and it will give out the points of that player.
I appreciate your efforts on helping out a newbie learner on vba :)
For a formula solution, I can only come up with one for the special case where you have just X's and zeroes (example 1) so far:
=SUM(IF(A1:G1<>"X",0,INDEX(FREQUENCY(IF(A1:G1="X",COLUMN(A1:G1)),IF(A1:G1<>"X",COLUMN(A1:G1))),N(IF({1},SUBTOTAL(2,OFFSET(A1,0,0,1,COLUMN(A1:G1)))))+1,1)))
entered as an array formula using CtrlShiftEnter
I have wrapped it in a SUM function to demonstrate that it generates an array which can be passed to another function (result: 13) or it can be array-entered across several cells:
You can test this code
Function get_array(r As Range, match_chr As String)
Dim check_val
Dim array_value
array_value = "{"
For i = 1 To r.Count
check_value = r.Item(i)
If (check_value = match_chr) Then
j = i + 1
Do While (j <= r.Count) And (check_value = r.Item(j))
j = j + 1
Loop
array_value = array_value & WorksheetFunction.Rept(j - i & ", ", j - i)
i = j - 1
Else
array_value = array_value & "0, "
End If
Next
array_value = Left(array_value, Len(array_value) - 2) & "}"
get_array = array_value
End Function
You can use it as below
EDIT
find below another function to return an array of values that can be used in the formulae
Function get_number_array(r As Range, match_chr As String)
Dim check_val
Dim array_value
Dim number_array(1 To 50) As Long
For i = 1 To r.Count
check_value = r.Item(i)
If (check_value = match_chr) Then
j = i + 1
Do While (j <= r.Count) And (check_value = r.Item(j))
j = j + 1
Loop
For k = 1 To j - i
number_array(i + k - 1) = j - i
Next k
i = j - 1
Else
number_array(i) = 0
End If
Next
get_number_array = number_array
End Function
You have to use it same as the previous one, but it will return excel array.

Excel for loop to get value from another row

I have an spreadsheet that contains various data. It looks like this:
A A A B B C C C C
a 1 2 3 2 1 4 2 3 2
b 0 2 3 3 0 1 2 3 0
c 6 6 3 0 2 1 0 4 0
etc.
What I want is to add all the Aa's and come up with a Aa total, all the Bb's and come up with a Bb total, all the Ab's etc.
What I want to do is, for every column, check if it is A, B or C. I want to do that because the data may change I might end up with four columns for A, two for B, etc. I know however that a, b and c will stay where they are.
I also don't know the order of A, B and C. There could be two A's followed by two C's and then one B.
My final result will be a table containing all the totals:
Aa Ab Ac
Ba Bb Bc
Ca Cb Cc
Where in the previous example would mean that Aa = 1 + 2 + 3 = 6, Ab = 5, etc.
Something like that.
I think the way to go is for 1-1 (the total of Aa's) is to go through every column in the first row. Check if it is an A. If it is, then get the value of the same column but second row. Add it to the total. When gone through all the columns, show up the total in 1-1.
What I have so far (for A):
Sub getA()
Dim x As Integer
Dim total As Integer
'cols = Find number of columns with data in them
For x = 1 To cols
'cell = cell in Ax
If InStr(1, cellvalue, "a") = 1 Then
'val = value from row 5 in same column
total = total + Val
End If
Next
End Sub
But I don't really know how to proceed with the commented lines.
Finally, another thing I would like to know is how will these values be presented in their respective cells without any extra event being carried (button for example). They should just appear in their cells from the moment someone opens the spreadsheet.
Any help is greatly appreciated.
Thanks.
Just an FYI, this can be done using the SUMPRODUCT formula:
=SUMPRODUCT(($B$1:$J$1=D$9)*($A$2:$A$4=$C10)*$B$2:$J$4)
EDIT
To compare the first letter then use this formula:
=SUMPRODUCT((LEFT($B$1:$J$1,1)=D$9)*($A$2:$A$4=$C10)*$B$2:$J$4)
Are you looking for something like:
Function countletter(strLetter As String) As Double
Dim x As Double, y As Double, xMax As Double, yMax As Double
xMax = Range("A1").CurrentRegion.Columns.Count
yMax = Range("A1").CurrentRegion.Rows.Count
For x = 1 To xMax
For y = 1 To yMax
If Cells(y, x).Value = strLetter Then
countletter = countletter + 1
End If
Next
Next
End Function

Auto calculate average over varying number values row by row

I have an Excel file with several columns in it and many rows. One column, say A has ID numbers. Another column, say G has prices. Column A has repeating ID numbers, however not all numbers repeat the same amount of times. Sometimes just once, other times 2, 3 or several times. Each column G for that row has a unique price.
Basically, I need to average those prices for a given ID in column A. If each ID was repeated the same number of times, this would be quite simple, but because they are not I have to manually do my average calculation for each grouping. Since my spreadsheet has many many rows, this is taking forever.
Here is an example (column H is the average that I am currently calculating manually):
A ... G H
1 1234 3.00 3.50
2 1234 4.00
3 3456 2.25 3.98
4 3456 4.54
5 3456 5.15
11 8890 0.70 0.95
13 8890 1.20
...
So in the above example, the average price for ID# 1234 would be 3.50. Likewise, the average price for ID# 3456 would be 3.98 and for #8890 would be 0.95.
NOTICE how rows are missing between row 5 and 11, and row 12 is missing too? That is because they are filtered out for some other reason. I need to exclude those hidden rows from my calculations and only calculate the average for the rows visible.
Im trying to write a VBA script that will automatically calculate this, then print that average value for each ID in column H.
Here is some code I have considered:
Sub calcAvg()
Dim rng As Range
Set rng = Range("sheet1!A1:A200003")
For Each Val In rng
Count = 0
V = Val.Value '''V is set equal to the value within the range
If Val.Value = V Then
Sum = Sum + G.Value
V = rng.Offset(1, 0) '''go to next row
Count = Count + 1
Else
'''V = Val.Value '''set value in this cell equal to the value in the next cell down.
avg = Sum / Count
H = avg '''Column G gets the avg value.
End If
Next Val
End Sub
I know there are some problems with the above code. Im not too familiar with VBA. Also this would print the avg on the same line everytime. Im not sure how to iterate the entire row.
This seems overly complicated. Its a simple problem in theory, but the missing rows and differing number of ID# repetitions makes it more complex.
If this can be done in an Excel function, that would be even better.
Any thoughts or suggestions would be greatly appreciated. thanks.
If you can add another row to the top of your data (put column Headers in it) its quite simple with a formula.
Formula for C2 is
=IF(A2<>A1,AVERAGEIFS(B:B,A:A,A2),"")
copy this down for all data rows.
This applies for Excel 2007 or later. If using Excel 2003 or earlier, use AVERAGEIF instead, adjusting ranges accordingly
If you can't add a header row, change the first formula (cell C1) to
=AVERAGEIFS(B:B,A:A,A1)
In my way ..
Sub calcAvg()
Dim x, y, i, y2, t, Count, Mount As Integer
Dim Seek0 As String
x = 1 '--> means Col A
y = 1 '--> means start - Row 1
y2 = 7 '--> means end - Row 19
For i = y To y2
If i = y Then
Seek0 = Cells(i, x)
t = i
Count = Cells(i, x + 6)
Mount = 1
Else
If Cells(i, x) <> Seek0 Then
Cells(t, x + 7) = Count / Mount
Count = Cells(i, x + 6)
Mount = 1
t = i
Seek0 = Cells(i, x)
Else
Count = Count + Cells(i, x + 6)
Mount = Mount + 1
End If
End If
Next
End Sub
Hope this helps ..

How to get pack all the nonnull data from a row of null and non-null data into an array using vbscript

I have an excel sheet,which has 144 coulms-(4 columns contains one set).i.e 36 sets i do have. Now how can i take out only the Non-Null data from the 2000X350 Excel sheet matrix data,into an 2D array using VBScript?
Here is an Example sheet:
PID T1 T1SD T1CD T1ST T2 T2SD T2CD T2ST T3 T3SD T3CD T3ST T4 T4SD T4CD T4ST ........
10 a b b t r t k l o
11 p p m d n n n b
.
.
PID on the column number 11 always.Evey set is comprised of (TN,TNSD,TNCD,TNST) where N=1 to 36. Array will not pick up data into it only iff a set full contains NUll data . Once all the data pick up will be done,it should then relase the data to each row. But thing should be remembered that if 2D array should assign data to the row from where it has been picked up.
Data(1,1)=(a,b,,b,t,,,r,t,k,l,o) During pick up
Cell(2,12)=(a,b,,b,t,,,r,t,k,l,o) when releasing the data.
that means data should need to mapped to the correct rows(setwise)
Please let me know if you have any confusion.
EDIT:
Output table
PID T1 T1SD T1CD T1ST T2 T2SD T2CD T2ST T3 T3SD T3CD T3ST T4 T4SD T4CD T4ST
10 a b b t r t k l o
11 p p m d n n n b
Thanks,Arup
Here is a partial answer that should get you a nudge in the right direction.
Sub Macro1()
Dim whichT As Integer
Dim whichC As Integer
Dim allNull As Boolean
Dim contents As String
For whichT = 0 to 8 ' this is which T set
allNull = True
for whichC = 1 to 4 ' this is which of the 4 elements
contents = Cells(2, whichT * 4 + whichC + 1)
Debug.Print "Contents of col ", whichT * 4 + whichC + 1, "are", contents
If Len(contents) > 0 then allNull = False ' any one of the non-blank elements sets to False
Next whichC
If allNull Then ... ' do some processing to move over the next 4.
Next whichT
End Sub

Resources