Grouped Stacked Chart in Excel - excel

I have the following data in Excel.
Project | Month | Calls Created | Open Calls | Closed Calls
Requests | Dec 2012 | 15 | 11 | 7
Requests | Jan 2013 | 6 | 8 | 9
Requests | Feb 2013 | 6 | 5 | 2
dotcom | Dec 2012 | 1 | |
dotcom | Jan 2013 | | 1 |
dotcom | Feb 2013 | 1 | 2 | 1
The data is only a small subset. For the full dataset, there will be four projects, and the dates range from 2012 to June 2014
I am trying to create a chart, that for month and each project the data is bar stacked, while the overlying X axis is the Month
I have attached an mockup of the chart that I am looking to produce
I can create stacked bar chart if I only want to use one of Calls Created, Open Calls, Closed Calls. However I need a chart that will combine all three

I show you what I think. If are ok, it's simple to adapt...
Following the scheme:
you have something like you need, but use a simple Chart Bar. The trick is to convert the data in the correct way. To do that I have Used VBA because it's more flexible...
Using this code inside a module connected to a button (Update) ... :
Dim N0(1 To 100) As String
Dim N1(1 To 100) As String
Dim N2(1 To 100) As String
Dim N3(1 To 100) As String
Range("B14:H44").Clear
Range("B1").Select
e = 0
For i = 1 To 9999
If ActiveCell.Offset(i, 0).Value = "" Then Exit For
e = e + 1
N0(e) = ActiveCell.Offset(i, 0).Value
N1(e) = ActiveCell.Offset(i, 1).Value
N2(e) = ActiveCell.Offset(i, 2).Value
N3(e) = ActiveCell.Offset(i, 3).Value
Next
Range("B15").Select
For i = 1 To e
If (i > 1) And (N0(i) = N0(1)) Then Exit For
ActiveCell.Offset((i - 1) * 4, 0).Value = "["
ActiveCell.Offset((i - 1) * 4 + 1, 0).Value = N0(i)
ActiveCell.Offset((i - 1) * 4 + 2, 0).Value = "]"
ActiveCell.Offset((i - 1) * 4 + 3, 0).Value = ""
Next
nRep = i - 1
Debug.Print nRep
nrow = 0
For i = 1 To e
If (i > nRep) And (N0(i) = N0(1)) Then nrow = nrow + 1
For k = 1 To 99
If ActiveCell.Offset((k - 1) * 4 + 1, 0).Value = "" Then Exit For
If ActiveCell.Offset((k - 1) * 4 + 1, 0).Value = N0(i) Then
ActiveCell.Offset((k - 1) * 4, 1 + nrow).Value = N1(i)
ActiveCell.Offset((k - 1) * 4 + 1, 1 + nrow).Value = N2(i)
ActiveCell.Offset((k - 1) * 4 + 2, 1 + nrow).Value = N3(i)
End If
Next
Next
The macro generate the NEW range Data for the Chart... The blank lines are used to visual divide the bars in base of the Mounth...
After, manually or if you prefer via VBA, you can adapt the chart (Decreasing the gap Width, add Labels ...)

Related

How to stack up excel rows to column(s)? [as shown below] [duplicate]

This question already has answers here:
excel vba I need to transpose data from columns to rows
(2 answers)
Closed 5 years ago.
I have a dataset with 50,000 records. The format is like shown below.
Category | A | B | C
---------|-----|-----|-----
X | 100 | 120 | 150
Y | 200 | 220 | 250
Z | 300 | 320 | 350
I want the resulting table to look like the one shown below.
Category | Type | price
---------|------|-----
X | A | 100
X | B | 120
X | C | 150
---------|------|----
Y | A | 200
Y | B | 220
Y | C | 250
---------|------|----
Z | A | 300
Z | B | 320
Z | C | 350
How can I do this in MS Excel?
hope this will help you ;)
Public Type res
cat As String
mtype As String
price As Long
End Type
Sub arranger()
'defs+inits
Set src = Sheets("src")
Set res = Sheets("result")
Dim mydatas() As res
lastrow = src.Cells(1, 1).End(xlDown).Row
'just for safe :)
If lastrow > 65000 Then Exit Sub
ReDim mydatas(2 To lastrow * 3)
act = 2
'fill array
For actrow = 2 To lastrow
For col = 2 To 4
mydatas(act).cat = src.Cells(actrow, 1)
mydatas(act).mtype = src.Cells(1, col)
mydatas(act).price = src.Cells(actrow, col)
act = act + 1
Next col
Next actrow
'write back in new format
'headers
res.Cells(1, 1) = "Cat"
res.Cells(1, 2) = "Type"
res.Cells(1, 3) = "price"
'datas
For act = 2 To UBound(mydatas) - 2
res.Cells(act, 1) = mydatas(act).cat
res.Cells(act, 2) = mydatas(act).mtype
res.Cells(act, 3) = mydatas(act).price
Next act
End Sub

Combinational algorithm for Excel VBA

