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
Related
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
I'm getting error while writing variant variable vArr to text file. In this line I'm getting error:
textData = CStr(vArr)
I have searched but couldn't find a solution to my problem. I'm much appreciated for your help.
Here is the full code:
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 fileName As String, textData As String
Dim textRow As String, fileNo As Integer
fileName = "E:\Projeler\test.txt"
fileNo = FreeFile
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
textData = CStr(vArr)
Open fileName For Output As #fileNo
Print #fileNo, textData
Close #fileNo
'rOut(lrow).Resize(1, rSets.Columns.Count).Value = vArr
Else
Perm1 rSets, vArr, rOut, lSetN + 1, lrow
End If
Next j
End Sub
Change the array to a string with Join.
textData = CStr(join(vArr, chr(32)))
I've used a space (i.e. chr(32)) for the delimiter but you can change that.
I have this code. DataSet is set as a variant.
DataSet = Selection.Value
Works fine but is there a way I can change it to just column A, specifically cells A2 to A502? Ive tried setting that as the range but it doesn't work. It also needs to ignore blank spaces because not all of the cells will have content. I am trying to eliminate the need to highlight the cells as the entries will only be in that specific range.
Try these 2 versions:
Option Explicit
Public Sub getNonemptyCol_ForLoop()
Dim dataSet As Variant, fullCol As Variant, i As Long, j As Long
Dim lrFull As Long, lrData As Long, colRng As Range
Set colRng = ThisWorkbook.Worksheets(1).Range("A2:A502")
fullCol = colRng
lrFull = UBound(fullCol)
lrData = lrFull - colRng.SpecialCells(xlCellTypeBlanks).Count
ReDim dataSet(1 To lrData, 1 To 1)
j = 1
For i = 1 To lrFull
If Len(fullCol(i, 1)) > 0 Then
dataSet(j, 1) = fullCol(i, 1)
j = j + 1
End If
Next
End Sub
Public Sub getNonemptyCol_CopyPaste() 'without using a For loop
Dim dataSet As Variant, ws As Worksheet
Application.ScreenUpdating = False
Set ws = ThisWorkbook.Worksheets(1)
With ws.UsedRange
ws.Activate
.Range("A2:A502").SpecialCells(xlCellTypeConstants).Copy
.Cells(1, (.Columns.Count + 1)).Activate
ActiveSheet.Paste
dataSet = ws.Columns(.Columns.Count + 1).SpecialCells(xlCellTypeConstants)
'dataSet now contains all non-blank values
ws.Columns(.Columns.Count + 1).EntireColumn.Delete
.Cells(1, 1).Activate
End With
Application.ScreenUpdating = True
End Sub
Assign with dynamic column.
Sub SetActiveColunmInArray()
Dim w As Worksheet
Dim vArray As Variant
Dim uCol As Long
Dim address As String
Set w = Plan1 'or Sheets("Plan1") or Sheets("your plan name")
w.Select
uCol = w.UsedRange.Columns.Count
address = w.Range(Cells(1, 1), Cells(1, uCol)).Cells.address
vArray = Range(address).Value2
End Sub
I'm trying to write some VBA that will find the matching numbers that appear in both Sheet 1 and Sheet 2, and output them to Sheet 3. My code is below, but is producing no result. What am I doing wrong?
Sub match()
Dim a As Integer
dim i as long, ii as long
a = 2
Dim lastrow As Long
Dim ylastrow As Long
ylastow = Sheets("Sheet2").UsedRange.Rows.Count
lastrow = Sheets("Sheet1").UsedRange.Rows.Count
for i = a to lastrow
for ii = a to ylastrow
if Sheets("Sheet1").Cells(i,1) = Sheets("Sheet2").Cells(ii,2) then
Sheets("Sheet3").range("A100000").xlup
End Sub
Assuming you want the matching cells copied into sheet3 by rows continuously
You are missing next ii and next i and end if also your destination cell in sheet3 is not set right
this should work
Sub match()
Dim a As Integer
Dim i As Long, ii As Long, j As Long
a = 2
j = 2
Dim lastrow As Long
Dim ylastrow As Long
Sheet1rows = Sheets("Sheet1").UsedRange.Rows.Count + 1
sheet2rows = Sheets("Sheet2").UsedRange.Rows.Count + 1
For i = a To Sheet1rows
For ii = a To sheet2rows
If Sheets("Sheet1").Cells(i, 1) = Sheets("Sheet2").Cells(ii, 1) Then
Sheets("Sheet3").Range("A" & j) = Sheets("Sheet1").Cells(i, 1)
j = j + 1
End If
Next ii
Next i
End Sub
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...?