Splitting large data set into columns - vba

I have a large data set that I have managed to arrange in the following format as an example:
A A
One 1
Two 2
Three 3
Four 4
Five 5
Six 6
Seven 7
B B
One 1
Two 2
Three 3
Four 4
Five 5
Six 6
C C
One 1
Two 2
Three 3
Four 4
Five 5
Six 6
Seven 7
Eight 8
I would like to split the data into separate columns...like this:
A A B B C C
One 1 One 1 One 1
Two 2 Two 2 Two 2
Three 3 Three 3 Three 3
Four 4 Four 4 Four 4
Five 5 Five 5 Five 5
Six 6 Six 6 Six 6
Seven 7 Seven 7
Eight 8
This is one of the last steps in getting my data into a usable format. I am already employing various VBA macros to get the data into the first format. I hit a roadblock with this next major step. I have done some extensive googling and have not been able to come with the right macro. Thank you for your help.

Assuming no other frills and you're headers being more than just A, B, or C, I'd use the following macro. Just to be clear, this is highly inflexible. It will only work if you've got exactly the same setup as above, so it's up to you to apply the logic I used and modify it to suit your needs.
Sub NoFrills()
Dim aRng As Range, bRng As Range, cRng As Range
Dim endA As Long, endB As Long, endC As Long
Dim aRngToCopy As Range, bRngToCopy As Range, cRngToCopy As Range
Dim NextCol As Long, RngToCopy As Range
Set aRng = Columns(1).Find(What:="Alpha")
Set bRng = Columns(1).Find(What:="Beta")
Set cRng = Columns(1).Find(What:="Charlie")
'endA = bRng.Offset(-1, 0).Row 'Not really needed.
endB = cRng.Offset(-1, 0).Row
endC = cRng.End(xlDown).Row
'Set aRngToCopy = Range(aRng, Cells(endA, 2)) 'Not really needed.
Set bRngToCopy = Range(bRng, Cells(endB, 2))
Set cRngToCopy = Range(cRng, Cells(endC, 2))
Application.ScreenUpdating = False
NextCol = aRng.End(xlToRight).Offset(0, 1).Column
bRngToCopy.Cut Cells(1, NextCol)
NextCol = aRng.End(xlToRight).Offset(0, 1).Column
cRngToCopy.Cut Cells(1, NextCol)
With Application
.CutCopyMode = False
.ScreenUpdating = True
End With
End Sub
My approach here is to determine ahead of time all the "blocks" of cells needing to be cut and transferred into the horizontal format. Then, I keep on determining the next column that needs to be targeted by calling NextCol again and again and pasting to that cell.
The logic, of course, is that you've got more ranges, you have to store these ranges somewhere (hint, hint) and loop over them (more hint, hint).
Let us know if this helps or at the very least points you in the direction you want.
Screenshots:
Set-up:
Result:

Related

Optimizing large data set using VBA (Solver not working)

