Excel VBA Macro to split long row into many of equal length - excel

I am currently working on a problem where I have an Excel Spreadsheet that I would like to use a VBA macro on. Each of the following 3 rows are consecutive.
Name of Data
abc A1 B2 B4 C4 E2 F43 d4 V8 f9 k11 j20 … x
cde A2 B3 B12 C6 E9 F34 d6 V4 f13 k111 j209 … x
efg A3 B5 B7 C8 E11 F68 d19 V12 f91 k114 j2014 … x
…
Desired
abc A1 B2 B4 C4 E2 F43 d4 V8
abc f9 k11 j20 …
cde A2 B3 B12 C6 E9 F34 d6 V4
cde f13 k111 j209 …
efg A3 B5 B7 C8 E11 F68 d19 V12
efg f91 k114 j2014 …
I have the data name for each row and some rows can be hundreds of entries long spanning hundreds columns. So what I would like to do would be make my row length stop at 8 columns wide. I would hope that the macro can check through each row to see if the length is greater than 8, insert a row with the same data name and paste the next 8 columns, subtract it from the total columns and paste the next row, until it has reached the end of the first long row, and continue checking through all the rows. In essense, it saves a whole lot of time from counting up 8 columns wide, cut and pasting it in an inserted row below, preserving all other data. I'm new to this, so macro or VBA help is much appreciated.
Thanks,
John

The below macro will do exactly as you ask. It has some assumptions that I'll leave to you to fix, such as
Data is in sheet 1
Name column is always A, all data columns start from B
Everything starts in cell A1
This macro will run through every row and, for those rows with more than 9 data elements, it will create a new row and populate it with the previous rows Name and the remaining data rows. It will continue doing this until there is less than or equal to 8 data elements per row.
Since you have MANY rows you say, it would be a good idea to turn off screen updating, like so Application.ScreenUpdating = False before the for loop and turn it back on after the for loop.
Public Sub SplitRows()
Dim rowRange As Variant
Dim colCount As Integer
Dim lastColumn As Long
Dim rowCount As Integer
rowCount = Cells(Rows.Count, "A").End(xlUp).Row
Dim i As Integer
i = 1
Do While (i < rowCount)
lastColumn = Sheet1.Cells(i, Columns.Count).End(xlToLeft).Column
colCount = Sheet1.UsedRange.Columns.Count
rowRange = Range(Cells(i, 2), Cells(i, colCount))
'if the row has more than 9 values (name column + 8 data columns)
If Not lastColumn <= 8 Then
Dim x As Integer
'from column 2 (B, aka first data column) to last column
For x = 2 To colCount - 1
'if data is not empty AND x mod 8 is 1 (meaning 8 goes into x enough times to have a remainder of 1)
If Not IsEmpty(rowRange(1, x - 1)) And (x Mod 8) = 1 Then
Cells(i, 1).Offset(1).EntireRow.Insert 'insert new row below current row
rowCount = rowCount + 1 'update row count because we added a row
Sheet1.Cells(i + 1, 1).Value = Sheet1.Cells(i, 1).Value 'set first column name
Dim colsLeft As Integer
For colsLeft = x To colCount - 1
'take data value from col 9 to end and populate newly created row
Sheet1.Cells(i + 1, colsLeft - 7).Value = rowRange(1, colsLeft)
Sheet1.Cells(i, colsLeft + 1).Value = "" 'set data value from col 9 on and set to empty
Next
Exit For 'exit loop, weve done all we need to and must now check the newly populated row
End If
Next
End If
i = i + 1
Loop
End Sub
Here is a before and after of the results:
BEFORE
AFTER

Argh, I tried somewhat along this lines, but I have to go to work. Maybe it is helpful as a starting point.
Public Sub Test()
Dim mastercell As Range
Set mastercell = ActiveWorkbook.Worksheets(1).Cells(1, 1)
Dim masterValue As String
masterValue = mastercell.Value
If GetCount(masterValue) > 8 Then
Dim tempvalue As String
tempvalue = masterValue
Dim Rowcount As Integer
Dim ColCount As Integer
Rowcount = mastercell.Row
ColCount = mastercell.Column + 1
Do While GetCount(tempvalue) > 8
Dim WriteValue As String
WriteValue = GetFirstEight(tempvalue)
ActiveWorkbook.Worksheets(1).Cells(Rowcount, ColCount).Value = WriteValue
ColCount = ColCount + 1
tempvalue = Replace(tempvalue, WriteValue, 0, 1)
Loop
End If
End Sub
Private Function GetCount(str As String) As Integer
Dim Splitter As String
Splitter = " "
Dim SplitArray As Variant
SplitArray = Split(str)
GetCount = UBound(SplitArray)
End Function
Private Function GetFirstEight(str As String) As String
Dim i As Integer
Dim NewString As String
Dim SplitArray() As String
SplitArray = Split(str)
For i = 0 To 7
NewString = NewString & SplitArray(i) & " "
Next
GetFirstEight = NewString
End Function

