Storing only specific values in an array from another array - vba

The goal of this sub is to run through an existing array where all values stored in the array slots before the array slot containing the String "Score" are useless, and all the ones after this slot and before the slot containing the String "Why?" are meaningful. So the array could look like this:
IQRngRef(0).Value2(1) = "Pineapple"
IQRngRef(0).Value2(2) = "Apple"
IQRngRef(0).Value2(3) = "Lemons"
IQRngRef(0).Value2(4) = "Score"
IQRngRef(0).Value2(5) = "23"
IQRngRef(0).Value2(6)= "45"
IQRngRef(0).Value2(7) = "333"
IQRngRef(0).Value2(8) = "Why?"
IQRngRef(0).Value2(9) = "77"
IQRngRef(0).Value2(10) = "60"
I want to then store only the values {23|45|333} into an array roleArray(). The Following is what I came up with, but I'm sure there's an easier/ more efficient way.
Also, this is giving me run-time error 451 property let procedure not defined and property get procedure did not return an object on this line: roleIdentifier = IQRngRef(0).Value2(rowIterator) and I can't figure out how to fix it.
Any help with this would be much appreciated.
Private Sub IdentifyRolesAndScoresRows(ByRef IQRngRef As Variant, ByVal rowNumb As Long)
Dim rowIterator As Long
Dim roleIdentifier As String
Do Until roleIdentifier = "Score"
For rowIterator = 1 To rowNumb
roleIdentifier = IQRngRef(0).Value2(rowIterator)
Next rowIterator
Loop
Dim roleArray(1 To 10) As String
Dim roleArrayCount As Long
Do Until roleIdentifier = "Why?"
For rowIterator = rowIterator + 1 To rowNumb
roleIdentifier = IQRngRef(0).Value2(rowIterator)
roleArrayCount = roleArrayCount + 1
roleArray(roleArrayCount) = roleIdentifier
Next rowIterator
Loop
End Sub
This is the code that fills IQRngRef()
Private Sub CaptureIQRefsLocally(ByVal ShRef As Worksheet, ByVal rowNumb As Long, ByVal colNumb As Long, ByRef IQRef As Variant, ByRef IQRngref As Variant)
'capture IQ references in arrays. Values for column titles in IQRef and full column Ranges in IQRngRef.
Dim iCol As Long
Dim alignIQNumbToArrayNumb As Long
With ShRef
For iCol = 1 To colNumb
alignIQNumbToArrayNumb = iCol - 1
Set IQRngref(alignIQNumbToArrayNumb) = .Range(.Cells(1, iCol), .Cells(rowNumb, iCol))
IQRef(alignIQNumbToArrayNumb) = .Cells(1, iCol).Value
'IsThisaKeyIQ IQRngref, IQRef
Next iCol
End With
End Sub

See if you can adapt this for your particular situation.
Sub x()
Dim v(1 To 10), n1 As Long, n2 As Long, v1, i As Long
v(1) = "Pineapple"
v(2) = "Apple"
v(3) = "Lemons"
v(4) = "Score"
v(5) = "23"
v(6) = "45"
v(7) = "333"
v(8) = "Why?"
v(9) = "77"
v(10) = "60"
n1 = Application.Match("Score", v, 0)
n2 = Application.Match("Why?", v, 0)
v1 = Application.Index(v, Evaluate("ROW(" & n1 + 1 & ":" & n2 - 1 & ")"))
For i = LBound(v1) To UBound(v1)
MsgBox v1(i, 1)
Next i
End Sub

You have to work with "Variant array of 1D Variant arrays" (i.e. Variant/Variant) and then slice these latter by means of Application.Index function as per This Link
so, first change CaptureIQRefsLocally() sub as follows:
Private Sub CaptureIQRefsLocally(ByVal ShRef As Worksheet, ByVal rowNumb As Long, ByVal colNumb As Long, ByRef IQRef As Variant, ByRef IQRngref As Variant)
'capture IQ references in arrays. Values for column titles in IQRef and full column Ranges values in IQRngRef.
Dim iCol As Long
Dim alignIQNumbToArrayNumb As Long
With ShRef
For iCol = 1 To colNumb
alignIQNumbToArrayNumb = iCol - 1
IQRngref(alignIQNumbToArrayNumb) = Application.Transpose(.Range(.Cells(1, iCol), .Cells(rowNumb, iCol)).Value) ' make an 1D array out of range values and store it in current 'IQRngref' element
IQRef(alignIQNumbToArrayNumb) = .Cells(1, iCol).Value
'IsThisaKeyIQ IQRngref, IQRef
Next iCol
End With
End Sub
and then change IdentifyRolesAndScoresRows() sub as follows:
Private Sub IdentifyRolesAndScoresRows(ByRef IQRngref As Variant, ByVal rowNumb As Long)
Dim startIndex As Long, endIndex As Long
startIndex = Application.Match("Score", IQRngref(0), 0)
endIndex = Application.Match("Why?", IQRngref(0), 0)
Dim roleArray As Variant
roleArray = Application.Transpose(Application.Index(IQRngref(0), Evaluate("ROW(" & startIndex + 1 & ":" & endIndex - 1 & ")"))) ' from https://www.mrexcel.com/forum/excel-questions/927644-split-array-vba-2.html
End Sub

