i foung similiar topic, but i can t apply completily that solution to my needs... I want to upgrade excel workbook at my job by making it more auto-entry capable.
Mostly i use excel functions, but sometimes i need some VBA coding, which im not very familiar with. So my problem is, that i need something like this mentioned on this thread. How to get the first and last numbers in a sequence
I have box numbers in different sequince in ascening order starting from "A4" to X on
Sheet1. Example Box numbers: M004935149,M004935150,M004935151,M004935202,M004935203,M004935204,M004935205, is it possible when i copy&paste(values) to sheet2 from "A4" to X (depenting on number of boxes copied) to make a string, sentence or whatever is called in specific form in some other cells. M004935149-151 // M004935202-205. I used code from topic in link above, it can make half job done but i can t figure it out how to make entry from desired cell range and display them on worksheet, and to display values in desired format. Link of screen shoots from my example is following:
I hope that someone can help. Thanks in advance.
Check this
Option Explicit
Sub test2()
Dim ws As Worksheet
Dim arr() As String, result As String, letter As String, cellValue As String, tempLastElement As String
Dim lastColumn As Long, counter As Long
Dim firstColumn As Integer, targetRow As Integer, i As Integer
Set ws = Worksheets("Sheet1")
firstColumn = 1 'number of first column with target data
targetRow = 1 'number of row with target data
lastColumn = ws.Range(ws.Cells(targetRow, firstColumn), ws.Cells(targetRow, Columns.Count).End(xlToLeft).Columns).Count
ReDim arr(1 To lastColumn - firstColumn + 1)
letter = Left(ws.Cells(targetRow, firstColumn).Value, 1) 'if count of character in start of string will be more 1, replace 1 on to count of characters
For i = 1 To UBound(arr)
cellValue = ws.Cells(targetRow, i).Value
arr(i) = Right(cellValue, Len(cellValue) - 1) 'if count of character in start of string will be more 1, replace 1 on to count of characters
Next i
ReDim sequenceArr(1 To UBound(arr))
sequenceArr(1) = arr(1)
counter = 2
For i = 1 To UBound(arr) - 1
If CLng(arr(i)) + 1 = CLng(arr(i + 1)) Then
tempLastElement = arr(i + 1)
sequenceArr(counter) = tempLastElement
Else
counter = counter + 1
sequenceArr(counter) = arr(i + 1)
counter = counter + 1
End If
Next
ReDim Preserve sequenceArr(1 To counter)
result = ""
counter = 1
For i = 1 To UBound(sequenceArr) - 1
If counter > UBound(sequenceArr) Then Exit For
If result = "" Then
result = letter & sequenceArr(counter) & "-" & Right(sequenceArr(counter + 1), 3)
counter = counter + 2
Else
result = result & "//" & letter & sequenceArr(counter) & "-" & Right(sequenceArr(counter + 1), 3)
counter = counter + 2
End If
Next
ws.Range("D4").Value = result
End Sub
Result on
Related
I have a code that matches a cell value in Column C on Sheet1 to a pivot table on Sheet3 and then copies certain columns over.
Code will check how many entries there are on Sheet1 that need to be checked
Loop 2: For every value in Column C/Sheet1 with a match in Column A on Sheet 2 it will then copy over the corresponding data from Column B,C,D,E.
Since there are multiple matches possible by value/Sheet I am limiting the data pull to three matches (three loops in the code). To achieve that I am increasing i +1 or i+2 to get the next row in the pivot table.
The table on Sheet 2 is sometimes 10,000+ rows and excel crashes.
Does anyone have an idea how to speed up the loop codes (Loop2,3,4 are the same) to make it less work intensive e.g. array possibly? They are causing the lock up since I think the code keeps running up and down column A.
Set sheet3 = Sheets("OrbitPivotTable")
CellChanged = Sheet1.Range("A1").Value + 1
LastRow = sheet3.Cells(Rows.Count, "A").End(xlUp).Row
LastData = Sheet1.Cells(Rows.Count, "C").End(xlUp).Row
'Loop1
For i = 1 To LastRow
If Sheet1.Range("C" & CellChanged).Value = "" Then GoTo Nextstep2
If Sheet1.Range("C" & CellChanged).Value = sheet3.Range("A" & i) Then
Sheet1.Range("H" & CellChanged).Value = sheet3.Range("B" & i).Value 'Customer
Sheet1.Range("I" & CellChanged).Value = sheet3.Range("C" & i).Value 'Rate Val start
Sheet1.Range("J" & CellChanged).Value = sheet3.Range("D" & i).Value 'ATA All in
Sheet1.Range("K" & CellChanged).Value = sheet3.Range("E" & i).Value 'Special Remarks
Found = True
End If
If Found = True Or i = LastRow Then
If CellChanged = LastData Then
Exit For
End If
If Found = True Then
Found = False
Nextstep2:
CellChanged = CellChanged + 1
End If
i = 0
End If
Next i
'Loop2
etc....
Excel File
I might have misunderstood the process in the file you shared, but this should be faster (and much less code overall).
I put the pivot table lookup in a loop, switched to Match(), and reduced the number of read/writes using arrays where possible.
EDITED to fix an embarrassing bug where I forgot to adjust the Match() result m to account for the starting row of the range I run match() against...
Sub HB_IPT_Rate_Check()
Dim wsReport As Worksheet, wsCPK As Worksheet, wsOrbitPivot As Worksheet
Dim c As Range, rwReport As Range, lastPivotRow As Long
Dim ata, m, numMatches As Long, matchFrom As Long, matchRow As Long
Set wsReport = ThisWorkbook.Worksheets("Comparison Report")
Set wsCPK = ThisWorkbook.Worksheets("CPK")
Set wsOrbitPivot = ThisWorkbook.Worksheets("OrbitPivotTable")
'loop over the rows in the report sheet
For Each c In wsReport.Range("C3", wsReport.Cells(Rows.Count, "C").End(xlUp)).Cells
ata = c.Value 'read this once....
Set rwReport = c.EntireRow
'1st Database Match "CPK"
m = Application.Match(ata, wsCPK.Columns("A"), 0)
If Not IsError(m) Then
With wsCPK.Rows(m)
rwReport.Columns("D").Resize(1, 4).Value = _
Array(.Columns("B").Value, .Columns("C").Value, _
.Columns("F").Value, .Columns("H").Value)
'Sum of HB CWGT (KG),Sum of MB CWGT (KG),Achiev CPK,Density
End With
Else
'no match...
End If
'2nd Database Match "Orbit"
lastPivotRow = wsOrbitPivot.Cells(Rows.Count, "A").End(xlUp).Row
numMatches = 0 'reset match count
matchFrom = 2
m = Application.Match(ata, wsOrbitPivot.Range("A" & matchFrom & ":A" & lastPivotRow), 0)
'keep going while we still have a match and we've not reached the max result count
Do While Not IsError(m) And numMatches < 3
numMatches = numMatches + 1
matchRow = matchFrom + (m - 1) 'adjust the matched row index according to where we started looking...
'sanity check
Debug.Print "Matched " & ata & " on row " & matchRow
rwReport.Columns("H").Offset(0, (numMatches - 1) * 4).Resize(1, 4).Value = _
wsOrbitPivot.Cells(matchRow, "B").Resize(1, 4).Value
'find the next match if any, starting below the last match
matchFrom = matchRow + 1
m = Application.Match(ata, wsOrbitPivot.Range("A" & matchFrom & ":A" & lastPivotRow), 0)
Loop
Next c 'next report row
End Sub
Use Dictionary to set row and column number.
Data is assigned to fit rows and columns in a virtual array.
Sub test()
Dim Ws(1 To 4) As Worksheet
Dim DicR As Object ' Dictionary
Dim DicC As Object ' Dictionary
Dim vDB, arr()
Dim s As String
Dim i As Long, n As Long, j As Integer
Dim r As Long, c As Integer
Set Ws(1) = Sheets("Comparison Report")
Set Ws(2) = Sheets("CPK")
Set Ws(3) = Sheets("OrbitPivotTable")
Set Ws(4) = Sheets("Orbit")
'Row index dictionary
Set DicR = CreateObject("Scripting.Dictionary") 'New Scripting.Dictionary
'Column index dictionary
Set DicC = CreateObject("Scripting.Dictionary") ' New Scripting.Dictionary
vDB = Ws(1).UsedRange
For i = 3 To UBound(vDB, 1)
s = vDB(i, 3)
If s <> "" Then
If DicR.Exists(s) Then
'DicC(s) = DicC(s) + 1
Else
n = n + 1
DicR.Add s, n 'row index
DicC.Add s, 0 'column index
End If
End If
Next i
'Create an array of virtual tables based on the number of dictionaries.
'Since the number of columns cannot be predicted, a specific number of 1000 was entered.
'in my test, number 100 is too small
ReDim arr(1 To DicR.Count, 1 To 1000)
For j = 2 To 4
vDB = Ws(j).Range("a1").CurrentRegion
For i = 2 To UBound(vDB, 1)
s = vDB(i, 1)
If DicR.Exists(s) Then
r = DicR(s)
c = DicC(s) * 4 + 1
DicC(s) = DicC(s) + 1
arr(r, c) = vDB(i, 2)
arr(r, c + 1) = vDB(i, 3)
arr(r, c + 2) = vDB(i, 4)
arr(r, c + 3) = vDB(i, 5)
End If
Next i
Next j
With Ws(1)
.Range("d3").Resize(UBound(arr, 1), UBound(arr, 2)) = arr
End With
End Sub
Result image
I have a very basic question, but would love to know how to do this. I want to write a function in VBA where I can highlight a column as an input, and then spit out the result somewhere else.
Thanks in advance :)
e.g. column A
--------
10
8
5
6
1
3
2
becomes:
column A
--------
10
8
6
2
I just did it from column a to b, but you probably want range as the current selection and a different output column.
Option Explicit
Sub filterlist()
Dim rng As Range
Set rng = Range("a1:a5")
Dim celluse As Range
Dim arr As Variant
For Each celluse In rng
If celluse.Value Mod 2 = 0 Then
If IsEmpty(arr) Then
arr = Array(celluse.Value)
Else
ReDim Preserve arr(UBound(arr) + 1)
arr(UBound(arr)) = celluse.Value
End If
End If
Next celluse
Dim i As Long
For i = 0 To UBound(arr)
Range("b" & i + 1) = arr(i)
Next i
End Sub
This code should do the trick.
You can enter as an array-formula directly to a sheet: {=RemoveOdds(A1:A7)} or as part of another procedure:
Sub Test()
RemoveOdds Selection
End Sub
Public Function RemoveOdds(Target As Range) As Variant
Dim vFinal() As Variant
Dim rCell As Range
Dim x As Long
ReDim vFinal(1 To Target.Cells.Count)
x = 1
For Each rCell In Target
If rCell Mod 2 = 0 Then
vFinal(x) = rCell.Value
x = x + 1
End If
Next rCell
'So missing values do not show up as 0 at bottom of array.
' Do While x <= Target.Cells.Count
' vFinal(x) = ""
' x = x + 1
' Loop
ReDim Preserve vFinal(1 To x - 1)
'RemoveOdds = vFinal 'Basic array - will place values horizontally on sheet.
RemoveOdds = Application.Transpose(vFinal) 'Will place values vertically on sheet.
End Function
I have a giant dataset that looks like this
I am trying to go down the list of different companies and grab 3 per company and combine them. Based on the photo above, I would have 2 different lists with 3 companies each (except TH Repair which will have 2 in the final list).
My real dataset contains hundreds of different companies, each with dozens/hundreds of entries so I would finish with dozens of lists (each potentially hundreds long).
I tried to record a macro and ended up with this code
Sub Loop1()
'
' Loop1 Macro
'
'
Range("A4:E6").Select
Selection.Copy
Sheets("Sheet3").Select
Range("A18").Select
ActiveSheet.Paste
Sheets("Sheet2").Select
Range("A11:E13").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet3").Select
Range("A21").Select
ActiveSheet.Paste
Sheets("Sheet2").Select
Range("A17:E19").Select
Application.CutCopyMode = False
Selection.Copy
Sheets("Sheet3").Select
Range("A24").Select
ActiveSheet.Paste
End Sub
However, this turned out to be WAY more complicated then I expected.
I am looking for the end result to look like this
See if something like this works for you. I only ran one scenario through it so you will want to test it more.
This makes the assumption that the data is sorted by column B on the original sheet
This procedure makes an assumption that there is either headers or no data on row 1.
You will need to change the "Sheet1" in this line Set ws1 = ActiveWorkbook.Worksheets("Sheet1") to the name of the sheet you are starting with.
Option Explicit
Public Sub MoveData()
Dim ws1 As Worksheet
Set ws1 = ActiveWorkbook.Worksheets("Sheet1")
Dim ws2 As Worksheet
Set ws2 = ActiveWorkbook.Worksheets.Add()
Dim rw As Long
Dim match_count As Integer
Dim list_multiplier As Integer
list_multiplier = 7
Dim list_row() As Long
ReDim list_row(0)
list_row(0) = 2
For rw = 2 To ws1.Range("A" & ws1.Rows.Count).End(xlUp).Row
If ws1.Range("B" & rw).Value <> ws1.Range("B" & rw).Offset(-1, 0).Value Then
match_count = 0
Else
match_count = match_count + 1
End If
Dim list_num As Integer
list_num = match_count \ 3
If list_num > UBound(list_row, 1) Then
ReDim Preserve list_row(list_num)
list_row(list_num) = 2
End If
ws2.Cells(list_row(list_num), 1 + list_multiplier * list_num).Value = ws1.Range("A" & rw).Value
ws2.Cells(list_row(list_num), 2 + list_multiplier * list_num).Value = ws1.Range("B" & rw).Value
ws2.Cells(list_row(list_num), 3 + list_multiplier * list_num).Value = ws1.Range("C" & rw).Value
ws2.Cells(list_row(list_num), 4 + list_multiplier * list_num).Value = ws1.Range("D" & rw).Value
ws2.Cells(list_row(list_num), 5 + list_multiplier * list_num).Value = ws1.Range("E" & rw).Value
list_row(list_num) = list_row(list_num) + 1
Next rw
End Sub
When you record your macro, ensure that "Use Relative References" on the Developer Ribbon tab is enabled, :)
assuming row 3 has your data headers, you could try this:
Option Explicit
Sub main()
Dim nLists As Long, iList As Long
Dim data As Variant
Dim dataToDelete As Range
With Range("F3", Cells(Rows.Count, 1).End(xlUp))
data = .Value
nLists = WorksheetFunction.Max(.Resize(,1))
nLists = nLists \ 3 + IIf(nLists - 3 * (nLists \ 3) = 0, -1, 0)
End With
With Range("A3").Resize(, 6)
For iList = 0 To nLists
Set dataToDelete = Nothing
With .Offset(, iList * 6).Resize(UBound(data))
.Value = data
.AutoFilter Field:=1, Criteria1:="<=" & iList * 3, Criteria2:=">" & (iList + 1) * 3, Operator:=xlOr
If Application.WorksheetFunction.Subtotal(103, .Cells) > 1 Then Set dataToDelete = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
.Parent.AutoFilterMode = False
If Not dataToDelete Is Nothing Then dataToDelete.Delete xlShiftUp
End With
Next
End With
End Sub
Your task is actually slightly trickier than your online advice suggests. Basically, you have to do the following:
Find out how many unique 'keys' (ie unique items in column B) you have. This will tell you the total number of rows you need (ie number of unique keys * 3)
Count the number of items for each 'key'. This will tell you how many columns you need (ie max item count / 3 * number of columns in array [A:E = 5])
Loop through each line of data and it put on appropriate row for that 'key'. Once three has been reached, jump the column for that key 6 columns to the right, and continue.
If you were to use a Class object and Collection type of object, this could be really quite concise code, but judging by your post you are at the beginning of your programming journey in VBA. Therefore, I've broken down each task into separate chunks of code so you will hopefully see how arrays can work for you. Once you practise with arrays a little, perhaps you could have a go at making this code more efficient by combining some of the loops:
Public Sub RunMe()
Dim data As Variant
Dim r As Long, c As Long, i As Long, dataRows As Long, dataCols As Long, keyLen As Long, maxCount As Long
Dim keys As String
Dim k As Variant
Dim keyArray() As String
Dim keyCount() As Long, threeCount() As Long, rowNum() As Long, colNum() As Long
Dim output() As Variant
'Read the data - change "Sheet1" to your sheet name.
'Shows how to write range values into a variant to
'create an array of variants.
data = ThisWorkbook.Worksheets("Sheet1").UsedRange.Value2
dataRows = UBound(data, 1)
dataCols = UBound(data, 2)
'Create a list of unique keys.
'Note: not the most efficient way, but shows how to
'create an array from a value-separated string.
For r = 1 To dataRows
If InStr(keys, CStr(data(r, 2))) = 0 Then
If Len(keys) > 0 Then keys = keys & "|"
keys = keys & CStr(data(r, 2))
End If
Next
keyArray = Split(keys, "|")
keyLen = UBound(keyArray)
'Initialise the row and column numbers for each key.
'Shows how to iterate an array using For Each loop.
ReDim rowNum(keyLen)
ReDim colNum(keyLen)
r = 1
i = 0
For Each k In keyArray
rowNum(i) = r
colNum(i) = 1
r = r + 3
i = i + 1
Next
'Count the number of items for each key.
'Shows how to iterate an array using For [index] loop.
ReDim keyCount(keyLen)
For r = 1 To dataRows
i = IndexOfKey(keyArray, CStr(data(r, 2)))
keyCount(i) = keyCount(i) + 1
If keyCount(i) > maxCount Then maxCount = keyCount(i)
Next
'Size the output array.
c = WorksheetFunction.Ceiling(maxCount / 3, 1)
ReDim output(1 To (keyLen + 1) * 3, 1 To c * dataCols + c - 1)
'Populate the output array.
ReDim threeCount(keyLen)
For r = 1 To dataRows
i = IndexOfKey(keyArray, CStr(data(r, 2)))
'Copy the columns for this row.
For c = 1 To dataCols
output(rowNum(i), colNum(i) + c - 1) = data(r, c)
Next
'Increment the count and if it's equals 3 then
'reset the row num and increase the column number.
threeCount(i) = threeCount(i) + 1
rowNum(i) = rowNum(i) + 1
If threeCount(i) = 3 Then
rowNum(i) = rowNum(i) - 3
colNum(i) = colNum(i) + dataCols + 1
threeCount(i) = 0
End If
Next
'Write the data - change "Sheet2" to your sheet name.
'Shows how to write an array to a Range.
ThisWorkbook.Worksheets("Sheet2").Range("A3") _
.Resize(UBound(output, 1), UBound(output, 2)).Value = output
End Sub
Private Function IndexOfKey(list() As String, key As String) As Long
Dim i As Long
Dim k As Variant
'Helper function to find index position of key in array.
For Each k In list
If key = k Then
IndexOfKey = i
Exit Function
End If
i = i + 1
Next
IndexOfKey = -1
End Function
I have the following example data:
The first column is a list of names, the second column is the year those names belong to.
What I want to do is build a list of all unique (distinct) names from one year.
So for example in the year 2016 I want it to build a list like in the end result column, while in the year 2017 I want it to build a list with the unique names of that year.
Preferably I want it to be a (dynamic) named range so that the calculation only has to be performed once and so that I can use the =INDEX(examplenamedrange, 1) formula to call the names that I want to use.
If this is not possible in a dynamic named range then storing the array in VBA is also a possibility.
I’ve seen a few Excel formulas around the net that look at unique values in a list, but none that I could find with an extra criteria (in this case: Year).
Can anyone set me on the right path?
Here is a short VBA sub to achieve what you ask.
To set up a sub, press Alt+F11 to open VBA editor, then Insert>Module and paste the following code. I have commented it to show what each section is doing. You could also set this up to run when the Year cell is changed, but I'll leave that as an exercise for you! To run it, press F5 in the VBA editor or click the run button.
Sub uniqueInYear()
Dim sh As Worksheet
Set sh = ActiveSheet
Dim vcell As Range
Dim namesString As String
namesString = ""
Dim namesList() As String
' Compile string with all names comma separated for given year
For Each vcell In Range("A2:A" & sh.UsedRange.Rows.Count)
' check if name already captured for given year
If InStr(namesString, vcell.Value) = 0 And vcell.Offset(0, 1).Value = sh.Range("E1").Value Then
namesString = namesString & "," & vcell.Value
End If
Next vcell
' If empty then quit
If namesString = "" Then
Exit Sub
End If
' Remove leading comma
namesString = Right(namesString, Len(namesString) - 1)
' Put names into array
namesList = Split(namesString, ",")
' Write names to result column after clearing it
sh.Range("E2:E" & sh.UsedRange.Rows.Count + 1).Value = ""
Dim nameVar As Variant
For Each nameVar In namesList
sh.Range("E" & sh.UsedRange.Rows.Count + 1).End(xlUp).Offset(1, 0).Value = nameVar
Next nameVar
' Named range - delete if it exists then create a-fresh
On Error Resume Next
sh.Parent.Names("UniqueNames").Delete
On Error GoTo 0
sh.Parent.Names.Add name:="UniqueNames", _
RefersTo:=sh.Range("E2", sh.Range("E" & sh.UsedRange.Rows.Count + 1).End(xlUp))
End Sub
Outcome:
Just to add another similar, but different method in there. You can use a UDF which returns an array. So paste the code into a code module and then use the following formula on the worksheet
=GetNamesInYear(names,dates,2016)
where names is your range of names, dates is your range of dates and 2016 is the year you are searching, either a number written in the formula or reference to a cell with the value 2016 is fine.
To return the full array you need to enter the formula with Ctrl+Shift+Enter. To view all results, rather than just the first result, highlight that cell and the 5 below it (say), press F2 to edit and then Ctrl+Shift+Enter again.
Alternatively you can access the array with any worksheet function that can deal with string arrays. For example:
=INDEX(GetNamesInYear(names,dates,2016),2)
returns the second item in the array
Here is the code
Function GetNamesInYear(names As Range, years As Range, year As Integer) As Variant
Dim namesArr As Variant
namesArr = names.Value2
Dim yearsArr As Variant
yearsArr = years.Value2
Dim results As Long
results = 0
Dim resultArr As Variant
Dim i As Long
ReDim resultArr(0 To 0)
For i = 1 To UBound(namesArr, 1)
If Not InArray(resultArr, namesArr(i, 1)) And (yearsArr(i, 1) = year) Then
ReDim Preserve resultArr(0 To results)
resultArr(results) = namesArr(i, 1)
results = results + 1
End If
Next i
GetNamesInYear = Application.WorksheetFunction.Transpose(resultArr)
End Function
Private Function InArray(arr As Variant, value As Variant) As Boolean
Dim i As Integer
For i = 0 To UBound(arr)
If arr(i) = value Then
InArray = True
Exit Function
End If
Next i
InArray = False
End Function
The result looks like this:
Update
Names and dates inputs are now split out (separate ranges) as per comments from OP
You could try this:
Sub Names()
Dim x, Years, Counted, ColumnCount, j, lColumn
Dim Names(), FoundNames()
Years = Range("B1").Value
Counted = 0
ColumnCount = 2
ReDim Names(Range("A" & Rows.count).End(xlUp).row)
ReDim FoundNames(LBound(Names) To UBound(Names))
lColumn = Cells(1, Cells(1, Columns.count).End(xlToLeft).Column).Column
For Each c In Range(Range("A1"), Range("A" & Rows.count).End(xlUp))
If Years <> Range("B" & c.row).Value Then
For i = LBound(Names) To UBound(Names)
If Names(i) <> "" Then
j = j + 1
FoundNames(j - 1) = Names(i)
End If
Next i
ReDim Preserve FoundNames(LBound(Names) To j - 1)
Cells(1, lColumn + ColumnCount).Value = Years
For i = LBound(FoundNames) To UBound(FoundNames)
Cells(i + 2, lColumn + ColumnCount).Value = FoundNames(i)
Next
ColumnCount = ColumnCount + 1
Years = Range("B" & c.row).Value
Counted = 0
ReDim Names(Range("A" & Rows.count).End(xlUp).row)
ReDim FoundNames(LBound(Names) To UBound(Names))
End If
If InStr(Join(Names, ","), c.Value) < 1 Then
Names(Counted) = c.Value
Counted = Counted + 1
End If
Next c
j = 0
For i = LBound(Names) To UBound(Names)
If Names(i) <> "" Then
j = j + 1
FoundNames(j - 1) = Names(i)
End If
Next i
ReDim Preserve FoundNames(LBound(Names) To j - 1)
Cells(1, lColumn + ColumnCount).Value = Years
For i = LBound(FoundNames) To UBound(FoundNames)
Cells(i + 2, lColumn + ColumnCount).Value = FoundNames(i)
Next
End Sub
The results look like this:
Array formula can work here:
=INDEX($A$1:$A$15, N(IF({1}, MODE.MULT(IF(($B$1:$B$15=2016)*(ROW($A$1:$A$15)=MATCH($A$1:$A$15, $A$1:$A$15, 0)), (ROW($A$1:$A$15)) * {1,1})))))
Define your named range as dynaRange_2016, and see it's use in the two images
:
You could name a range for each year instead, and then define yet another name for the uniques range. This is more versatile:
define Named range_2017 as =INDEX(Sheet5!$A:$A, MATCH(2017,Sheet5!$B:$B, 0)):INDEX(Sheet5!$A:$A, MATCH(2017,Sheet5!$B:$B, 1))
Then define another named range uniques_2017 as=INDEX(Sheet5!range_2017, N(IF({1}, MODE.MULT(IF(ROW(Sheet5!range_2017)-MATCH(2017, Sheet5!$B:$B, 0)+1=MATCH(Sheet5!range_2017, Sheet5!range_2017, 0), (ROW(Sheet5!range_2017)-MATCH(2017, Sheet5!$B:$B, 0)+1) * {1,1})))))
In your sheet, you can call as INDEX(uniques_2017, 3) for example. Do the same for all years that you expect to occur.
I create an array in vba by looping through cells in a sheet (originalWS). So let's say cells (2,5) to (2,12) have the following:
(2,5)Item
(3,5)Type
(4,5)Nominal Diameter
(5,5)Lead
.
.
.
(12,5)For Use with End Blocks
Thus, when I loop with the code below I get an array that looks like this:
[Item,Type,Nominal Diameter,Lead,...,For Use with End Blocks].
However, I would like to add two empty spaces between each value in my array. so that it looks like this:
[Item,"","",Type,"","",Nominal Diameter,"","",Lead,"","",...,For Use with End Blocks,"",""]
ReDim propertyArr(1, lastRow)
For i = 1 To lastRow
propertyArr(1, i) = originalWS.Cells(i + 1, 5).Value
Debug.Print propertyArr(1, i)
Next
I have tried to loop to by the final total size of the array so (lastRow*3) and step forward by 3. However, I'm having a hard time figuring out how I would reset my orginalWS.cells(i,5) values so that they are consecutive.
In other words, when I loop stepping by 3 my values would be:
propertyArr(1,1) = originalWS.Cells(2,5).value
propertyArr(1,4) = originalWS.cells(5,5).value
propertyArr(1,7) = originalWS.cells(8,5).value
How can I loop so that I store values in my array every 2 places, while I get the values from a consecutive list in a sheet.
Can I do this without having to add extra empty row
a way to add two empty spaces between each value within my original loop without having to add extra empty rows?
Or, can I add the two empty spaces between each value after I created my array the first time?
This should do the trick,
Dim lRowNo As Long
lRowNo = lastRow * 3
ReDim propertyArr(1, lRowNo)
For i = 1 To lRowNo
If i Mod 3 = 1 Then
propertyArr(1, i) = originalWS.Cells(i + 1, 5).Value
Else
propertyArr(1, i) = ""
End If
Debug.Print propertyArr(1, i)
Next
Something like:
Sub ytrewq()
Dim propertyArr(), lastRow As Long
Dim originalWS As Worksheet
Set originalWS = ActiveSheet
lastRow = 5
ReDim propertyArr(1, 2 * lastRow)
For i = 1 To 2 * lastRow Step 2
propertyArr(1, i) = originalWS.Cells(i + 1, 5).Value
propertyArr(1, i + 1) = ""
Debug.Print propertyArr(1, i)
Next
End Sub
UNTESTED
You can also unroll the loop a bit to do this a bit more efficiently. Note that for each iteration, i isn't incremented by 1, but by 3.
Public Sub test()
Dim lastRow As Long
lastRow = 6
Dim lastIndex As Long
lastIndex = lastRow * 3
ReDim propertyArr(1 To lastIndex)
Dim i As Long
For i = 1 To lastIndex Step 3
propertyArr(i) = CInt(i / 3)
propertyArr(i + 1) = vbNullString
propertyArr(i + 2) = vbNullString
Next
End Sub
Or without loops
Dim ws As Worksheet
Set ws = Sheets(1)
propertyarr = Join(Application.Transpose(ws.Range("E1:E5")), ","""","""",")
to put back into array
propertyarr = Split(Join(Application.Transpose(ws.Range("E1:E5")), ",,,"), ",")
I figured out the answer. I wasn't updating the cells I needed correctly. See code below:
count = 3
lastIndex = lastRow * 3
ReDim propertyArr(1, lastIndex)
For i = 1 To lastIndex Step 3
propertyArr(1, i) = originalWS.Cells((count - 1), 5)
count = count + 1
Next