Working with jagged arrays, printing sub-array to sheet, vba - excel

Background:
Was trying to come up with efficient ways to handle a large amount of tables on a single sheet and came across Jagged Arrays (herein "Jars").
To simply understand some basics of Jars, I was trying to build a simple scenario of staggered information to be able to create the Jar.
My Jar is labeled big_arr and each array inside is called lil_arr.
Here is the data for the scenario:
ColA 'adding row number in front of each word
1 cat
2 dog
3
4 mouse
5 elephant
6
7 zebra
8 snake
9
10 cheese
11 pickle
12
13 anteater
14 mirkat
15
16 skunk
17 smurf
In the above scenario, big_arr(2) = lil_arr where `lil_arr = array("mouse","elephant").
I would then have big_arr(i) print to a sheet; the sheet is labeled as i, when looping. So sheet 2 would have cells(1,1).value = "mouse" and cells(1,2).value = "elephant".
Issue:
I am having issues getting the data to print as expected.
The exact printing that is happening (based on i as the sheet name):
1 has cells(1,1).value = 0
2 has cells(1,1).value = "skunk"
3 has cells(1,1).value = 0
4 has cells(1,1).value = 0
5 has cells(1,1).value = 0
6 has cells(1,1).value = 0
I don't seem to be able to print using Application.Transpose(big_arr(i)). I have attempted to loop, but don't seem to have appropriate syntax.
Question:
Any help to resolve the issue with Application.Transpose(), which does not trigger an error message, would be appreciated.
Otherwise, help to get the loop to work with appropriate syntax would be phenomenal.
Code in question:
Code with Application.Transpose() for printing
Sub create_jagged_array_of_tables()
Dim big_arr As Variant, lil_arr As Variant, lr As Long, i As Long, j As Long, k As Long, ws As Worksheet
lr = Cells(Rows.Count, 1).End(xlUp).Row
ReDim big_arr(1 To lr)
For i = 1 To lr
j = 1
Do Until IsEmpty(Cells(i + j, 1))
j = j + 1
Loop
If j > 1 Then
lil_arr = Cells(i, 1).Resize(j).Value
big_arr(j) = lil_arr
i = i + j
k = k + 1
Else
MsgBox "row " & i & " is not part of an array"
End If
Next i
For i = 1 To k
Set ws = Sheets.Add
ws.Name = i
Cells(1, 1).Value = Application.Transpose(big_arr(i))
Next i
End Sub
Code for the loop I attempted, giving type-mismatch, focusing only on the for i = 1 to k loop:
For i = 1 To k
Set ws = Sheets.Add
ws.Name = i
'Cells(1, 1).Value = Application.Transpose(big_arr(i))
For j = 1 To UBound(big_arr(i), 1)
Cells(j, 1).Value = big_arr(i)(j)
Next j
Next i

In this case j will always = 2 at the line:
big_arr(j) = lil_arr
so you keep overwriting that.
I assume you want to use k instead of j for the counter of big_arr:
big_arr(k) = lil_arr
But that will require you to have a k=1 before the i loop.
Also you need to resize the output to the size of the lil_array:
Sub create_jagged_array_of_tables()
Dim big_arr As Variant, lil_arr As Variant, lr As Long, i As Long, j As Long, k As Long, ws As Worksheet
lr = Cells(Rows.Count, 1).End(xlUp).Row
ReDim big_arr(1 To lr)
k = 0
For i = 1 To lr
j = 1
Do Until IsEmpty(Cells(i + j, 1))
j = j + 1
Loop
If j > 1 Then
lil_arr = Cells(i, 1).Resize(j).Value
k = k + 1
big_arr(k) = lil_arr
i = i + j
Else
MsgBox "row " & i & " is not part of an array"
End If
Next i
For i = 1 To k
Set ws = Sheets.Add
ws.Name = i
Cells(1, 1).Resize(1, UBound(big_arr(i), 1)).Value = Application.Transpose(big_arr(i))
Next i
End Sub

Did just a little tweaking and it's working for me:
Sub create_jagged_array_of_tables()
Dim big_arr As Variant, lil_arr As Variant, lr As Long, i As Long, j As Long, k As Long, ws As Worksheet
lr = Cells(Rows.Count, 1).End(xlUp).Row
Dim big_arr_size As Long
' Assumes you have groups of 2 per small array
big_arr_size = WorksheetFunction.CountA(Range("A1:A" & lr)) / 2
ReDim big_arr(1 To big_arr_size)
k = 1
For i = 1 To lr
j = 1
Do Until IsEmpty(Cells(i + j, 1))
j = j + 1
Loop
If j > 1 Then
lil_arr = Cells(i, 1).Resize(j).Value
big_arr(k) = lil_arr ' changed `j` to `k`
i = i + j
k = k + 1
Else
MsgBox "row " & i & " is not part of an array"
End If
Next i
For i = 1 To big_arr_size
Set ws = Sheets.Add
ws.Name = i
ws.Cells(1, 1).Value = big_arr(i)(1, 1)
ws.Cells(1, 2).Value = big_arr(i)(2, 1)
Next i
End Sub
Edit: Here's a perhaps different way you can do this. It avoids using a "small array" to set as part of a larger array.
Sub t()
Dim big_arr As Variant
Dim lr As Long
lr = Sheets("Sheet1").Cells(Rows.Count, 1).End(xlUp).Row
Dim big_arr_size As Long
' Assumes you have groups of 2 per small array
big_arr_size = WorksheetFunction.CountA(Range("A1:A" & lr)) / 2
ReDim big_arr(1 To lr)
big_arr = Range("A1:A" & lr).Value
Dim i As Long, wsName As Long
Dim ws As Worksheet
wsName = LBound(big_arr)
For i = LBound(big_arr) To UBound(big_arr) - 1
If Not IsEmpty(big_arr(i, 1)) And Not IsEmpty(big_arr(i + 1, 1)) Then
Set ws = Sheets.Add
ws.Name = wsName
ws.Cells(1, 1).Value = big_arr(i, 1)
ws.Cells(1, 2).Value = big_arr(i + 1, 1)
wsName = wsName + 1
End If
Next i
End Sub

The Post already had two brilliant answers (one accepted) and both have there unique characteristics. But just want to share some of my idea since I find the the post highly interesting. I just tried to simplify the creation of jagged array using single loop using a flag and avoided transpose. May please not taken as contravention.
Sub create_jagged_array_of_tables()
Dim big_arr() As Variant, lil_arr As Variant, lr As Long, i As Long, j As Long, k As Long, ws As Worksheet
Dim Nw As Boolean, Xval As Variant
lr = Cells(Rows.Count, 1).End(xlUp).Row
k = 0
j = 0
For i = 1 To lr
Xval = Cells(i, 1).Value
If IsEmpty(Xval) = False Then
If Nw = False Then
Nw = True
k = k + 1
j = 1
ReDim lil_arr(1 To 1, 1 To j)
lil_arr(1, j) = Xval
ReDim Preserve big_arr(1 To k)
big_arr(k) = lil_arr
Else
j = j + 1
ReDim Preserve lil_arr(1 To 1, 1 To j)
lil_arr(1, j) = Xval
big_arr(k) = lil_arr
End If
Else
Nw = False
End If
Next i
For i = 1 To k
Set ws = Sheets.Add
ws.Name = i
Cells(1, 1).Resize(1, UBound(big_arr(i), 2)).Value = big_arr(i)
Next i
End Sub
And if creation of jagged Array is not required and sole objective is to copy the content in the desired fashion, the it could be further simplified to
Sub test1()
Dim lr As Long, Rng As Range, Area As Range, Cnt As Long, Arr As Variant
lr = Cells(Rows.Count, 1).End(xlUp).Row
Set Rng = Range("A1:A" & lr)
Rng.AutoFilter Field:=1, Criteria1:="<>"
Set Rng = Rng.SpecialCells(xlCellTypeVisible)
Cnt = 0
For Each Area In Rng.Areas
Cnt = Cnt + 1
Set ws = Sheets.Add
ws.Name = Cnt
Arr = Area.Value
If IsArray(Arr) Then
ws.Cells(1, 1).Resize(UBound(Arr, 2), UBound(Arr, 1)).Value = Application.Transpose(Arr)
Else
ws.Cells(1, 1).Value = Arr
End If
Next
Rng.AutoFilter Field:=1
End Sub

Related

Separate each word after line breaks into new rows

My loop seems to create infinite rows and is bugging
For Each Cell In Workbooks(newBook).Sheets(1).Range("A1:A" & lRow)
Checker = Cell.Value
For Counter = 1 To Len(Checker)
If Mid(Checker, Counter, 1) = vbLf Then
holder = Right(Mid(Checker, Counter, Len(Checker)), Len(Checker))
Workbooks(newBook).Sheets(1).Range(Cell.Address).EntireRow.Insert
End If
Next
Next Cell
Use a reverse loop. For i = lRow to 1 Step -1. Also to separate word, you can use SPLIT().
Is this what you are trying?
Option Explicit
Sub Sample()
Dim ws As Worksheet
Dim lRow As Long
Dim i As Long, j As Long
Dim Ar As Variant
'~~> Change this to the relevant worksheet
Set ws = Sheet2
With ws
'~~> Find last row in Column A
lRow = .Range("A" & .Rows.Count).End(xlUp).Row
'~~> Reverse Loop in Column A
For i = lRow To 1 Step -1
'~~> Check if cell has vbLf
If InStr(1, .Cells(i, 1).Value, vbLf) Then
'~~> Split cell contents
Ar = Split(.Cells(i, 1).Value, vbLf)
'~~> Loop through the array from 2nd position
For j = LBound(Ar) + 1 To UBound(Ar)
.Rows(i + 1).Insert
.Cells(i + 1, 1).Value = Ar(j)
Next j
'~~> Replace cells contents with content from array from 1st position
.Cells(i, 1).Value = Ar(LBound(Ar))
End If
Next i
End With
End Sub
BEFORE
AFTER
This is my solution, works with 2 dimensional ranges as well and it works on Selection, so select the range with the cells you want to split and run the code.
Sub splitByNewLine()
Dim pasteCell As Range, rowCumulationTotal As Integer
rowCumulationTotal = 0
Dim arr() As Variant
arr = Selection
Selection.Clear
For i = 1 To UBound(arr)
Dim rowCumulationCurrent As Integer, maxElemsOnRow As Integer
rowCumulationCurrent = 0
maxElemsOnRow = 0
For j = 1 To UBound(arr, 2)
Dim elems() As String, elemCount As Integer
elems = Split(arr(i, j), vbLf)
elemCount = UBound(elems)
For k = 0 To elemCount
Cells(Selection.Row + i + rowCumulationTotal + k - 1, Selection.Column + j - 1) = elems(k)
If maxElemsOnRow < k Then
rowCumulationCurrent = rowCumulationCurrent + 1
maxElemsOnRow = k
End If
Next k
Next j
rowCumulationTotal = rowCumulationTotal + rowCumulationCurrent
Next i
Exit Sub
End Sub
Input:
Output:

How to split cell contents from multiple columns into rows by delimeter?

The code I have takes cells containing the delimiter (; ) from a column, and creates new rows (everything except the column is duplicated) to separate those values.
What I have
I need this for multiple columns in my data, but I don't want the data to overlap (ex: for 3 columns, I want there to be only one value per row in those 3 columns). It would be ideal if I could select multiple columns instead of only one as my code does now.
What I want
Sub splitByCol()
Dim r As Range, i As Long, ar
Set r = Worksheets("Sheet").Range("J2000").End(xlUp)
Do While r.Row > 1
ar = Split(r.Value, "; ")
If UBound(ar) >= 0 Then r.Value = ar(0)
For i = UBound(ar) To 1 Step -1
r.EntireRow.Copy
r.Offset(1).EntireRow.Insert
r.Offset(1).Value = ar(i)
Next
Set r = r.Offset(-1)
Loop
End Sub
Try this code
Sub Test()
Dim a, x, e, i As Long, ii As Long, iii As Long, k As Long
a = Range("A1").CurrentRegion.Value
ReDim b(1 To 1000, 1 To UBound(a, 2))
For i = LBound(a) To UBound(a)
For ii = 2 To 3
x = Split(a(i, ii), "; ")
For Each e In x
k = k + 1
b(k, 1) = k
b(k, 2) = IIf(ii = 2, e, Empty)
b(k, 3) = IIf(ii = 3, e, Empty)
b(k, 4) = a(i, 4)
Next e
Next ii
Next i
Range("A5").Resize(UBound(b, 1), UBound(b, 2)).Value = b
End Sub
I'd go this way
Sub SplitByCol()
With Worksheets("Sheet")
With .Range("B2", .Cells(.Rows.Count, "B").End(xlUp))
Dim firstColValues As Variant
firstColValues = .Value
Dim secondColValues As Variant
secondColValues = .Offset(, 1).Value
Dim thirdColValues As Variant
thirdColValues = .Offset(, 2).Value
.Offset(, -1).Resize(, 4).ClearContents
End With
Dim iRow As Long
For iRow = LBound(firstColValues) To UBound(firstColValues)
Dim currFirstColValues As Variant
currFirstColValues = Split(firstColValues(iRow, 1), "; ")
Dim currSecondColValues As Variant
currSecondColValues = Split(secondColValues(iRow, 1), "; ")
With .Cells(.Rows.Count, "C").End(xlUp).Offset(1, -1)
With .Resize(UBound(currFirstColValues) + 1)
.Value = currFirstColValues
.Offset(, 2).Value = thirdColValues(iRow, 1)
End With
End With
With .Cells(.Rows.Count, "B").End(xlUp).Offset(1, 1)
With .Resize(UBound(currSecondColValues) + 1)
.Value = currSecondColValues
.Offset(, 1).Value = thirdColValues(iRow, 1)
End With
End With
Next
End With
End Sub
Follow the code step by step by pressing F8 while the cursor is in any code line in the VBA IDE and watch what happens in the Excel user interface
EDIT
adding edited code for a more "parametric" handling by means of a helper function
Sub SplitByCol()
With Worksheets("Sheet")
With .Range("B2", .Cells(.Rows.Count, "B").End(xlUp))
Dim firstColValues As Variant
firstColValues = .Value
Dim secondColValues As Variant
secondColValues = .Offset(, 1).Value
Dim thirdColValues As Variant
thirdColValues = .Offset(, 2).Value
.Offset(, -1).Resize(, 4).ClearContents
End With
Dim iRow As Long
For iRow = LBound(firstColValues) To UBound(firstColValues)
Dim currFirstColValues As Variant
currFirstColValues = Split(firstColValues(iRow, 1), "; ")
Dim currSecondColValues As Variant
currSecondColValues = Split(secondColValues(iRow, 1), "; ")
WriteOne .Cells(.Rows.Count, "C").End(xlUp).Offset(1), _
currFirstColValues, thirdColValues(iRow, 1), _
-1, 2
WriteOne .Cells(.Rows.Count, "B").End(xlUp).Offset(1), _
currSecondColValues, thirdColValues(iRow, 1), _
1, 1
Next
End With
End Sub
Sub WriteOne(refCel As Range, _
currMainColValues As Variant, thirdColValue As Variant, _
mainValuesOffsetFromRefCel As Long, thirdColValuesOffsetFromRefCel As Long)
With refCel.Offset(, mainValuesOffsetFromRefCel)
With .Resize(UBound(currMainColValues) + 1)
.Value = currMainColValues
.Offset(, thirdColValuesOffsetFromRefCel).Value = thirdColValue
End With
End With
End Sub
Please, use the next code. It uses arrays and should be very fast for big ranges to be processed, working mostly in memory:
Sub testSplitInsert()
Dim sh As Worksheet, lastR As Long, arr, arrSp, arrFin, i As Long, j As Long, k As Long
Set sh = ActiveSheet
lastR = sh.Range("B" & sh.rows.count).End(xlUp).row
arr = sh.Range("B1:D" & lastR).Value
ReDim arrFin(1 To UBound(arr) * 10, 1 To 3) 'maximum to keep max 10 rows per each case
k = 1 'initialize the variable to load the final array
For i = 1 To UBound(arr)
arrSp = Split(Replace(arr(i, 1)," ",""), ";") 'trim for the case when somebody used Red;Blue, instead of Red; Blue
For j = 0 To UBound(arrSp)
arrFin(k, 1) = arrSp(j): arrFin(k, 3) = arr(i, 3): k = k + 1
Next j
arrSp = Split(Replace(arr(i, 1)," ",""), ";")
For j = 0 To UBound(arrSp)
arrFin(k, 2) = arrSp(j): arrFin(k, 3) = arr(i, 3): k = k + 1
Next j
Next
sh.Range("G1").Resize(k - 1, 3).Value = arrFin
End Sub
It processes the range in columns "B:D" and returns the result in columns "G:I". It can be easily adapted to process any columns range and return even overwriting the existing range, but this should be done only after checking that it return what you need...

Looping with variable data set

Based on the picture I would like for each animal to be copied to each Set/# (and for the outcome to be on Sheet 2).
Example of Goal
The issue is that it won't always be a set of 14 it can vary based on the data but the Animals would stay the same (no more then 4).
Below is what I have, granted it is not based on the picture. That is an example.
Sub DowithIf()
rw = 5
cl = 2
rw = 1000
Do While rw < erw
If Cells(rw, cl) <> Cells(rw - 1, cl) Then
Cells(rw, cl + 1) = Cells(rw, cl)
Range("A5:B5").Select
Selection.Copy
Sheets("Sheet2").Select
Range("A2").Select
ActiveSheet.Paste
Range("A2:B4").Select
Application.CutCopyMode = False
Selection.FillDown
Sheets("Data").Select
Range("E3:J5").Select
Selection.Copy
Sheets("Sheet2").Select
Range("C2").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
ElseIf Cells(rw, cl) = "" Then
Exit Do
End If
rw = rw + 1
Loop
End Sub
I think you'd find this easier if you looked at VBA as more of a programming language than a macro recorder. In your example, the task is really just to create an array whose row count is:
number of set names * number of set items
All you'd need to do is populate that array following a certain pattern. In your example it would be:
set number n with all set items, set number n + 1 with all set items, etc.
Skeleton code would look something like this:
Const SET_NAMES_ROW_START As Long = 6
Const SET_ITEMS_ROW_START As Long = 6
Const SET_NAMES_COL As String = "A"
Const SET_ITEMS_COL As String = "E"
Const OUTPUT_ROW_START As Long = 6
Const OUTPUT_COL As String = "G"
Dim names() As Variant, items() As Variant, output() As Variant
Dim namesCount As Long, itemsCount As Long
Dim idx As Long, nameIdx As Long, itemIdx As Long
'Read the set values.
With Sheet1
names = .Range( _
.Cells(SET_NAMES_ROW_START, SET_NAMES_COL), _
.Cells(.Rows.Count, SET_NAMES_COL).End(xlUp)) _
.Resize(, 2).Value2
items = .Range( _
.Cells(SET_ITEMS_ROW_START, SET_ITEMS_COL), _
.Cells(.Rows.Count, SET_ITEMS_COL).End(xlUp)) _
.Value2
End With
'Dimension the output array.
namesCount = UBound(names, 1)
itemsCount = UBound(items, 1)
ReDim output(1 To namesCount * itemsCount, 1 To 3)
'Populate the output array.
nameIdx = 1
itemIdx = 1
For idx = 1 To namesCount * itemsCount
output(idx, 1) = names(nameIdx, 1)
output(idx, 2) = names(nameIdx, 2)
output(idx, 3) = items(itemIdx, 1)
itemIdx = itemIdx + 1
If itemIdx > itemsCount Then
'Increment the name index by 1.
nameIdx = nameIdx + 1
'Reset the item index to 1.
itemIdx = 1
End If
Next
'Write array to the output sheet.
Sheet1.Cells(OUTPUT_ROW_START, OUTPUT_COL).Resize(UBound(output, 1), UBound(output, 2)).Value = output
So I think this will let you dynamically pick the size of your data set. I'm assuming that the column headers will always be at Row 5 as pictured. It loops through each input column and provides a unique output in H, I, and J. Disclaimer: I didn't get to test this as I'm not on my work PC.
Sub MixTheStuff()
'sets size of data in A (Set). -5 for the header row as noted
x = ThisWorkbook.Sheets("Data").Cells(Rows.Count, 1).End(xlUp).Row - 5
'sets size of data in B (#)
y = ThisWorkbook.Sheets("Data").Cells(Rows.Count, 2).End(xlUp).Row - 5
'sets size of data in E (Animal)
z = ThisWorkbook.Sheets("Data").Cells(Rows.Count, 5).End(xlUp).Row - 5
i=6 'First row after the headers
For sThing = 1 to x 'set thing
For nThing = 1 to y 'number thing
For aThing = 1 to z 'animal thing
'Pastes the value of the stuff (Set, #, and Animal respectively)
ThisWorkbook.Sheets("Data").cell(i,10) = ThisWorkbook.Sheets("Data").cell(x,1).value
ThisWorkbook.Sheets("Data").cell(i,11) = ThisWorkbook.Sheets("Data").cell(y,2).value
ThisWorkbook.Sheets("Data").cell(i,12) = ThisWorkbook.Sheets("Data").cell(z,5).value
i = i + 1 'Go to the next output row
Next sThing
Next nThing
Next aThing
End Sub
Sort of Unpivot
This will allow you to handle maximally 1023 animals.
The Code
Option Explicit
Sub SortOfUnpivot()
Const FirstRow As Long = 6
Const LastRowCol As String = "E"
Const dstFirstCell As String = "H6"
Dim srcCols As Variant
srcCols = VBA.Array("A", "B", "E")
Dim LB As Long
LB = LBound(srcCols)
Dim UB As Long
UB = UBound(srcCols)
Dim srcCount As Long
srcCount = UB - LB + 1
Dim LastRow As Long
LastRow = Cells(Rows.Count, LastRowCol).End(xlUp).Row
Dim rng As Range
Set rng = Cells(FirstRow, LastRowCol).Resize(LastRow - FirstRow + 1)
Dim Source As Variant
ReDim Source(LB To UB)
Dim j As Long
For j = LB To UB
Source(j) = rng.Offset(, Columns(srcCols(j)).Column - rng.Column).Value
Next j
Dim UBS As Long
UBS = UBound(Source(UB))
Dim Dest As Variant
ReDim Dest(1 To UBS ^ 2, 1 To srcCount)
Dim i As Long
Dim k As Long
For j = 1 To UBS
k = k + 1
For i = 1 + (j - 1) * UBS To UBS + (j - 1) * UBS
Dest(i, 1) = Source(0)(k, 1)
Dest(i, 2) = Source(1)(k, 1)
Dest(i, 3) = Source(2)(i - (j - 1) * UBS, 1)
Next i
Next j
Range(dstFirstCell).Resize(UBound(Dest), srcCount).Value = Dest
End Sub

How to transpose these row values into this specific format using VBA?

I am using Excel 2016 and I am new to VBA. I have an Excel worksheet which contains 262 rows (with no headers). An extract of the first 2 rows are shown below (starts at column A and ends at column L):
I would like to run a VBA code on the worksheet to transpose the data as follows:
How should I go about it?
Try
Sub test()
Dim vDB, vR()
Dim i As Long, j As Integer, n As Long
Dim r As Long
vDB = Range("a1").CurrentRegion
r = UBound(vDB, 1)
For i = 1 To r
For j = 1 To 6
n = n + 1
ReDim Preserve vR(1 To 2, 1 To n)
vR(1, n) = vDB(i, j)
vR(2, n) = vDB(i, j + 6)
Next j
Next i
Sheets.Add
Range("a1").Resize(n, 2) = WorksheetFunction.Transpose(vR)
End Sub
A Special Transpose
Sub SpecialTranspose()
Const cLngRows As Long = 262 ' Source Number of Rows
Const cIntColumns As Integer = 6 ' Source Number of Columns Per Set
Const cIntSets As Integer = 2 ' Source Number of Sets
Const cStrSourceCell As String = "A1" ' Source First Cell
Const cStrTargetCell = "M1" ' Target First Cell
Dim vntSource As Variant ' Source Array
Dim vntTarget As Variant ' Target Array
Dim h As Integer ' Source Array Set Counter / Target Array Column Counter
Dim i As Long ' Source Array Row Counter
Dim j As Integer ' Source Array Column Counter
Dim k As Long ' Target Array Row Counter
' Resize Source First Cell to Source Range and paste it into Source Array.
vntSource = Range(cStrSourceCell).Resize(cLngRows, cIntColumns * cIntSets)
' Resize Target Array
ReDim vntTarget(1 To cLngRows * cIntColumns, 1 To cIntSets)
' Calculate and write data to Target Array.
For h = 1 To cIntSets
For i = 1 To cLngRows
For j = 1 To cIntColumns
k = k + 1
vntTarget(k, h) = vntSource(i, cIntColumns * (h - 1) + j)
Next
Next
k = 0
Next
' Paste Target Array into Target Range resized from Target First Cell.
Range(cStrTargetCell).Resize(cLngRows * cIntColumns, cIntSets) = vntTarget
End Sub
You could use arrays to do your transpose:
Sub Transpose()
'Declare variables
Dim wsHome As Worksheet
Dim arrHome, arrNumber(), arrLetter() As Variant
Dim intNum, intLetter, lr, lc As Integer
Set wsHome = ThisWorkbook.Worksheets("Sheet1")
Set collNumber = New Collection
Set collLetter = New Collection
'Set arrays to position to 0
intNum = 0
intLetter = 0
'Finds last row and column of data
lc = Cells(1, Columns.Count).End(xlToLeft).Column
lr = Cells(Rows.Count, 1).End(xlUp).Row
'Move data into array
arrHome = wsHome.Range(Cells(1, 1), Cells(lr, lc)).Value
For x = LBound(arrHome, 1) To UBound(arrHome, 1)
For y = LBound(arrHome, 2) To UBound(arrHome, 2)
'Check if value is numeric
If IsNumeric(arrHome(x, y)) = True Then
ReDim Preserve arrNumber(intNum)
arrNumber(intNum) = arrHome(x, y)
intNum = intNum + (1)
Else
ReDim Preserve arrLetter(intLetter)
arrLetter(intLetter) = arrHome(x, y)
intLetter = intLetter + 1
End If
Next y
Next x
'clear all values in sheet
wsHome.UsedRange.ClearContents
ActiveSheet.Range("A1").Resize(UBound(arrNumber), 1).Value = Application.WorksheetFunction.Transpose(arrNumber)
ActiveSheet.Range("B1").Resize(UBound(arrLetter), 1).Value = Application.WorksheetFunction.Transpose(arrLetter)
End Sub
Let us assume that data appears in Sheet 1.Try:
Option Explicit
Sub TEST()
Dim LastColumn As Long, LastRowList As Long, LastRowNumeric As Long, LastRowNonNumeric As Long, R As Long, C As Long
With ThisWorkbook.Worksheets("Sheet1")
LastRowList = .cells(.Rows.Count, "A").End(xlUp).Row
LastColumn = .cells(1, .Columns.Count).End(xlToLeft).Column
For R = 1 To LastRowList
For C = 1 To LastColumn
If IsNumeric(.cells(R, C).Value) = True Then
LastRowNumeric = .cells(.Rows.Count, LastColumn + 2).End(xlUp).Row
If LastRowNumeric = 1 And .cells(1, LastColumn + 2).Value = "" Then
.cells(LastRowNumeric, LastColumn + 2).Value = .cells(R, C).Value
Else
.cells(LastRowNumeric + 1, LastColumn + 2).Value = .cells(R, C).Value
End If
Else
LastRowNonNumeric = .cells(.Rows.Count, LastColumn + 3).End(xlUp).Row
If LastRowNonNumeric = 1 And .cells(1, LastColumn + 3).Value = "" Then
.cells(LastRowNonNumeric, LastColumn + 3).Value = .cells(R, C).Value
Else
.cells(LastRowNonNumeric + 1, LastColumn + 3).Value = .cells(R, C).Value
End If
End If
Next C
Next R
End With
End Sub

Dynamic concatenation by header names, lost on how to do this

I need to a dynamic way to concatinate some cells in a row with a delimiter (in this instance |) as the columns move about (per project) this has to be by header names (there might be from 2 to many) columns that need to be concatinated for a project
I am trying to use arrays as there can be as many as 4000,000 rows
I have been trying for hours and here is my effort I know it is very wrong but I am at a loss
Thank you
Sub CAT()
fCAT "ElementsFile", "ElementsFile", "D", Array("Age", "Gender Identity", "Ethnicity1", "Ethnicity1")
End Sub
Sub fCAT(sShtName As String, pbShtName As String, InsertCol As String, ar As Variant)
Dim myresult
Dim col1 As String, col2 As String, col3 As String, col4 As String
Dim aLR As Long, i As Long, j As Long, k As Long
'Totaly at a loss here
For i = LBound(ar) To UBound(ar)
Dim ari As Variant
Dim coli As String
Next i
Set wsS = ThisWorkbook.Sheets(sShtName)
Set wsPB = ThisWorkbook.Sheets(pbShtName)
With wsS
aLR = .Range("A" & .Rows.Count).End(xlUp).Row
For i = LBound(ar) To UBound(ar)
j = .Rows(1).Find(ar(i)).Column
ari = .Range(Cells(1, j), Cells(aLR, j)).Select
Next i
End With
'Totaly at a loss here
ReDim myresult(1 To aLR, 1 To aLR)
For k = 1 To aLR
For i = LBound(ar) To UBound(ar)
j = wsS.Rows(1).Find(ar(i)).Column
myresult(k, 1) = Cells(k, j) & "|" & Cells(k, j + 1) & "|" & Cells(k, j + 2) & "|" & Cells(k, j + 3)
Next i
Next k
wsT.Range("D1").Resize(aLR, 1) = myresult
End Sub
Here is what I finally came up with a bit of a mess maybe but it works
Sub concat()
Dim myresult, CN
Dim HN As Variant
Dim wsS As Worksheet, wsPB As Worksheet
Dim str As String
Dim LR As Long, i As Long, j As Long, k As Long
HN = Array("Age", "Gender Identity", "Ethnicity1", "Ethnicity2")
Set wsS = ThisWorkbook.Sheets("ElementsFile")
Set wsPB = ThisWorkbook.Sheets("ElementsFile")
wsPB.Columns(4).Insert
ReDim CN(0 To UBound(HN))
With wsS
LR = .Range("A" & .Rows.Count).End(xlUp).Row
'Get Array of column numbers coresponding to Header names
For i = 0 To UBound(HN)
j = wsS.Rows(1).Find(HN(i)).Column
CN(i) = j
Next i
End With
ReDim myresult(1 To LR, 1 To 1)
For i = 1 To LR
str = vbNullString
If Not (IsEmpty(Cells(i, CN(0))) And IsEmpty(Cells(i, CN(1)))) Then
For k = UBound(HN) To 0 Step -1
If k <> UBound(HN) Then
str = Cells(i, CN(k)) & "|" & str
Else: str = Cells(i, CN(k)) & str
End If
Next k
myresult(i, 1) = str
Else
myresult(i, 1) = vbNullString
End If
Next i
str = vbNullString
wsPB.Range("D1").Resize(LR, 1) = myresult
End Sub

Resources