I have a large set of data on which I need to obtain a set of values that reach to a desired number. To be more precise, I have supermarket tickets each of them with one ID number and the amount that was charged to the customer.
I have to pick up tickets that add to a specific number (let's say 1,000,000 USD). I tried using solver where in one column you use a binary 1/0 variable and then just sumif to get the ones that solver selects that add up to the target value of 1M. However, this works when the sample is small. The file i just got has over 280,000 tickets, so there is no way solver can handle that number of binary variables.
How can I get in one column the number of tickets that add up to 1,000,000? I tried using a "while" loop but my computer crashes after 5 min or so. Any ideas?
Thanks!
Is this what you want? Enter the logic in A1:A11.
=RANDBETWEEN(1,50)
=IFERROR(RANDBETWEEN(1,100-SUM($A$1:A1)),0)
=IFERROR(RANDBETWEEN(1,100-SUM($A$1:A2)),0)
=IFERROR(RANDBETWEEN(1,100-SUM($A$1:A3)),0)
=IFERROR(RANDBETWEEN(1,100-SUM($A$1:A4)),0)
=IFERROR(RANDBETWEEN(1,100-SUM($A$1:A5)),0)
=IFERROR(RANDBETWEEN(1,100-SUM($A$1:A6)),0)
=IFERROR(RANDBETWEEN(1,100-SUM($A$1:A7)),0)
=IFERROR(RANDBETWEEN(1,100-SUM($A$1:A8)),0)
=IFERROR(RANDBETWEEN(1,100-SUM($A$1:A9)),0)
=SUM(A1:A10)
Result
Or, maybe this. Enter this in A1:A41
8
6
3
2
6
10
9
4
12
8
6
1
8
10
8
14
10
9
12
12
14
6
4
3
4
4
4
0
6
10
4
9
6
3
11
12
10
7
12
8
8
Put 92 in cell B1, and run this VBA.
Sub FindSeries()
Dim StartRng As Range
Dim EndRng As Range
Dim Answer As Long
Dim TestTotal As Long
Answer = Range("B1") '<<< CHANGE
Set StartRng = Range("A1")
Set EndRng = StartRng
Do Until False
TestTotal = Application.Sum(Range(StartRng, EndRng))
If TestTotal = Answer Then
Range(StartRng, EndRng).Select
Exit Do
ElseIf TestTotal > Answer Then
Set StartRng = StartRng(2, 1)
Set EndRng = StartRng
Else
Set EndRng = EndRng(2, 1)
If EndRng.Value = vbNullString Then
MsgBox "No series found"
Exit Do
End If
End If
Loop
End Sub

vba dynamic sum based on dynamic range values

I would like to thank everyone for their feedback so far it has helped a great deal. one question that I am grappling with is how to make my column values even so I can do a dynamic subtototal.
Column k Column R Column Y Column AF Column AM Column AT
1 2 4 2 3 5
3 9 7 8 2 4
2 3 6 3 5 8
3 3 2
5
TOT 9 14 25 13 12 17
Column k Column R Column Y Column AF Column AM Column AT
1 2 4 2 3 5
3 9 7 8 2 4
2 3 6 3 5 8
3 3 2
5
TOT 9 14 25 13 12 17
on a monthly basis the column values can fluctuate, the question is how do I use VBA to create a dynamic sum based on the column with the most values.
Dim Rin As Range
Dim Rout As Range
Dim lRa As Long
lRa = Range("i" & Rows.count).End(xlUp).Row
Set Rin = ws.Range("i" & lRa)
Set Rout = ws.Range("I" & lRa)
aCell.Range("I11:P12", "R12:AY12").Copy
Rout.Offset(2, 0).Resize(1, 28).Formula = "=SUBTOTAL(9," &
Rin.Address(0, 0) & ")"
lR = ws.Range("I" & Rows.count).End(xlUp).Row - 1 ' Finds the last blank
row
ws.Range("I" & lR).PasteSpecial xlPasteFormats
If you know where your data starts you can use a method such as that given by Shai Rado.
You can't have any entirely empty rows or columns in the range.
You can then feed this lastRow variable into the range address for adding your subtotal formula.
Example: If your data is a continuous set of populated columns starting at Cell D3 the following will get the last used row number in the range of columns:
Option Explicit
Public Sub AddSubTotal()
Dim lastRow As Long
Dim lastCol As Long
Dim firstRow As Long
Dim rowHeight As Long
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Sheet2") 'change as appropriate
With ws.Range("D3").CurrentRegion 'change as appropriate
firstRow = .Rows(1).Row
lastRow = .Rows(.Rows.Count).Row
lastCol = .Columns(.Columns.Count).Column
rowHeight = lastRow - firstRow + 1
End With
With ws
.Range(.Cells(lastRow + 1, "D"), .Cells(lastRow + 1, lastCol)).FormulaR1C1 = "=SUBTOTAL(9,R[-" & rowHeight & "]C:R[-1]C)"
End With
End Sub
If you need a different method to find the last used row and last used column there a lots of available resources online including my favourite by Ron De Bruin Find last row, column or last cell. The appropriateness of each method is determined by the shape and properties of your range e.g. no blank columns or rows in range. So choose a method that is right for your data or that can test for different likely cases and apply different methodologies as appropriate.
It is quite difficult to give a definitive answer (and i don't want to code lots of different possible scenarios) to this question without knowing more about the nature and variability of the data. If you familiarise yourself with the methods of finding last row and column you will be able to implement those that suit your needs.

Index of range with multiple non-sequential columns in VBA

Consider the following VBA MWE
Sub test()
Dim rng As Range
Set rng = Range("A:A, C:C")
Dim rRow As Range
For i = 1 To 5
Set rRow = Intersect(rng, rng.Cells(i, 1).EntireRow)
rRow.Value = 1
rRow.Cells(, 2).Value = 2
Next
End Sub
Which produces an output that looks like this
1 2 1
1 2 1
1 2 1
1 2 1
1 2 1
As we can see, the line rRow.Value = 1 sets the cells in the first and third column to 1. Now, I can't get my head around why the rRow.Cells(1,2) doesn't access the third column such that the output is
1 2
1 2
1 2
1 2
1 2
and leave the second column empty, since this appears to be what is happening in the line rRow.Value = 1. Can someone explain this logic to me?
EDIT:
Commenting out the rRow.Cells(,2).Value = 2 such that the code reads
Sub test()
Dim rng As Range
Set rng = Range("A:A, C:C")
Dim rRow As Range
For i = 1 To 5
Set rRow = Intersect(rng, rng.Cells(i, 1).EntireRow)
rRow.Value = 1
'rRow.Cells(, 2).Value = 2
Next
End Sub
yields the following output
1 1
1 1
1 1
1 1
1 1
where columns A and C are filled with ones, and column B is left alone.
Using the Range or Cells property of a Range object (rather than the more usual Worksheet), provides a reference to a range relative to the top left cell of the original range. It is not in any way restricted to cells within that original range.
Hence this:
Range("A1").Range("B2")
refers to the cell one column to the right and one row below A1. So does this:
Range("A1:A10").Range("B2")
Note that it still only refers to one cell, even though the original range was 10 cells. Cells works in exactly the same way (just as it does with a Worksheet parent). So this:
Range("A1").Cells(2, 2)
and this:
Range("A1:A10").Cells(2, 2)
both refer to B2.

Copy and paste x times based on a variable

I have a list of tournament competitors. If the list of competitors exceeds 12 then I need to split the list as equally as possible over 2 tournament rings (ie, if 13 then 7 in 'Ring 1' and 6 in 'Ring 2'). The varibles that dictate how the competitors are split are contained in cell D35 & D37. So I need a piece of code that looks at the value in cell D35 and then carries out a copy a paste sequence (to populate the Ring 1 range) THAT many times. It then needs to do the same for the value shown in D37. Can anyone help with this. I've had a look around on here and the NET and I'm still struggling.
Also, how do I post a sample of my workbook to show what I'm trying to do with the above question??
Kind Regards
Andy
Just split your task in some simple tasks:
-Check if more than 12 competitors
-if more than 12, divide in 2 rings; if not only use 1 ring
-copy the competitors, paste them in the specified ring
(i did not really understand what you meant with your variables ind D35 and D37, however i am not yet allowed to make comments to your post. I will alter this answer if you comment)
I am assuming your list of competitors starts in Cell A1
Sub process()
Dim countCompetitors As Integer
Dim amountRing1 As Integer
Dim amountRing2 As Integer
Dim rng1 As Range
Dim rng2 As Range
countCompetitors = 1
'count the amount of competitors
While Not IsEmpty(ActiveSheet.Cells(countCompetitors, 1))
countCompetitors = countCompetitors + 1
Wend
countCompetitors = countCompetitors - 1
If countCompetitors Mod 2 = 0 Then
'amount of competitors is even
amountRing1 = countCompetitors / 2
amountRing2 = countCompetitors / 2
Else
'amount of competitors is odd
amountRing1 = countCompetitors / 2
amountRing2 = countCompetitors / 2 - 1
End If
'rng1 and rng2 now contain the competitors
Set rng1 = ActiveSheet.Range(Cells(1, 1), Cells(amountRing1, 1))
Set rng2 = ActiveSheet.Range(Cells(amountRing1 + 1, 1), Cells(amountRing1 + amountRing2, 1))
'copy ring1 in column B and ring2 in column C
rng1.Copy ActiveSheet.Range("B2")
rng2.Copy ActiveSheet.Range("C2")
End Sub
I hope i could help you a bit with that, as already mentioned, please explain what you mean with the variables in Cells D35 and D37, I will alter my code accordingly ;)

Inserting blank rows every x number of rows [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Insert row every X rows in excel
I have a large set of data (let's say it goes from B5 to J500 and let's say this range is named rngOutput). I am trying to go through this data and add 2 empty rows every x number of rows where x is a number the user specifies. For example if x is 10 then every 10 rows 2 new rows should be inserted. Conceptually, this is the code that should work:
For i = 1 to Number of rows in rngOutput
If i mod x = 0 Then
Insert 2 Rows
End If
Next i
However, when you insert 2 new rows, the row count changes and the formula messes up (i.e. it adds 2 rows after the first 10 rows, then it adds another 2 rows after the next 8 rows (since it counts those 2 new rows you added as actual rows) then it adds another 2 rows after the next 6 rows, etc.
I am trying to figure out a way to accomplish adding 2 new rows every x number of rows cleanly to avoid the above problem.
Thank you for the help and please let me know if you need additional clarification!
This is like Chris's only fleshed out. When inserting or deleting rows you have to work up from the bottom:
Sub InsertXRowsEveryYRows_WithMeaningfulVariableNames()
Dim NumRowsToInsert As Long
Dim RowIncrement As Long
Dim ws As Excel.Worksheet
Dim LastRow As Long
Dim LastEvenlyDivisibleRow
Dim i As Long
NumRowsToInsert = 2 'any number greater than 0
RowIncrement = 10 'ditto
Set ws = ActiveSheet
With ws
LastRow = .Range("A" & .Rows.Count).End(xlUp).Row
LastEvenlyDivisibleRow = Int(LastRow / RowIncrement) * RowIncrement
If LastEvenlyDivisibleRow = 0 Then
Exit Sub
End If
Application.ScreenUpdating = False
For i = LastEvenlyDivisibleRow To 1 Step -RowIncrement
.Range(i & ":" & i + (NumRowsToInsert - 1)).Insert xlShiftDown
Next i
End With
Application.ScreenUpdating = True
End Sub
Count from the bottom of the range
For i = Number of rows in rngOutput to 1 step -1
If i mod x = 0 Then
Insert 2 Rows
End If
Next i