Let's say that I have a set of items as the one below:
Item Units
Apples 5
Pears 5
Carrots 1
Oranges 4
And I have six persons to whom I can give these items. Let's call them:
Mr. A
Mr. B
Mr. C
Mr. D
Mr. E
Mr. F
I would need an Excel VBA code that retrieves me all the potential combinations of sharing the items presented above to the different persons. Please note that (i) order is not important and (ii) one person may have more than one unit of the same item.
In order to report this information I was thinking of a structure similar to that presented below:
Mr A Mr B ...
Apples Pears Carrots Oranges Apples Pears Carrots Oranges ...
Scenario 1 1 0 1 2 2 2 0 0 ...
Scenario 2 1 0 1 1 2 2 0 0 ...
...
I know that the number of combinations will be huge, but don't mind about computing requirements.
I've been trying to figure out the algorithm, but have not been able to achieve it.
Thanks in advance,
I tried to use Mr A and Mr B for testing and there are 360 different scenarios.
Sub distr_list()
Dim apple_a, apple_b
Count = 1
Cells(Count, 4) = "apple_a"
Cells(Count, 5) = "pear_a"
Cells(Count, 6) = "carrot_a"
Cells(Count, 7) = "orange_a"
Cells(Count, 9) = "apple_b"
Cells(Count, 10) = "pear_b"
Cells(Count, 11) = "carrot_b"
Cells(Count, 12) = "orange_b"
For apple_a = 0 To 5
For apple_b = 0 To 5
For pear_a = 0 To 5
For pear_b = 0 To 5
For carrot_a = 0 To 1
For carrot_b = 0 To 1
For orange_a = 0 To 4
For orange_b = 0 To 4
If apple_a + apple_b = 5 Then
If pear_a + pear_b = 5 Then
If carrot_a + carrot_b = 1 Then
If orange_a + orange_b = 4 Then
Cells(Count + 1, 4) = apple_a
Cells(Count + 1, 5) = pear_a
Cells(Count + 1, 6) = carrot_a
Cells(Count + 1, 7) = orange_a
Cells(Count + 1, 9) = apple_b
Cells(Count + 1, 10) = pear_b
Cells(Count + 1, 11) = carrot_b
Cells(Count + 1, 12) = orange_b
Count = Count + 1
End If
End If
End If
End If
Next
Next
Next
Next
Next
Next
Next
Next
End Sub
You can apply the same logic for the rest.

Generate every possible chain through a table of numbers