Related

vba code to get all combinations if they equal a cell

So I have a 15 column by 100 row sheet of data that has all different percentages, from 100%-0%, that I'm looking to get all possible combinations out of it. Right now I have a code that works that puts the combinations into another 15 columns. The problem is, how do I make the code only output the combinations that when added together = 100%. This is the code that I have right now.
Sub Perm()
Dim rSets As Range, rOut As Range
Dim vArr As Variant, lRow As Long
Set rSets = Range("A1").CurrentRegion
ReDim vArr(1 To rSets.Columns.Count)
Set rOut = Cells(1, rSets.Columns.Count + 2)
Perm1 rSets, vArr, rOut, 1, lRow
End Sub
Sub Perm1(rSets As Range, ByVal vArr As Variant, rOut As Range, ByVal lSetN As Long, lRow As Long)
Dim j As Long
For j = 1 To rSets.Rows.Count
If rSets(j, lSetN) = "" Then Exit Sub
vArr(lSetN) = rSets(j, lSetN)
If lSetN = rSets.Columns.Count Then
lRow = lRow + 1
rOut(lRow).Resize(1, rSets.Columns.Count).Value = vArr
Else
Perm1 rSets, vArr, rOut, lSetN + 1, lRow
End If
Next j
End Sub
I assumed your percentages were decimal values and not text (.3 instead of 30%). Just added an if statement that sum's vArr and checks if the sum is 1.
Sub Perm1(rSets As Range, ByVal vArr As Variant, rOut As Range, ByVal lSetN As Long, lRow As Long)
Dim j As Long
For j = 1 To rSets.Rows.Count
If rSets(j, lSetN) = "" Then Exit Sub
vArr(lSetN) = rSets(j, lSetN)
If lSetN = rSets.Columns.Count Then
If WorksheetFunction.Sum(vArr) = 1 Then
lRow = lRow + 1
rOut(lRow).Resize(1, rSets.Columns.Count).Value = vArr
End If
Else
Perm1 rSets, vArr, rOut, lSetN + 1, lRow
End If
Next j

VBA Code for Finding Cells Below which match the key

I have the following requirement I have 2 columns with unique keys called code. In one column below the code, there are one or multiple values present which is the answer. Like in below format
A X
1
2
B Y
9
3
Now the code will have a value populated in next column, while answers wont.
Now I have to find answers for all codes like A, B, C etc. For e.g If I compare with A then answer should be 1,2. I was writing a small subroutine as a beginning but I am facing issues. Can you please correct it
Sub CalculateCellValue()
Dim ValuesBelow As Variant
Dim ValuesRight As String
Dim rows1 As Integer
rows1 = 4
Dim colC As Integer
colC = 2
ValuesRight = ActiveSheet.Cells(rows1 + 1, colC + 1)
While (Not IsEmpty(ValuesRight))
ValuesBelow = ActiveSheet.Cells(rows1 + 1, colC)
rows1 = rows1 + 1
ValuesRight = ActiveSheet.Cells(rows1 + 1, colC + 1)
Wend
MsgBox (ValuesBelow)
End Sub
Purely for an ordered example as shown:
Option Explicit
Sub test()
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ThisWorkbook
Set ws = wb.Worksheets("Sheet5") 'Change as appropriate
Dim myArr()
myArr = ws.Range("A1:B" & GetLastRow(ws, 1)).Value
Dim i As Long
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
For i = LBound(myArr, 1) To UBound(myArr, 1)
If myArr(i, 2) <> vbNullString Then
If Not dict.exists(myArr(i, 1)) Then
Dim currKey As String
currKey = myArr(i, 1)
dict.Add myArr(i, 1), vbNullString
End If
Else
dict(currKey) = dict(currKey) & ", " & myArr(i, 1)
End If
Next i
Dim key As Variant
For Each key In dict
MsgBox key & " = " & Right$(dict(key), Len(dict(key)) - 1)
Next key
End Sub
Public Function GetLastRow(ByVal ws As Worksheet, Optional ByVal columnNumber As Long = 1) As Long
With ws
GetLastRow = .Cells(.Rows.Count, columnNumber).End(xlUp).Row
End With
End Function
I used below code to match my requirement
Function findBelowAll(rows1 As Long)
Dim ValuesBelow() As Variant
ReDim ValuesBelow(1 To 1) As Variant
Dim ValuesRight As Variant
Dim colC As Long
colC = 1
Dim i As Long
ValuesRight = ""
While (ValuesRight = "")
rows1 = rows1 + 1
' change / adjust the size of array
ReDim Preserve ValuesBelow(1 To UBound(ValuesBelow) + 1) As Variant
' add value on the end of the array
ValuesBelow(UBound(ValuesBelow)) =
Worksheets(ActiveSheet.Name).Cells(rows1, colC).Value
ValuesRight = Worksheets(ActiveSheet.Name).Cells(rows1, 2).Value
Wend
For i = LBound(ValuesBelow) To UBound(ValuesBelow) - 1
findBelowAll = findBelowAll & ValuesBelow(i) & vbNewLine
Next i
End Function