Related

Excel search and copy from more sheet

I have a file.xls with three sheets.
Sheet1, 2 columns, 3000 rows;
ColumnA: location_id
ColumnB: location_label
Sheet2, 2 columns, 5000 rows;
ColumnA: location_id
ColumnB: screen_id
Sheet3, 2 columns, 6000 rows;
ColumnA: screen_id
ColumnB: screen_name
how to group data into a new sheet4 with the following syntax (view image);
ColumnA: Location_label
ColumnB: screen_name
location_id get location_label name in sheet 1, location_id get screen_id value in sheet2, screen_id get screen_name value in sheet3 and in sheet4 result with location_label and screen_name.
#EDIT QUESTION WITH USE VLOOKUP;
I tried to use VLOOKUP but from error after the first id number 19 of sheet2...i have used this '
=VLOOKUP(Sheet2!A2;Sheet1!A2:B2133;2;)
i get
RED
GREEN
YELLOW
#N/D
#N/D
Public Sub pair_value()
'here i tried to deconstruct the code so it is easy to follow
'this type of paring would work much better with access
'you can use this code to start
Dim h1 As Integer 'row count in sheet1
Dim h2 As Integer 'row count in sheet2
Dim h3 As Integer 'row count in sheet3
Dim h4 As Integer 'row count in sheet4
Dim ar1() As Variant
Dim ar2() As Variant
Dim ar3() As Variant
Dim ar4() As Variant
Dim pair1() As Variant
Dim range1 As Range
Dim range2 As Range
Dim range3 As Range
Dim i As Integer
Dim j As Integer
'---------------------------------------------------
'This exercise would be so much easier using ACCESS
'---------------------------------------------------
'number of rows in each sheets
h1 = Worksheets(1).Cells(Rows.Count, 1).End(xlUp).Row
h2 = Worksheets(2).Cells(Rows.Count, 1).End(xlUp).Row
h3 = Worksheets(3).Cells(Rows.Count, 1).End(xlUp).Row
h4 = Worksheets(4).Cells(Rows.Count, 1).End(xlUp).Row
'define arrays
ReDim ar1(h1, 2)
ReDim ar2(h2, 2)
ReDim ar3(h3, 2)
ReDim pair1(h2, 2)
'set range
Set range1 = Worksheets(1).Range(Worksheets(1).Cells(2, 1), Worksheets(1).Cells(h1, 2))
Set range2 = Worksheets(2).Range(Worksheets(2).Cells(2, 1), Worksheets(2).Cells(h2, 2))
Set range3 = Worksheets(3).Range(Worksheets(3).Cells(1, 1), Worksheets(3).Cells(h3, 2))
'load range into arrays
ar1 = range1
ar2 = range2
ar3 = range3
'associate location_label to screen_id using location_id as primary key
For i = 1 To UBound(ar2)
For j = 1 To UBound(ar1)
If ar2(i, 1) = ar1(j, 1) Then
'load screen id + label in pair1 in pair1 array
pair1(i, 1) = ar2(i, 2)
pair1(i, 2) = ar1(j, 2)
End If
Next j
Next i
'associate location_label to screen_name using screen_id as primary key
For i = 1 To UBound(ar3)
For j = 1 To UBound(pair1)
If ar3(i, 1) = pair1(j, 1) Then
Debug.Print j
'past results in sheets(4)
h4 = Worksheets(4).Cells(Rows.Count, 1).End(xlUp).Row
Worksheets(4).Cells(h4 + 1, 1).Value = pair1(j, 2)
Worksheets(4).Cells(h4 + 1, 2).Value = ar3(i, 2)
End If
Next j
Next i
End Sub
Well I'm not the best with Excel and I do not fully understand what you want as an end result. But here is my guess what I think is the problem.
I suspect that in sheet 4 you type you code in A2 and use your mouse to drag the formula to the bottom of the column. If this is the case, your formula will not be exactly the same in every cell, because your matrix in the formula will change while you drag your mouse. Therefore the error #N/D
To prevent your matrix from changing while dragging your mouse you should use '$'.
So your formula would be:
=VLOOKUP(Sheet2!A2;Sheet1!$A$2:$B$2133;2;)
Possibly a tip:
I see you have the same columns in more than 1 sheet. You can put all you data in 1 sheet and then use filters to select the data you want. See this article.

