Grouping data according to total - vba

Assuming that I have the table below to work with:
project name total units
a 3
b 4
c 1
d 5
e 2
f 5
g 8
h 12
i 8
j 10
k 4
l 7
m 9
n 19
o 15
p 6
q 3
I would like to have the project names grouped with the total units not exceeding 20 for example.
So if I add up project a up to f, it will give me a total of 20. So this group of projects to be grouped and given a unique identifier by Excel.
I want to easily determine which file number the specific project goes into. So as soon as I enter the project name and the total units, it can return a number to me saying which file number the project should go into.
project name total units file number
a 3 1
b 4 1
c 1 1
d 5 1
e 2 1
f 5 1
g 8 2
h 12 2
i 8 3
j 10 3
k 4 4
l 7 4
m 9 4
n 19 5
o 15 6
p 6 7
q 3 7
The final outcome I would like to have whereby the total units are summed up and the project names with sum equal or less than 20 is grouped and given a file number.
Is it possible to have Excel do such thing?

Don't do with VBA, what you could easily do with Excel's inbuilt functions. The SUMIF() function will help a lot here
Place the following formula into cell C2 (assuming the setup above)
=IF(A2="a",ROUNDDOWN((B2-1)/20,0)+1,IF(SUMIF($C1:C$2,C1,$B1:B$2)+B2>20,C1+1,C1))
The formula is doing the following:
If the name is "a" then check how many files needed based on units in "a"
For all other names: sum the previous units in the current file (i.e. file number in cell above) and add the current project units. If the number exceeds 20 add 1 to the file number, otherwise use the same file number
I have tested this, but let me know if you have any problems.

OK with the provisos mentioned by barryleajo and assuming your individual total units are between 1 and 19 you need this algorithm I think:-
If it's the first line of data
Running total=total units
Else
If (Previous running total + total units) > 20
Running total=total units
Else
Running total=Previous running total + total units
So in the spreadsheet below I've set D2=B2 and E2=1,
then put the formula
=IF(D2+B3>20,B3,D2+B3)
into D3
and
=IF(B3=D3,E2+1,E2)
into E3 and pulled them down.

The following code works. I added comments to help you understand the answer.
Dim total_units As Range
Dim file_number As Integer
Dim cumulative_sum As Integer
Sub filenumber()
'Fill in column C header with string 'file_number'
Range("C1") = "file_number"
'Set total_units as the range variable
Set total_units = ThisWorkbook.Sheets(1).Range("B2")
'File_number starts equal to 1
file_number = 1
'Cumulative sum starts in the first row of total_units
cumulative_sum = total_units
'Loop until non empty rows of column project_name
Do While Not total_units = ""
'Fill in column C
total_units.Offset(, 1) = file_number
'Records the cumulative_sum in the row
cumulative_sum = cumulative_sum + total_units.Offset(1, 0)
'If cumulative sum exceeds 20, then, `file_number` changes and the start point in `cumulative_sum` also changes
If cumulative_sum > 20 Then
cumulative_sum = total_units.Offset(1, 0)
file_number = file_number + 1
End If
'Move the range
Set total_units = total_units.Offset(1, 0)
'Next row
Loop
End Sub

Related

How to get sum of the values for every 10 rows?

Have a column in Datagridview (over 300 rows) and would like to get the sum of every 10 rows. There is no gap between the data in Column 1.
I just want to have the sum value for every 10th rows in the other column. What I have tried showing me the cumulative #s in each row!
Any help would be appreciated. Thanks so much!
C1 C2
2
3
4
6
5
3
5
7
6
5 46
8
9
4
7
5
2
1
2
5
4 47
1
2
6
.
.
.
Dim sum As integer = 0
For i = 0 To data1.Rows.Count - 1
sum += data1.Rows(i).Cells(0).Value
data1.Rows(i).Cells(1).Value = sum
If (i+1) = 10 Then
Sum = 0
Else
End If
Next
There are multiple ways, here is one example:
Loop over every row to sum, then on every 10th output sum and reset number.
For i = 0 To data1.Rows.Count - 1
sum += data1(0, i).Value
If i.ToString.EndsWith("9") Then 'since it starts at 0, 10th ends with 9 not 0
data1(1, i).Value = sum
sum = 0
End If
Next
Use LINQ to get the sum of each group of n rows
Private Function getSumsOfGroupsOfNRows(n As Integer) As IEnumerable(Of Double)
Return DataGridView1.Rows.OfType(Of DataGridViewRow).
GroupBy(Function(r) Math.Truncate(r.Index / n)).
Select(Function(g) g.Sum(Function(r) CDbl(r.Cells(0).Value)))
End Function
Test it with this
Dim n = 10
' adds numbers 1 through 100 in the datagridview, for testing
DataGridView1.Rows.AddRange(Enumerable.Range(1, 100).Select(
Function(r)
Dim row As DataGridViewRow = DataGridView1.Rows(0).Clone
row.Cells(0).Value = r
Return row
End Function).ToArray())
' get the sums in an IEnumerable(Of Double)
Dim sums = getSumsOfGroupsOfNRows(n)
' write into second column
For i = 1 To n
DataGridView1.Rows.Item(n * i - 1).Cells(1).Value = sums(i - 1)
Next