Question as understood by author of accepted answer
My code operates on the worksheet below. The code creates the required output but I can only prevent the code from entering an endless loop by having seven nested loops; one per row of data. The current data is only an example and tables of up to 17 rows are expected so this is not a practical approach.
The table of numbers is in range C7:G23. Chains start in range C7:G7. The 1 in cell C7 leads to row 1 which is identified by the 1 in column A. Range C8:G8 specifies the 1 can be followed by 2, blank, blank, 4 or blank. The blanks indicate end of chain. The 2 and the 4 identify the next possible links in the chain. As each possible chain is identified, it is output to the next free row under I1:P1.
Can anyone suggest how to achieve this output without the risk of endless loops and without one nested loop per row in the table of numbers?
Row| A |B| C | D | E | F | G |H|I|J|K|L|M|N|O|P|
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
1| | | | | | | | | Test 3 |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
2| | | | | | | | |1|2| | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
3| | | | | | | | |1|2|3|4|6| | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
4| | | | | | | | |1|2|3|4|6| | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
5| | | | | | | | |1|2|3|4|6|5| | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
6| | |Col1|Col2|Col3|Col4|Col5| |1|2|3|4|6|5| | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
7|Rows| | 1 | | | | | |1|2|3|4|6|5| | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
8| 1| | 2 | | | 4 | | |1|2|3|4|6|5| | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
9| 2| | | 3 | | | | |1|2|3|4|6|5| | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
10| 3| | 4 | | | | | |1|2|3|4|6| | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
11| 4| | 6 | | | | | |1|2|3|4|6| | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
12| 5| | | | | | | |1|2|3|4| | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
13| 6| | | | 5 | | | |1|2|3|4| | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
14| 7| | | | | | | |1|2|3|4| | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
15| 8| | | | | | | |1|2|3|4| | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
16| 9| | | | | | | |1|2|3| | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
17| 10| | | | | | | |1|2|3| | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
18| 11| | | | | | | |1|2|3| | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
19| 12| | | | | | | |1|2|3| | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
20| 13| | | | | | | |1|2| | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
21| 14| | | | | | | |1|2| | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
22| 15| | | | | | | |1|2| | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
23| 16| | | | | | | |1| | | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
24| | | | | | | | |1| | | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
25| | | | | | | | |1|4|6| | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
26| | | | | | | | |1|4|6| | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
27| | | | | | | | |1|4|6|5| | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
28| | | | | | | | |1|4|6|5| | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
29| | | | | | | | |1|4|6|5| | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
30| | | | | | | | |1|4|6|5| | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
31| | | | | | | | |1|4|6|5| | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
32| | | | | | | | |1|4|6| | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
33| | | | | | | | |1|4|6| | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
34| | | | | | | | |1|4| | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
35| | | | | | | | |1|4| | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
36| | | | | | | | |1|4| | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
37| | | | | | | | |1|4| | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
38| | | | | | | | |1| | | | | | | |
|----+-+----+----+----+----+----+-+-+-+-+-+-+-+-+-|
Original title: Excel vba - How to avoid X times a “for next” loop in a “for next” loop to find the Cells combination for a template of X rows and 5 columns
Original question:
The code I have done is working perfectly! But only because I repeat 7 times (due to 7 rows) a "for next" loop in a "for next" loop....(see below).
Sub test3()
Range("I2:P40").ClearContents
' "Tableau" means matrix in french
Dim Tableau() As Long
' "l" means row (it is like r)
ReDim Tableau(l)
l = 0
' "l0" means row 0 (it is like r0)
Dim l0 As Long
Dim Pass As Long
l0 = 7
Pass = 2
'"PlagePX" Range of row addresses. To take in account for combinations in the matrix
Dim PlagePX As Range
Set PlagePX = Range(Cells(l0, 1), Cells(23, 1))
Cells(l0, 1).Select
Cells(l0, 3).Select
' "CL" means columns of row1,2,3,4,5,... (it is like RC1,2,3,4,5,...)
For CL1 = 1 To 5
If IsEmpty(Cells(l0, 3)) = False Then
ReDim Preserve Tableau(l)
Tableau(l) = Application.WorksheetFunction.Match(Cells(l0, 3).Value, PlagePX, 0) + 6
Cells(Tableau(l), 1).Select
Cells(Tableau(l), 2 + CL1).Select
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l), 2 + CL1).Value
l = l + 1
Else
Cells(Pass, 9) = Cells(l0, 3).Value
Exit For
End If
For CL2 = 1 To 5
If IsEmpty(Cells(Tableau(l - 1), 2 + CL1)) = False Then
ReDim Preserve Tableau(l)
Tableau(l) = Application.WorksheetFunction.Match(Cells(Tableau(l - 1), 2 + CL1).Value, PlagePX, 0) + 6
Cells(Tableau(l), 1).Select
Cells(Tableau(l), 2 + CL2).Select
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 1), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l), 2 + CL2).Value
l = l + 1
Else
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 1), 2 + CL1).Value
Pass = Pass + 1
Exit For
End If
For CL3 = 1 To 5
If IsEmpty(Cells(Tableau(l - 1), 2 + CL2)) = False Then
ReDim Preserve Tableau(l)
Tableau(l) = Application.WorksheetFunction.Match(Cells(Tableau(l - 1), 2 + CL2).Value, PlagePX, 0) + 6
Cells(Tableau(l), 1).Select
Cells(Tableau(l), 2 + CL3).Select
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 2), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 1), 2 + CL2).Value
Cells(Pass, 12) = Cells(Tableau(l), 2 + CL3).Value
l = l + 1
Else
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 2), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 1), 2 + CL2).Value
Pass = Pass + 1
Exit For
End If
For CL4 = 1 To 5
If IsEmpty(Cells(Tableau(l - 1), 2 + CL3)) = False Then
ReDim Preserve Tableau(l)
Tableau(l) = Application.WorksheetFunction.Match(Cells(Tableau(l - 1), 2 + CL3).Value, PlagePX, 0) + 6
Cells(Tableau(l), 1).Select
Cells(Tableau(l), 2 + CL4).Select
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 3), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 2), 2 + CL2).Value
Cells(Pass, 12) = Cells(Tableau(l - 1), 2 + CL3).Value
Cells(Pass, 13) = Cells(Tableau(l), 2 + CL4).Value
l = l + 1
Else
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 3), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 2), 2 + CL2).Value
Cells(Pass, 12) = Cells(Tableau(l - 1), 2 + CL3).Value
Pass = Pass + 1
Exit For
End If
For CL5 = 1 To 5
If IsEmpty(Cells(Tableau(l - 1), 2 + CL4)) = False Then
ReDim Preserve Tableau(l)
Tableau(l) = Application.WorksheetFunction.Match(Cells(Tableau(l - 1), 2 + CL4).Value, PlagePX, 0) + 6
Cells(Tableau(l), 1).Select
Cells(Tableau(l), 2 + CL5).Select
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 4), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 3), 2 + CL2).Value
Cells(Pass, 12) = Cells(Tableau(l - 2), 2 + CL3).Value
Cells(Pass, 13) = Cells(Tableau(l - 1), 2 + CL4).Value
Cells(Pass, 14) = Cells(Tableau(l), 2 + CL5).Value
l = l + 1
Else
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 4), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 3), 2 + CL2).Value
Cells(Pass, 12) = Cells(Tableau(l - 2), 2 + CL3).Value
Cells(Pass, 13) = Cells(Tableau(l - 1), 2 + CL4).Value
Pass = Pass + 1
Exit For
End If
For CL6 = 1 To 5
If IsEmpty(Cells(Tableau(l - 1), 2 + CL5)) = False Then
ReDim Preserve Tableau(l)
Tableau(l) = Application.WorksheetFunction.Match(Cells(Tableau(l - 1), 2 + CL5).Value, PlagePX, 0) + 6
Cells(Tableau(l), 1).Select
Cells(Tableau(l), 2 + CL6).Select
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 5), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 4), 2 + CL2).Value
Cells(Pass, 12) = Cells(Tableau(l - 3), 2 + CL3).Value
Cells(Pass, 13) = Cells(Tableau(l - 2), 2 + CL4).Value
Cells(Pass, 14) = Cells(Tableau(l - 1), 2 + CL5).Value
Cells(Pass, 15) = Cells(Tableau(l), 2 + CL6).Value
l = l + 1
Else
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 5), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 4), 2 + CL2).Value
Cells(Pass, 12) = Cells(Tableau(l - 3), 2 + CL3).Value
Cells(Pass, 13) = Cells(Tableau(l - 2), 2 + CL4).Value
Cells(Pass, 14) = Cells(Tableau(l - 1), 2 + CL5).Value
Pass = Pass + 1
Exit For
End If
'The question is which approach I should follow for X rows,
'to avoid repeating again and again a "For Next" loop in a "For Next" loop???
For CL7 = 1 To 5
If IsEmpty(Cells(Tableau(l - 1), 2 + CL6)) = False Then
ReDim Preserve Tableau(l)
Tableau(l) = Application.WorksheetFunction.Match(Cells(Tableau(l - 1), 2 + CL6).Value, PlagePX, 0) + 6
Cells(Tableau(l), 1).Select
Cells(Tableau(l), 2 + CL7).Select
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 6), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 5), 2 + CL2).Value
Cells(Pass, 12) = Cells(Tableau(l - 4), 2 + CL3).Value
Cells(Pass, 13) = Cells(Tableau(l - 3), 2 + CL4).Value
Cells(Pass, 14) = Cells(Tableau(l - 2), 2 + CL5).Value
Cells(Pass, 15) = Cells(Tableau(l - 1), 2 + CL6).Value
Cells(Pass, 16) = Cells(Tableau(l), 2 + CL7).Value
Else
Cells(Pass, 9) = Cells(l0, 3).Value
Cells(Pass, 10) = Cells(Tableau(l - 6), 2 + CL1).Value
Cells(Pass, 11) = Cells(Tableau(l - 5), 2 + CL2).Value
Cells(Pass, 12) = Cells(Tableau(l - 4), 2 + CL3).Value
Cells(Pass, 13) = Cells(Tableau(l - 3), 2 + CL4).Value
Cells(Pass, 14) = Cells(Tableau(l - 2), 2 + CL5).Value
Cells(Pass, 15) = Cells(Tableau(l - 1), 2 + CL6).Value
Pass = Pass + 1
Exit For
End If
Pass = Pass + 1
Next
l = l - 1
Next
l = l - 1
Next
l = l - 1
Next
l = l - 1
Next
l = l - 1
Next
l = l - 1
Next
MsgBox "fin"
End Sub
SO,
The question is: How to do it when you have X rows in order to avoid infinite "for next" loop????
Is anyone have the answer or tell me which approach I should follow?
I have tested my code as fully as I can. I have increased the height and width of the Data Table and have included erroneous values. However, there is no substitute for testing with real values. Let me know if any input values fail to give the results you expect.
I have not studied your code. It is possible that I might find a simple correction that would prevent the endless loops. However, finding that simple correction would take a long time and the code would still rely on the current table size. The code below is all new.
I will refer to:
C6:G6 as the Data Table Header.
C7:G23 as the Data Table.
I2:P100 as the Results Table.
My code starts by discovering the true size of the Data Table. That is, my code does not assume the Data Table is five columns wide or 17 rows deep.
My macro requires the Data Table Header contain a text value for every column of the Data Table. You have used “Col1”, “Col2”, “Col3” and so on. My code does not rely on these names but it does reply on there being a value for every column.
If you position the cursor to cell C6 and click Ctrl+Right, the cursor jumps to cell G6. If you are not familiar with Ctrl+Arrow, have a play and see how the cursor moves. My code does the VBA equivalent to find the last column of the Data Table Header.
Now that I know the width of the Data Table, I can define a range of this width that includes all rows. I can then search this range up from the bottom for the first row with a value. This gives me the last row of the Data Table.
I can now load the entire Data Table as an array to a Variant variable.
All the code to do the above is in the subroutine LoadDataTable. I have a subroutine TestLoadDataTable to demonstrate the Data Table has been loaded correctly by outputting the table to the Immediate Window so:
Row Col01 Col02 Col03 Col04 Col05
0 1
1 2 4
2 3
3 4
4 6 5
I have “0” where you have “start cell in green” but otherwise this matches your Data Table.
When a worksheet range is loaded to a variant, the array always has lower bounds of 1. The column numbers in the header above are the true column numbers for the array. The row numbers in the left hand column are the true row numbers less 1. I have not loaded column A to this array; because I do not need the values. If your rows are not in numeric order (as per your example) we will need a couple of extra steps but that is not a problem.
I am loading the data to an array because it is faster and more convenient to get the data out of an array.
If you are unsure about any of the above, experiment. Try different numbers of rows and columns and see what macro TestLoadDataTable outputs. Work down TestLoadDataTable and LoadDataTable and study what each statement achieves. Search online for any statement you do not know for a definition.
As the main routine works down the Data Table, a sequence will grow. It would start with (1) then discover (1 2) then (1 2 3) then (1 2 3 4) then (1 2 3 4 6). I will hold the growing sequence in an array.
I could use ReDim Preserve to grow the array but I avoid ReDim Preserve when I can. ReDim Preserve is a very useful statement but it is a very expensive statement. The interpreter must find space for the new larger array, copy the data from the old array, initialise the new section and release the old array for garbage collection. As the array gets bigger and bigger, this takes longer and longer and macros can slow to a crawl.
If the Data Table has N rows, a sequence cannot have N+1 values without repeating a row. If I size an array to hold a sequence to have N+1 entries, I know it cannot be filled without a repeat. I thought at first this would be enough to prevent endless loops. However, I can design Data Tables that would spawn large numbers of half-grown sequences before filling the array. I will instead check a new entry against all previous entries in a sequence; a repeat would indicate an error.
I have two approaches to managing the sequences. I do not think the first approach will be satisfactory but I will explain it.
For the first approach, I would have a pending array or collection. You know about arrays. A “collection” is what most programming languages call a “list”. You read from a collection in the same way you read from array. You can add new entries to or remove existing entries from a collection easily. Arrays are faster to access than collection. The description below is high level so the choice or array or collection does not matter.
Each entry in Pending would be an incomplete sequence.
I would start by placing one entry in Pending for each value in the first row of the Data Table. You have “1” in column 1 of your first row. I do not know if you could have more than one value in the first row but it is easy to allow for the possibility. In your example, I would have one entry containing sequence (1) in Pending. I would then loop performing the following steps until Pending was empty.
For each loop, I would take a copy of the last entry of Pending and then delete that last entry from Pending. If I call that copy Work, then with your example, Work contains (1) and Pending is now empty.
Looking at the row of the Data Table labelled 1, the macro can see possible extensions to this sequence as: (1 2), (1 empty), (1 empty), (1 4) and (1 empty). The sequences of the form (1 empty) are complete and can be written to the Results Table. Sequences (1 2) and (1 4) are added to Pending.
For the second repeat of the loop, Pending now has two entries. The code copies the last entry – (1 4) – to Work and deletes it from Pending. The possible extensions are (1 4 6), (1 4 empty), (1 4 empty), (1 4 empty) and (1 4 empty). Sequences (1 4 empty) are complete and can be written to the Results Table. Sequence (1 4 6) is added to Pending.
If you run through this sequence on paper, you can quickly see how it is generating results for the Results Table. There is little code within the loop and certainly much less code than you have. You may need to play with the idea for a while but, once grasped, it is easy to understand. The down side is that the entries in the Results table will be in a very strange sequence: (1), (1), (1), (1), (1 4), (1 4), (1 4), (1 4), (1 4 6), (1 4 6), (1 4 6), (1 4 6), (1 4 6 5), (1 4 6 5), (1 4 6 5), (1 4 6 5), (1 2) and so on. Perhaps you would be happy with this sequence. Note: I do not understand why you are happy with duplicates in your Results Table but have retained them to match your Results Table.
The other approach involves recursion. Recursion is another idea which is impossible to understand until suddenly it is easy. I compare it with driving. You KNOW at the end of your first lesson that you will never be able to control a wheel, three pedals, a gear stick while looking out the windscreen and checking in the view rear mirror. But a month later, you do not remember what you found difficult.
Suppose you have ProcessA which calls ProcessB which calls ProcessC. Most beginners seem happy with the idea that the interpreter has found memory for all ProcessA’s data. They are also happy with the idea that when ProcessA calls ProcessB, the interpreter finds more memory for ProcessB’s data so ProcessA’s data is safe until it is required again. When ProcessC is called, both ProcessA’s data and ProcessB’s data are kept safe. If ProcessA calls ProcessA, is it a big step to accept the interpreter will keep the first copy of ProcessA’s data safe while the second copy is running?
For ProcessA to call itself, you require ProcessA to be iterative. Extending (1) to (1 4), extending (1 4) to (1 4 6) and extending (1 4 6) to (1 4 6 5) are all the same problem so you can use the same code, each with its own data, for each extension.
You need three routines which I will call Control, ExtendOrOutput and Output. Control will load the Data Table and initialise the Results Table before calling ExtendOrOutput((1)) where (1) is the initial sequence.
ExtendOrOutput will look at the last entry in the current sequence and, in this case, convert (1) to (1 2), (1 empty), (1 empty), (1 4) and (1 empty). Each of these possible extensions requires processing:
Call ExtendOrOutput((1 2))
Call Output((1 empty))
Call Output((1 empty))
Call ExtendOrOutput((1 4))
Call Output((1 empty))
Now Call ExtendOrOutput ((1 2)) will do the same to give:
Call Output((1 2 empty))
Call ExtendOrOutput((1 2 3))
Call Output((1 2 empty))
Call Output((1 2 empty))
Call Output((1 2 empty))
The nature of calling subroutines means that everything under Call ExtendOrOutput((1 2)) is performed before the first `Call Output((1 empty)) so the sequence in which these calls are performed are:
Call ExtendOrOutput((1 2))
Call Output((1 2 empty))
Call ExtendOrOutput((1 2 3))
Call ExtendOrOutput((1 2 3 4))
Call ExtendOrOutput((1 2 3 4 6))
Call Output((1 2 3 4 6 empty))
Call Output((1 2 3 4 6 empty))
Call ExtendOrOutput((1 2 3 4 6 5))
Call Output((1 2 3 4 6 5 empty))
Call Output((1 2 3 4 6 5 empty))
Call Output((1 2 3 4 6 5 empty))
Call Output((1 2 3 4 6 5 empty))
Call Output((1 2 3 4 6 5 empty))
Call Output((1 2 3 4 6 empty))
Call Output((1 2 3 4 6 empty))
Call Output((1 2 3 4 empty))
Call Output((1 2 3 4 empty))
Call Output((1 2 3 4 empty))
Call Output((1 2 3 4 empty))
Call Output((1 2 3 empty))
Call Output((1 2 3 empty))
Call Output((1 2 3 empty))
Call Output((1 2 3 empty))
Call Output((1 2 empty))
Call Output((1 2 empty))
Call Output((1 2 empty))
Call Output((1 empty))
Call Output((1 empty))
Call ExtendOrOutput((1 4))
Call Output((1 empty))
If you scan down the Call Outputs you will see this gives the same sequence in the Results Table as you currently have.
I do not output results directly to the worksheet. Instead I create an array, ResultsTable, and output to that. I have specified this array to be 1,000 rows. If I fill the array, I give up. I do not know understand why you need the sequences but I imagine 1,000 is more than enough. You can increase or decrease 1,000 if necessary. If this is not acceptable, I have other ideas.
Option Explicit
' Constants are a good way of defining values that might change in the future
Const ColWshtDataTableLeft As Long = 3
Const RowWshtDataTableHdr As Long = 6
Const WshtName As String = "Data" ' Change to your name for the worksheet
Sub Control()
' Call LoadDataTable to copy the Data Table to an array
' Call ExtendOrOutput to create the Result Table of all chain through the Data Table
Dim ColDataTableCrnt As Long
Dim ColResultsTableCrnt As Long
Dim ColWshtCrnt As Long
Dim ColWshtResultTableLeft As Long
Dim DataTable As Variant
Dim ResultsTable As Variant
Dim RowDataTableCrnt As Long
Dim RowResultsTableCrnt As Long
Dim RowResultsTableCrntMax As Long
Dim Sequence() As Variant
Call LoadDataTable(DataTable) ' Load Data Table
' First column of Results Table which leave a blank column between Data Table
' and Results Table.
ColWshtResultTableLeft = ColWshtDataTableLeft + UBound(DataTable, 2) + 1
With Worksheets(WshtName)
' Delete columns to be used by Results Table plus those to the right or Results Table
.Columns(ColNumToCode(ColWshtResultTableLeft) & ":" & _
ColNumToCode(Columns.Count)).Delete
' Merge cells of header for Results Table. Width of Results Table is discussed below.
.Range(.Cells(1, ColWshtResultTableLeft), _
.Cells(1, ColNumToCode(ColWshtResultTableLeft + UBound(DataTable, 1) + 1))).Merge
With .Cells(1, ColWshtResultTableLeft)
.Value = "Results Table"
.HorizontalAlignment = xlCenter
End With
End With
' Size ResultsTable. Allow for 1,000 rows which I assume is more than could possibly
' be required. Width is height of Data Table + 2. "height of Data Table" allows a
' sequence to reference every row of the Data Table. I use the first extra column as
' a test for an over run. I do not think this is possible becuase of test for repeat
' row but thismakes absolute sure. I use to second extra column for an "error word"
' such as "Repeat" or "Overrun".
ReDim ResultsTable(1 To 1000, 1 To UBound(DataTable, 1) + 2)
RowResultsTableCrntMax = 0 ' Last used row in ResultsTable
'' Write values to ResultsTable to confirm entire table written to worksheet
'For RowResultsTableCrnt = 1 To UBound(ResultsTable, 1)
' For ColResultsTableCrnt = 1 To UBound(ResultsTable, 2)
' ResultsTable(RowResultsTableCrnt, ColResultsTableCrnt) = "'" & RowResultsTableCrnt & ":" & ColResultsTableCrnt
' Next
'Next
' Initialise the Sequence array
ReDim Sequence(0 To UBound(ResultsTable, 2))
Sequence(0) = 1 ' Last entry used
' Call ExtendOrOutput for every non-empty column in top row of DataTable.
' I know there will be a value in the first column. I do not know if there
' could be a value in later columns but no harm looking.
For ColDataTableCrnt = 1 To UBound(DataTable, 2)
If Not IsEmpty(DataTable(1, ColDataTableCrnt)) Then
Sequence(1) = DataTable(1, ColDataTableCrnt)
Call ExtendOrOutput(DataTable, ResultsTable, RowResultsTableCrntMax, Sequence)
End If
Next
' Output ResultTable to row 2 of Results Table in worksheet
With Worksheets(WshtName)
.Range(.Cells(2, ColWshtResultTableLeft), _
.Cells(UBound(ResultsTable, 1) + 1, _
ColWshtResultTableLeft + UBound(ResultsTable, 2) - 1)).Value = ResultsTable
End With
End Sub
Sub ExtendOrOutput(ByRef DataTable As Variant, ByRef ResultsTable As Variant, _
ByRef RowResultsTableCrntMax As Long, ByRef Sequence() As Variant)
' * DataTable as loaded from the worksheet. Values within DataTable are row
' numbers within DataTable except the value recorded is one less than the
' actual row number. Note: because DataTable has been loaded from a
' worksheet, dimension 1 is for rows and dimension 2 is columns.
' * ResultsTable be will loaded with completed sequences by Output. Note: because
' ResultsTable is to be written to a worksheet, dimensions are as for DataTable.
' ResultsTable has two more columns than should be necessary. In the event of
' an error with a sequence, an error word will be written to the last column.
' "Repeat" means a row number has repeated. "Overrun" means a value has been
' written to the penultimate column which should not be possible.
' * RowResultsTableCrntMax is the last currentlt used row within ResultsTable.
' * Sequence contains a sequence of row numbers which this routine will attempt
' to extend. If it cannot be extended, it is output to ResultsTable.
' Its definition is (0 to N+2) where N is the number of rows in DataTable.
' Entry 0 is used to hold the number of the last used entry within Sequence.
' Entry N+1 and N+2 are used as explained above under Results Table.
Dim ColDataTableCrnt As Long
Dim InxSequenceCrnt As Long
Dim InxSequenceMax As Long
Dim RepeatFound As Boolean
Dim RowDataTableCrnt As Long
If RowResultsTableCrntMax > UBound(ResultsTable, 1) Then
' Results Table is full
Exit Sub
End If
InxSequenceMax = Sequence(0) ' Last used entry in Sequence
RowDataTableCrnt = Sequence(InxSequenceMax) + 1 ' Last value in Sequence + 1
For ColDataTableCrnt = 1 To UBound(DataTable, 2)
If IsEmpty(DataTable(RowDataTableCrnt, ColDataTableCrnt)) Then
' This sequence is complete
Call Output(ResultsTable, RowResultsTableCrntMax, Sequence)
Else
' This sequence can be extended
InxSequenceMax = InxSequenceMax + 1
Sequence(InxSequenceMax) = DataTable(RowDataTableCrnt, ColDataTableCrnt)
Sequence(UBound(Sequence)) = "" ' No error
If IsNumeric(Sequence(InxSequenceMax)) Then
' Value is numeric but is it in range
If Sequence(InxSequenceMax) > -1 And Sequence(InxSequenceMax) < UBound(DataTable, 1) Then
' Value is a valid row number
RepeatFound = False
For InxSequenceCrnt = 1 To InxSequenceMax - 1
If Sequence(InxSequenceCrnt) = Sequence(InxSequenceMax) Then
' Repeated value
RepeatFound = True
Sequence(UBound(Sequence)) = "Repeat"
Call Output(ResultsTable, RowResultsTableCrntMax, Sequence)
End If
Next
If Not RepeatFound Then
' No repeat but is this an overrun?
If InxSequenceMax + 1 = UBound(Sequence) Then
' Have overrun. I don't think this is possible
Debug.Assert False
Sequence(UBound(Sequence)) = "Overrun"
Call Output(ResultsTable, RowResultsTableCrntMax, Sequence)
Else
' Have good extension
Sequence(0) = Sequence(0) + 1
Call ExtendOrOutput(DataTable, ResultsTable, RowResultsTableCrntMax, Sequence)
Sequence(0) = Sequence(0) - 1
End If
End If
Else
' Value is out of range
Sequence(UBound(Sequence)) = "Out of range"
Call Output(ResultsTable, RowResultsTableCrntMax, Sequence)
End If
Else
' Value is non-numeric so cannot be a row number
Sequence(UBound(Sequence)) = "Non-numeric"
Call Output(ResultsTable, RowResultsTableCrntMax, Sequence)
End If
' Restore Sequence ready for next column of DataTable
Sequence(InxSequenceMax) = ""
Sequence(UBound(Sequence)) = ""
InxSequenceMax = InxSequenceMax - 1
End If
Next
End Sub
Sub Output(ByRef ResultsTable As Variant, ByRef RowResultsTableCrntMax As Variant, _
ByRef Sequence As Variant)
' Copy contents of Sequence to next available row in ResultsTable
Dim InxSequenceCrnt As Long
RowResultsTableCrntMax = RowResultsTableCrntMax + 1
If RowResultsTableCrntMax > UBound(ResultsTable, 1) Then
' Results Table is full
Exit Sub
End If
For InxSequenceCrnt = 1 To UBound(Sequence)
ResultsTable(RowResultsTableCrntMax, InxSequenceCrnt) = Sequence(InxSequenceCrnt)
Debug.Print " " & Sequence(InxSequenceCrnt);
Next
Debug.Print
End Sub
Sub TestLoadDataTable()
' Call LoadTableTable then output its contents to the Immediate Window
Dim ColDTCrnt As Long
Dim DataTable As Variant
Dim RowDTCrnt As Long
Call LoadDataTable(DataTable)
' Output header row for DataTable
Debug.Print "Row";
For ColDTCrnt = 1 To UBound(DataTable, 2)
Debug.Print " Col" & Right("0" & ColDTCrnt, 2);
Next
Debug.Print
' Output DataTable
For RowDTCrnt = 1 To UBound(DataTable, 1)
Debug.Print Right(" " & RowDTCrnt - 1, 3);
For ColDTCrnt = 1 To UBound(DataTable, 2)
Debug.Print " " & Right(" " & DataTable(RowDTCrnt, ColDTCrnt), 5);
Next
Debug.Print
Next
End Sub
Sub LoadDataTable(ByRef DataTable As Variant)
' Determine the size of the Data Table and load its contents to DataTable
Dim ColDataTableRight As Long
Dim RowDataTableBottom As Long
With Worksheets(WshtName)
' * You have a header for the Data Table: Col1|Col2|Col3| . . .
' * This statement relies on there being a header. It does not matter what the header
' values providing the header is complete. This is the equivalent to positioning the
' cursor to the left cell of the header row and clicking Right. Since the start cell
' contains a value, it moves to the cell before the next empty cell
ColDataTableRight = .Cells(RowWshtDataTableHdr, ColWshtDataTableLeft).End(xlToRight).Column
' This statement first defines a range which is the width of the Data Table but includes
' all rows of the worksheet. It then searches from row 1 backwards (that is it starts
' the bottom row and searches upwards) until it finds a row with a value. This is the
' last row of the Data Table
RowDataTableBottom = .Range(.Cells(1, ColWshtDataTableLeft), _
.Cells(Rows.Count, ColDataTableRight)) _
.Find("*", .Cells(1, ColWshtDataTableLeft), xlFormulas, , xlByRows, xlPrevious).Row
' Import data table to DataTable
DataTable = .Range(.Cells(RowWshtDataTableHdr + 1, ColWshtDataTableLeft), _
.Cells(RowDataTableBottom, ColDataTableRight)).Value
End With
End Sub
Function ColNumToCode(ByVal ColNum As Long) As String
Dim ColCode As String
Dim PartNum As Long
' Last updated 3 Feb 12. Adapted to handle three character codes.
If ColNum = 0 Then
ColNumToCode = "0"
Else
ColCode = ""
Do While ColNum > 0
PartNum = (ColNum - 1) Mod 26
ColCode = Chr(65 + PartNum) & ColCode
ColNum = (ColNum - PartNum - 1) \ 26
Loop
End If
ColNumToCode = ColCode
End Function

Shuffle values to a new row two cells at a time

To explain it in the easiest way possible:
| 1 | 2 | 3 | 4 |
| 5 | 6 | 7 | 8 |
... needs to look like:
| 1 | 2 |
| 3 | 4 |
| 5 | 6 |
| 7 | 8 |
I've tried using the TRANSPOSE() function, but it doesn't seem to work in this situation.
How can I accomplish this simple task?
In an unused cell to the right use this formula,
=OFFSET($A$1, INT((ROW(1:1)-1)/2), COLUMN(A:A)-1+MOD((ROW(1:1)-1), 2)*2)
Fill right one column and fill down as far as necessary. Your results should resemble the following.
      
You put excel-vba in your tags, so I'll post the vba code for you. I don't know how to do it with simple cell formulas. Hopefully it's configurable enough to get what you want, beyond the simple example you gave:
START_ROW = 1
START_COL = 1
STEP_COL = 2
OUTPUT_ROW = 3
OUTPUT_COL = 10
Row = START_ROW
Col = START_COL
Out_Row = OUTPUT_ROW
While Cells(Row, Col).Value <> ""
While Cells(Row, Col).Value <> ""
For step = 0 To STEP_COL - 1
Cells(Out_Row, OUTPUT_COL + step).Value = Cells(Row, Col + step).Value
Cells(Out_Row, OUTPUT_COL + step).Value = Cells(Row, Col + step).Value
Next step
Out_Row = Out_Row + 1
Col = Col + STEP_COL
Wend
Col = START_COL
Row = Row + 1
Wend

Excel to calculate if values in ranges meet certain criteria using VBA

I have two ranges in excel, say:
x | y
------------
5 | -1
46 | -4
2 | 1
67 | -1
22 | 1
6 | 0
34 | 0
7 | -2
I want calculate the sum of the second column for values less than O only if the respective values in the first column is less than 10 (i.e sum(y(i) for i<0 and x(i)<10) . Hence in this case the sum will be -3.
Assuming your headers are in A1:B1 and your data is A2:B9 use this:
=SUMIFS(B2:B9,A2:A9,"<10",B2:B9,"<0")
Try something like
Function calc() AS Integer
Dim sum AS Integer: sum = 0
Dim c AS Range
For Each c In ThisWorkbook.Worksheets(1).Range("A1:A15")
If c.Value < 10 And c.Offset(0, 1).Value < 0 Then
sum = sum + c.Offset(0, 1).Value
End If
Next c
calc = sum
End Function

Resources