Adding numbers in a column

I have written this code to add numbers in a column. It is not adding the last cell.
For example if there are three numbers 1, 2 and 3 it will sum up 1 and 2 and ignore value in third cell. When there is a fourth number 1, 2, 3 and 4 it adds 1, 2 and 3.
Sub add()
Dim Rng As Range, a As Integer
Set Rng = Range("b2", Range("b2").End(xlDown))
Counter = Rng.Count
a = 0
For i = 2 To Counter
a = a + Cells(i, "B").Value
Next i
ActiveCell.Value = a
End Sub
Let's say the Counter = Rng.Count gives 4, and you start your i = 2 (possibly to keep title of your column). Your code will not print 4 digits, because you start loop from 2.
The counter should look like this:
Counter = Rng.Count + 1
and it will work
It is because .End(xlDown) - it works like CTRL + downarrow, and ends on the last not empty, or first not empty cell in the column. That's why when you delete value in specific row it will "break" the range. If you want to scan all rows, no matter if it's empty or not use the loop from the first row, and you will get the sum of the whole column range (starting from row 2 of course):
Sub SumWholeColumn()
'give the sum of all numbers in column B, starting from B2
Dim i, a As Long
Dim column, addr As String
a = 0
column = "b"
For i = 2 To Rows.Count
addr = column & i
a = a + Cells(i, "B").Value
Next i
ActiveCell.Value = a
End Sub
If your range is fixed, you can speed up the calculation process by setting the range manually. Otherwise it will scan ALL rows. For example if you know, that your random numbers will not exceed row 1000, then you can use something like this:
Sub SumWholeColumn()
'give the sum of all numbers in column B, starting from B2
Dim i, a As Long
Dim maxRows As Integer
Dim column, addr As String
a = 0
column = "b"
maxRows = 1000
For i = 2 To maxRows
addr = column & i
a = a + Cells(i, "B").Value
Next i
ActiveCell.Value = a
End Sub
Well I think that at the first time I did not understood your point then, I thought you would like to paste numbers in column B from 0 to the last row, starting from the B2 address. If so - this will work:
Sub add()
Dim i, a As Long
Dim column As String
Dim addr As String
a = 0
column = "b"
For i = 2 To Rows.Count
addr = column & i
ActiveSheet.Range(addr).Value = a
a = a + 1
Next i
End Sub
but today I realised that your title "Adding numbers in a column via Excel VBA
" is wrong and probably you are trying to achieve something else (because you are trying to give some value in ActiveCell?) and if so, please correct me:
you have actually some numbers in column B, and you would like to give in the ActiveCell the sum of all those numbers? The answer for this will be:
Sub SumAll()
'give the sum of all numbers in column B, starting from B2
Dim Rng As Range
Dim a, i As Long
Set Rng = Range("b2", Range("b2").End(xlDown))
Counter = Rng.Count + 1
a = 0
For i = 2 To Counter
a = a + Cells(i, "B").Value
Next i
ActiveCell.Value = a
End Sub
You need to use "a" as Long, because Integer is up to 2147483647 and if you fill all rows in the column, starting from 0 and iterate the number by 1 to the last row, and sum the values it will give you 2147319811 - out of the Integer scope.
i value can be Integer (not Long as in my example), because "i" max value will not exceed the scope (Workbook rows are limited to 1048576). You can safely change i to Integer and save some KB's of memory :)

how to split data by IF