VBA running sum

Let's say I have 2 columns (the following table includes the result)
Product ID Price Average
1 4 5
1 4 5
1 7 5
2 3 3
2 3 3
3 9 9
I want to be able to write a VBA code to loop through the rows of Product IDs and create the 3rd column which has average out the Prices.
I guess a For statement would work, but how do I define temp variables to store each ID?
Thanks!
As Vasily and L.Dutch told, AVERAGEIF() function is all you need. If you want to loop it through all cells, you can use Do While loop like this:
Sub avg()
Dim i As Integer
i = 2
Do While Range("A" & i).Value <> ""
Range("C" & i).FormulaR1C1 = "=AVERAGEIF(C1,RC1,C2)"
i = i + 1
Loop
End Sub

How can I find the first four consecutive numbers with four free denominators?

The first two consecutive numbers with two free denominators are:
14 = 2 × 7
15 = 3 × 5
The first three consecutive numbers with three free denominators are:
644 = 2 × 7 × 23
645 = 3 × 5 × 43
646 = 2 × 17 × 19
How can I find the first four consecutive numbers with exactly four free denominators?
It isn't the case that 644 = 2 x 7 x 23. Rather 644 = 2 x 2 x 7 x 23 -- you are looking for numbers which have distinct prime divisors which might be repeated. In fact, 1309, 1310, 1311 are the first 3 numbers which have 3 distinct non-repeated factors, and it is impossible to do that for 4 since any run of 4 successive numbers will have a repeated factor of 4.
To solve the problem of finding 4 successive numbers each of which has 4 distinct (albeit possibly repeated) prime factors, you can use a modified version of the Sieve of Eratosthenes, one which keeps track of the number of distinct divisors:
Function SieveK(n As Long, k As Long) As Long
'Implements a modified Sieve of Erasthones
'to return the first of k successive numbers
'less than or equal to n, each of which has
'k distinct prime factors.
'Returns -1 if no such number exists
Dim nums As Variant
Dim i As Long, p As Long
Dim run As Long
Dim limit As Long
Dim primes As Variant
primes = Array(2, 3, 5, 7, 11, 13) 'small primes
p = 1
For i = 0 To k - 2
p = p * primes(i)
Next i
limit = Int(1 + n / p)
ReDim nums(2 To n) As Long
p = 2 'first prime
Do While p < limit
'mark subsequent multiples of p by adding 1
For i = 2 * p To n Step p
nums(i) = nums(i) + 1
Next i
'find next p -- which will be next 0
p = p + 1
Do While nums(p) <> 0
p = p + 1
Loop
Loop
'At this stage, all numbers are marked.
'primes are marked by 0 and composites are marked
'by the number of distinct prime factors.
'Check for a run of k ks
run = 0
For i = 2 To n
If nums(i) = k Then
run = run + 1
If run = k Then 'we have a winner!
SieveK = i - k + 1
Exit Function
End If
Else
'reset run counter
run = 0
End If
Next i
SieveK = -1
End Function
SieveK(10^6,4) evaluates to 134043 in less than a second. The function doesn't yield the factorization of this or the next 3 numbers, but those are easy to find. SieveK(10^8,5) evaluates to -1, so there isn't a run less than 100 million of 5 consecutive numbers each of which has 5 different prime factors.
Remark: in an earlier version of this answer I had a logic flaw (which didn't prevent the output from being correct). Namely -- I had sieved only to the square root of n, though if e.g. a number m looks like 2x3x5xp with p prime then m could have 4 distinct prime factors even though p might exceed the square root of n. I revised the algorithm to take care of this possibility.
Here you go:
134043 = 3 x 7 x 13 x 491
134044 = 2 x 23 x 31 x 47
134045 = 5 x 17 x 19 x 83
134046 = 2 x 3 x 11 x 677

if statement in excel, adding 1 if cell with text but

I am creating an excel sheet that has three columns. Detail, month and month count
1 -- I would like for the formula to look at the detail column and if there is text add the previous cell number plus 1 to new month count, if not insert 0
2-- I would like the formula to add the previous cell before the cell with 0 and for the cell with 0 not to impact the other cells or reset the cells back to 1 witch is the problem am having
3-- I also need the formula to reset for every month from what ever number it was back to 0 or 1 depending if the new month first cell has text or not. for this I need the formula to look at the month column
This is what I have so far:
=IF(ISTEXT(G95), I94+ 1, 0)
The formula for the count column should be as follows.
=IF(A2<>"",COUNTIF($B$1:B2,B2)-COUNTIFS($A$1:A2,"",$B$1:B2,B2),0)
Breakdown of how this works:
A2<>"" Will check if the detail column is populated
COUNTIF($B$1:B2,B2) will figure out how many entries are above this row that reference the same month.
COUNTIFS($A$1:A2,"",$B$1:B2,B2) Will find how many cells are blank provided that it also matches the month. This subtracted from the previous section gives you how many are not blank.
The IF will return 0 if the detail is empty.
Which returned the following data
Orderly Random
Det Mon Count Det Mon Count
X 1 1 2 0
X 1 2 X 1 1
X 1 3 X 1 2
1 0 2 0
X 1 4 X 2 1
X 2 1 X 1 3
X 2 2 X 1 4
2 0 1 0
2 0 1 0
2 0 2 0
3 0 3 0
X 3 1 X 3 1
3 0 1 0
X 3 2 3 0
X 3 3 X 1 5
3 0 X 2 2
X 3 4 X 3 2
3 0 3 0
X 3 5 3 0
X 3 6 2 0
It sounds like you want to keep a running total for the month count in the column and put a 0 if there is not text. If that is the case, you can put this formula in I95.
=IF(ISTEXT(G95),MAX($I$2:I94)+1, 0)

macro to re-arrange data

I have been trying to write a macro to re-arrange the Cells in the rows and columns of Stock tables for the output I desire. Luckily the Stock Tables are generally the same each and every time (Different names and values), and the desired outcome is the same format..
Here is some example Data.
A
1 Name
2 description
3 description
4 description
5 description
6 ID#: 56284
7 Quantity in stock: 34
8 Zoom In and Configure
B
1 Name
2 description
3 description
4 description
5 description
6 ID#: 56284
7 Quantity in stock: 50
8 Zoom In and Configure
And I would like the Output to go into something like this(If possible to sheet2 starting on Cell B2):
B C E
B Being Row 1
C being Row 2 3 4 and 5 Combined
E being JUST Row 7 Stock Value I.E 50
On a single spreadsheet there would be 4 columns, and 8 rows I would have to re-arrange.. Making 32 total.
It would be great to automated this, so any help would be greatly appreciated.
Let me clarify my understanding. For each column you want the following data format:
A A
1 Name 1 Name
2 Desc1 2 Desc1; Desc2; Desc3; Desc4
3 Desc2 On sheet 2 3 50
4 Desc3 --------------->
5 Desc4
6 Id#: 56284
7 Quantity in Stock: 50
8 Zoom in and configure
If this is the case you can use the following code. It assumes your data is in A1 to D8 in Sheet 1.
Sub FormatData()
Dim col As Integer
For col = 1 To 4
With Worksheets(2)
.Cells(1, col) = Cells(1, col) //Get name
.Cells(2, col) = Cells(2, col) & "; " & Cells(3, col) & "; " & Cells(4, col) & "; " & Cells(5, col) //Concatenate descriptions into single string
.Cells(3, col) = Cells(7, col) //Get quantity in stock
End With
Next col
End Sub