VBA Index/Match with multiple criteria (unique value & date)

I have a spreadsheet that has values for more than one month, so I am trying to first find the value based on a value in the wsRevFile worksheet and then ensure that this is the value from last month. When I use the following code, I get a "invalid number of arguments" error.
Sub RevLookup(wsMvFile As Worksheet, wsRevOld As Worksheet, wsNewRev As Worksheet, _
rowCount As Integer, workCol As String, _
srcCol1 As Integer, srcCol2 As Integer)
Dim vrw As Variant, i As Long
For i = 2 To rowCount
vrw = Application.Match(wsRevFile.Range("A" & i), wsNewRev.Columns(2), Format(DateSerial(Year(Date), Month(Date), 0), "mm/dd/yyyy"), wsNewRev.Columns(1), 0)
If IsError(vrw) Then
vrw = Application.Match(wsRevFile.Range("A" & i), wsRevOld.Columns(1), 0)
If Not IsError(vrw) Then _
wsRevFile.Range(workCol & i) = Application.Index(wsRevOld.Columns(srcCol1), vrw)
Else
wsRevFile.Range(workCol & i) = Application.Index(wsNewRev.Columns(srcCol2), vrw, 1)
End If
Next i
End Sub
I am assuming this has to do with the way I assigned the Application Match function, because the formula without this part works for other columns. Any ideas on how I could get this to work?
Thanks for your help!
Try ajusting the variables of the following procedure, as I didn't figure out your input and output data:
Sub Main()
Dim SearchValue As Variant
Dim SearchColumn As Range
Dim ReturnColumn As Range
Dim ResultRows As Collection
Dim LastDate As Variant 'Date?
Dim iRow As Variant
SearchValue = 10 '<-- change to suit
Set SearchColumn = wsNewRev.Range("B1:B10")
Set ReturnColumn = wsNewRev.Range("C1:C10") '<-- change to suit
Set ResultRows = GetLoopRows(SearchColumn, SearchValue)
For Each iRow In ResultRows
If LastDate < ReturnColumn(iRow) Then
LastDate = ReturnColumn(iRow)
End If
Next iRow
Debug.Print LastDate
End Sub
Function GetLoopRows(ParamArray pParameters() As Variant) As Collection
'Obtém limites de laços com levando em conta condições
'[vetor1], [valor1], [vetor2], [valor2], ...
Dim iCondition As Long
Dim i As Variant
Dim iRow As Variant
Dim Result As Collection
Dim NumConditions As Long
Dim SearchCollection As Collection
Dim ArraysCollection As Collection
Dim iArray As Variant
NumConditions = (UBound(pParameters) - LBound(pParameters) + 1) / 2
Set ArraysCollection = New Collection
Set SearchCollection = New Collection
For i = LBound(pParameters) To UBound(pParameters) Step 2
ArraysCollection.Add pParameters(i + 0).Value2
SearchCollection.Add pParameters(i + 1)
Next i
Set Result = New Collection
For iRow = LBound(ArraysCollection(1)) To UBound(ArraysCollection(1))
For iCondition = 1 To NumConditions
If ArraysCollection(iCondition)(iRow, 1) <> SearchCollection(iCondition) Then GoTo Continue
Next iCondition
Result.Add CLng(iRow)
Continue:
Next iRow
Quit:
Set GetLoopRows = Result
End Function

Find last cell with values using indexes