i need know to know how
i can add many rows as every time i write in Cell A3 the split data go to row 5 only but i need it go to the row 6 then 7 and so on
i can make 2 conditions:
if the cell A3 content number that number is start with "0" transfer to number column "column E" E5
if the Cell A3 content (( *.com or *.net )) that word transfer to product column "column B" B5
how i can add more delimiters like (//,/,-)?
Sub SplitText1()
Const SourceRange = "A3" ' <-- Source data range, change to suit
Const DestCell = "a5" ' <-- First destination cell, change to suit
Dim a As Variant, cell As Range, i As Long
For Each cell In Range(SourceRange)
a = split(cell.Value, "//")
ReDim Preserve a(0 To 6)
If Val(a(3)) = a(3) Then
a(4) = a(3)
a(3) = Empty
End If
Range(DestCell).Offset(i).Resize(, 6).Value = a
i = i + 1
Next
End Sub

Excel: How to get in array only cells with text starting with "SIM"

In excel I have column A with some numbers and letters and I would like to extract to a different column all the cells that start with "SIM" (ex, sim 3; sim 4; sim 5) and its correspondent value on column B and column C. I tried different methods using arrays but none worked as you can see the attached link. Link 2 is what I want the result to be.
Thank you very much.
Code tried
Desirable result
This can be achieved with a piece of VBA code. Am checking if the string in Col A starts with "SIM", Then I copy Column A, B, C to Column D, E, F
Change the column names as per your requirements
Sub findsim()
Dim rowcount As Integer
Dim Checkcol1 As Integer, Checkcol2 As Integer, Checkcol3 As Integer, Targetcol As Integer
Dim currentRowValue1 As String
Dim currentRow As Integer, arow As Integer
Checkcol1 = 1 'Denotes A column
Checkcol2 = 2 'Denotes B column
Checkcol3 = 3 'Denotes B column
Targetcol = 4
arow = 1
rowcount = Cells(Rows.Count, Checkcol1).End(xlUp).Row
For currentRow = 1 To rowcount
currentRowValue1 = Cells(currentRow, Checkcol1).Value
If UCase(Left$(currentRowValue1, 3)) = "SIM" Then
Cells(arow, Targetcol).Value = currentRowValue1
Cells(arow, Targetcol + 1).Value = Cells(currentRow, Checkcol2).Value
Cells(arow, Targetcol + 2).Value = Cells(currentRow, Checkcol3).Value
arow = arow + 1
End If
Next
End Sub
Edit
For excel formulas
In first target col say D column, paste this
=IF(UPPER(MID(A1,1, 3)) = "SIM", B1)
In Second target col say E column, paste this
=IF(UPPER(MID(A1,1, 3)) = "SIM", C1)
Note :- You will get blank cells for those which did not have SIM string. you have to manually delete the spaces

Copy top 3 values from data to another column

I want a macro that can find top 3 values from my sample data between columns B to G, then copy and paste the value to another column in Column Q with the row it is from (within Column A and the row (Row A) it came from.
Eg.
D1 D2 D3
Seq RowA ColumnA
T1 10 20 30 After running macro: T1 D3 30
T2 11 22 2 T2 D2 22
T3 2 3 10 T4 D3 21
T4 6 19 21
Sub Top3()
Dim rng As Range
Dim i As Integer
Dim r As Integer
Range("B2").CurrentRegion.Copy
Range("Q2").PasteSpecial Paste:=xlPasteValues
Range("Q2").CurrentRegion.Sort Key1:=Range("B2"), Order1:=xlDescending
r = 5
For i = 1 To 3
Cells(r, "R") = Cells(r + 1, "A")
Cells(r, "S") = Cells(r + 1, "A")
r = r + 1
Next
Range("B2").CurrentRegion.Clear
Range("C2").Activate
End Sub
Target: All the Cells in the list of values
Target is a range that is 3 Columns x 4 Rows Target.Address = $B$2:$D$5
Target(1) refers to the first cell
Target(Target.Cells.Count) is the last cell
Target(x) is bound by its Columns
Target(4) is the same as saying Target.Cells(2, 1)
Target(x) is not bound by its Rows
The first column in Target is column B and the last row is 5. The next cell outside of the Target range is column B row 6
Target(Target.Cells.Count + 1).Address = $B$6
With this information it we can create a 1 dimensional array of Data that indices and values match that of Target. Using a 1D array makes it easy to reference cells in the Target range using built-in Excel WorksheetFunctions.
Sub GetTop3()
Dim Data
Dim Target As Range
Dim index As Long, x As Long
Set Target = Range("Offset(B1,1,0,Counta(B:B)-1,3)")
ReDim Data(1 To Target.Cells.Count)
For x = 1 To Target.Cells.Count
Data(x) = Target(x)
Next
For x = 2 To 4
index = WorksheetFunction.Match(WorksheetFunction.Max(Data), Data, 0)
With Target(index)
Cells(x, "R") = Rows(.Row).Columns("A")
Cells(x, "S") = Cells(1, .Column)
Cells(x, "T") = .Value
End With
Data(index) = vbNullString
Next
End Sub
This blog shows how to resize a range using the Offset WorksheetFunction: Advanced Excel Dynamic Named Ranges

Resources