I try to find the last cell in a column that contains values. I know I can use something like:
Dim findme As Range = excel.Range("A1:A" & lastrow.row)
but I am looking for a way not to use this column-character format. Something like this VBA format
Dim rng as range
with myWorksheet
LastRow = .Cells(.Rows.Count, 1).End(xlUp).Row
rng = .Range(.Cells(1,1), .Cells(LastRow, 1))
end with
I created an helper class to solve this problem:
Imports Excel = Microsoft.Office.Interop.Excel
Public Class FindLastCellByIndex
Private Shared Function getStringRangeFormat(ByVal colNumber As Long, ByVal rowNumber As Long) As String
If colNumber > 0 AndAlso colNumber < 27 Then
Dim c As Char
c = Convert.ToChar(colNumber + 64)
Return (c & rowNumber & ":" & c).ToString
Else
Return ""
End If
End Function
Public Shared Function getSearchRange(ByRef _sheet As Excel.Worksheet, ByVal Col As Long, ByVal startRow As Long) As Excel.Range
Dim strColCharSpez As String = getStringRangeFormat(Col, startRow)
'Build String
Dim rng As Excel.Range = DirectCast(_sheet.Cells(_sheet.Rows.Count, startRow), Excel.Range)
Dim t As Long
t = rng.End(Excel.XlDirection.xlUp).Row
Return _sheet.Range(strColCharSpez & t)
End Function
End Class
This is the way to use it:
Dim rng As Excel.Range = FindLastCellByIndex.getSearchRange(_sheet, 3, 3)
Now you get the Range: Range(Cells(3,3), Cells(LastRow, 3) as an object.

Type mismatch VBA

This works Lastrow = 8, but not 9 (Type mismatch)
If i remove If Not (myarray = Empty) Then it does not work for 8
What is the easiest way to solve this?
Public Function GetRowToWriteOn(ByVal SheetName As String, ByVal idnr As Integer) As Integer
LastRow = (Sheets(SheetName).UsedRange.Rows.Count) + 1
MsgBox (LastRow)
myarray = Sheets(SheetName).Range("d8:d" & LastRow).Value
If Not (myarray = Empty) Then
For row = 1 To UBound(myarray, 1)
If (myarray(row, 1) = idnr) Then
GetRowToWriteOn = row
Exit Function
End If
Next
End If
GetRowToWriteOn = LastRow
Exit Function
End Function
MyArray is taking 2 different types, depending on the range given.
If you are looking at 1 cell, then it is a single variant (which can be tested if it is Empty)
If you are looking at 2 or more cells, then it becomes an array of variant, so you would have to test each cell.
myarray = Sheets(SheetName).Range("d8:d8").Value - myarray gets the value in d8
myarray = Sheets(SheetName).Range("d8:d9").Value - myarray(1,1) gets the value in d8, and myarray(2,1) gets the value in d9
to test, use:
if vartype(myarray)=vbArray then
' run through the array
else
' do single value stuff
endif
I feel like your code should look more like this
Option Explicit
Public Function GetRowToWriteOn(ByVal SheetName As String, ByVal idnr As Integer) As Integer
Dim lastrow As Long, row As Long
lastrow = (Sheets(SheetName).UsedRange.Rows.Count) + 1
MsgBox (lastrow)
Dim myarray() As Variant
myarray = Sheets(SheetName).Range("d8:d" & lastrow).Value
If Not (IsEmpty(myarray)) Then
For row = 1 To UBound(myarray, 1)
If (myarray(row, 1) = idnr) Then
GetRowToWriteOn = row
Exit Function
End If
Next
End If
GetRowToWriteOn = lastrow
Exit Function
End Function
BUT I also think there is another way to do what you want. A little simpler and used built in functions. I think I captured your intention here:
Dim RowToWriteOn As Long, SheetName As String, lastRow As Long
Dim rng As Range
SheetName = "Sheet1"
lastRow = (Sheets(SheetName).UsedRange.Rows.Count) + 1
Set rng = Sheets(SheetName).Range("d" & lastRow)
RowToWriteOn = rng.End(xlUp).row
Public Function GetRowToWriteOn(ByVal SheetName As String, _
ByVal idnr As Integer) As Long
Dim lastRow As Long, f As Range
lastRow = Sheets(SheetName).Cells(Rows.Count, 4).End(xlUp).Row
Set f = Sheets(SheetName).Range("D8:D" & lastRow).Find(what:=idnr, _
lookat:=xlWhole)
If Not f Is Nothing Then
GetRowToWriteOn = f.Row
Else
GetRowToWriteOn = lastRow + 1
End If
End Function
myarray = Sheets(SheetName).Range("d8:d" & LastRow)
(without value)...
And you can use: if ubound(myArray) > 1 then ;..
I think it could be as easy as this, no...?