Retrieving values of Mode Array in Excel - arrays

We have some spreadsheets with thousands of numbers where we would like to return the mode arrays. MODE.MULT can only handle 254 numbers.
Trying to overcome this, we split the mode arrays to check 252 cells of a single column. We were then going to check the Mode from among these numbers (understanding that this adds a degree of error, but considering the set size, we believe it to be small).
This led us to assigning a jagged array to the mode function, but we're having problems retrieving the values therein:
Dim ModeArray() As Variant 'Also tried ModeArray as not an array of variants but that left the same errors
Dim RowSet As Integer
RowSet = 2
Dim rr As Range
For n = 2 To 252
Set rr = Sheet5.Range(Sheet5.Cells(7, n), Sheet5.Cells(260, n))
If Application.WorksheetFunction.CountBlank(rr) < 253 Then
'Needed because a 1004 error will pop if there are no numbers in a particular column
ModeArray = Application.WorksheetFunction.Mode_Mult(rr)
For i = 1 To UBound(ModeArray)
Sheet5.Cells(RowSet, n).Value = ModeArray(i)
'We get a few different errors. E.g. sub script out of range errors or if (1)(1) is tested "Object doesn't support this property or method (Error 438)" even though the TypeName(ModeArray(i)) is Double
RowSet = 1 + RowSet
Next
RowSet = 2
End If
Next
We are only expecting 2-3 modes per column, but gave space for 5. That's not our problem, however. Trying to retrieve the information out of ModeArray doesn't work, and it's type is Variant().
How can we get the actual values of the mode out, so we can report them in another table? I'm aware I could put in array functions directly in the worksheet, but I'd like to avoid dealing with "N/A" values down stream, and I don't have a way to determine the length of the mode array in a function.
Alternatively, is there a way to skip this all together and retrieve the modes of a very large data set?
Edit:
I should note that the above script will work if there is only one Mode, it's a problem if there are more than one modes.

MODE.MULT can handle more than 254 numbers in each parameter - its limitation is that it cannot handle more than 254 different parameters.
Sub modes()
Dim modearray As Variant
Dim j As Long
modearray = Application.WorksheetFunction.Mode_Mult(Range("Sheet1!A:B"))
If UBound(modearray) = 1 Then
Debug.Print modearray(1)
Else
For j = LBound(modearray) To UBound(modearray)
Debug.Print modearray(j, 1)
Next j
End If
End Sub
This works (a little slowly) for several thousand numbers in cols A & B

Related

How to Pass an Array to and from a Function?

(Fair Warning, I am self taught on VBA so I apologize in advance for any cringe-worthy coding or notations.)
I have an estimating worksheet in excel. The worksheet will have a section for the user to input variables (which will be an array). The first input variable will "reset" the remaining input variables to a standard value when the first variable is changed. The standard values for the input variables are stored in a function in a module. I am attempting to fill the input variable array with the standard values from the function and then display those values on the sheet. I was easily able to do this without arrays but have had no luck in moving everything into arrays.
This is for excel 2010. I previously did not use arrays and created a new variable when needed, however the estimating sheet has grown much larger and it would be better to use arrays at this point. I have googled this question quite a bit, played around with removing and adding parenthesis, changing the type to Variant, trying to set the input variable array to be a variable that is an array (if that makes sense?), and briefly looked into ParamArray but that does not seem applicable here.
Dim BearingDim(1 To 9, 1 To 4, 1 To 8) As Range
Dim arrBearingGeneral(1 To 5, 1 To 8) As Range
Dim Test As Variant
Private Sub Worksheet_Change(ByVal Target As Range)
'Set General Variable array to cells on the worksheet
For i = 1 To 5
For j = 1 To 8
Set arrBearingGeneral(i, j) = Cells(9 + i, 3 + j)
Next j
Next i
'Set Bearing Input Variables to Cells on the Worksheet
For p = 1 To 4
For i = 1 To 9
Select Case p
Case Is = 1
Set BearingDim(i, p, 1) = Cells(16 + i, 4)
Case Is = 2
Set BearingDim(i, p, 1) = Cells(27 + i, 4)
Case Is = 3
Set BearingDim(i, p, 1) = Cells(37 + i, 4)
Case Is = 4
Set BearingDim(i, p, 1) = Cells(49 + i, 4)
End Select
Next i
Next p
'Autopopulate standard input variables based on Bearing Type
inputMD_StdRocker BearingType:=arrBearingGeneral(1, 1), _
arrBearingDim:=BearingDim
End Sub
Sub inputMD_StdRocker(ByVal BearingType As String, ByRef _
arrBearingDim() As Variant)
Dim arrBearingDim(1 To 9, 1 To 4)
Select Case BearingType
Case Is = "MF50-I"
For j = 1 To 2
arrBearingDim(2, j) = 20
arrBearingDim(3, j) = 9
arrBearingDim(4, j) = 1.75
Next j
arrBearingDim(5, 1) = 15
'There are numerous more select case, but those were removed to keep it
'short
End Select
End Sub
The expected output is my "BearingDim" Array will have certain array index values set to a standard value from the "inputMD_StdRocker" function. Then those values will be displayed in the cell that corresponds to the array index.
Currently, I get a compile error "Type Mismatch, Array or User-Defined Type Expected". I have been able to get around the type mismatch by removing the () from "arrBearingDim()" in the function title for "inputMD_StdRocker" however, it will not pass the values back to my "BearingDim" array.
Any help would be greatly appreciated.
This is a partial answer to what (I think) is a misunderstanding you have of how to use arrays. There are a few problems in your code.
First, you're defining a two-dimensional and a three-dimensional array of Ranges when I believe you really only want to store the values captured from the worksheet. (If I'm wrong, then you are never initializing the array of Ranges, so none of the ranges in the array actually point to anything.)
Secondly, it looks as if your initial array arrBearingGeneral is always filled from the same (static) area of the worksheet. If this is so (and you really do want the values from the cells, not an array of Range objects), then you can create a memory-based array (read this website, especially section 19). So the first part of your code can be reduced to
'--- create and populate a memory-based array
Dim bearingDataArea As Range
Dim arrBearingGeneral(1 To 5, 1 To 8) As Variant
Set bearingDataArea = ThisWorkbook.Sheets("Sheet1").Range("D10:K14")
arrBearingGeneral = bearingDataArea.Value
Optionally of course you can calculate the range of your data instead of hard-coding it ("D10:K14"), but this example follows your own example.
While this isn't a complete answer, hopefully it clears up an issue to get you farther down the road.

VBA prevent empty cells/data in sheet or array

I'm having some trouble to wrap my head around an array problem.
I collect data from a sheet and stores it into an array. xData(0) will be completed and can not miss data. But xData(1) there the values are stored may miss a value so I want to replace the empty cell with a 0. Instead of using for loops to add 0 into the sheets I'm thinking of using an array, due to the reason that I cant find a way to add 0 directly to the sheet without decreasing the speed. I have around 10 sheets in 15 different workbooks and therefor I need to find a faster way to do this.
My code snippet is as following.
Dim xData(1) As Variant
'Collect the data from the sheets ()
xData(0) = xData(1) = Application.Transpose(Range(Cells(1, 1), Cells(1, 1).End(xlDown)).Value2)
xData(1) = xData(1) = Application.Transpose(Range(Cells(1, 2), Cells(1, 2).End(xlDown)).Value2)
This will produce a 2D array but the xData(1) will not be the same size as xData(0) if any cell is empty (missing data). Is there a way to change the size of xData(1) to fit the size of xData(0), and then add 0 to the remaining length. Is there any other way because this may cause problems later on too because of the case if 1 value is missing in the middle.
If your goal is to replace empties with zeros, here is the core of an approach that:
avoids loops
avoids VBA arrays
Say we have data in column A from A1 through A1000 with some empties in that range. Running:
Sub ChangeEmpty2Zero()
Dim rng As Range, rngE As Range
Set rng = Range("A1:A1000")
On Error Resume Next
Set rngE = rng.SpecialCells(xlCellTypeBlanks)
On Error GoTo 0
If Not rngE Is Nothing Then rngE = 0
End Sub
will fill those empties with zeros.
NOTE:
If the "bottom of the column is outside UsedRange, that "bottom" will not be changed.You could adapt this to run on whatever ranges in whatever worksheets you need.

Excel VBA: How to concatenate variant array elements (row numbers) into a range object?

I did research this question but could not find the specific answer I was looking for and am actually even more confused at present.
I created a macro that would run through rows on a sheet and run boolean checks on a number of cells in each row that looked for the presence or absence of specific values, or calculated the outcome of a specific inequality. On the basis of those checks, the macro may or may not pass the row number into a specific array. That part is working fine.
My issue is, now that I have the row numbers (stored in variant arrays) - I cannot figure out how to properly concatenate that data into a range and then take a bulk excel action on those items. What I'd like to do is create a range of those values and then delete all of those rows at once rather than looping through.
My macro is on my work computer, but here's something I wrote that should explain what I'm trying to do:
Sub Test()
Dim Str As String
Dim r As Range
Dim i, a As Integer
Dim Count As Integer
Dim RngArray()
Count = ThisWorkbook.Sheets("Sheet1").Cells(Rows.Count, "A:A").End(xlUp).Row
ReDim RngArray(Count)
a = 0
For i = 1 To Count
If Not i = Count Then
RngArray(a) = i
Str = Str & RngArray(a) & ":" & RngArray(a) & ", "
a = a + 1
ElseIf i = Count Then
RngArray(a) = i
Str = Str & RngArray(a) & ":" & RngArray(a)
a = a + 1
Else: End If
Next i
Set r = Range(Str)'Error Can Appear here depending on my concatenation technique
Range(Str).EntireRow.Delete 'error will always appear here
End Sub
I've combined a few steps here and left out any Boolean checks; in my actual macro the values in the arrays are already stored and I loop from LBound to UBound and concatenate those values into a string of the form ("1:1, 2:2, 3:3, ...., n:n")
The reason why I did this is that the rows are all over the sheet and I wanted to get to a point where I could pass the argument
Range("1:1, 2:2, 3:3, ..., n:n").EntireRow.Delete
I think it's clear that I'm just not understanding how to pass the correct information to the range object. When I try to run this I get a "Method Range of Object Global" error message.
My short term fix is to just loop through and clear the rows and then remove all of the blank rows (the macro keeps track of absolute positions of the rows, not the rows after an iterative delete) - but I'd like to figure out HOW to do this my way and why it's not working.
I'm open to other solutions as well, but I'd like to understand what I'm doing wrong here. I should also mention that I used the Join() to try to find a workaround and still received the same type of error.
Thank you.
After some experimentation with my dataset for the macro above, I discovered that it worked on small sets of data in A:A but not larger sets.
I ran Debug.Print Len(Str) while tweaking the set size and macro and found that it appears Range() can only accept a maximum of 240 characters. I still don't understand why this is or the reason for the specific error message I received, but the macro will work if Len(Str) < 240.
I'll have to loop backwards through my array to delete these rows if I want to use my present method...or I may just try something else.
Thanks to Andrew for his attention to this!

compare two worksheets (with different number of rows on each sheet) and to get the list of not matching rows

Below is my requirement
1.Two sheets are available , say Sh1, Sh2
2.Row count differs between both the sheets(row count may/may not be the same between them)
3.Used Column of the sheets varies for data considered for comparison
But at any given point of time, both sheets will have same number of Used columns
( ideally speaking range is Dynamic for both sheets)
4.Now I need to perform comparison between them and find out the excess records in Sh1, Sh2 and save them in diff worksheets Sh3, Sh4 or so.
5.The comparison performed needs to be a Row level Comparison between Sh1 & Sh2
6.I prefer using Arrays (Loading the worksheets to Arrays) and perform comparison on it and return value - PERFORMANCE PLAYS A MAJOR ROLE SINCE THE DATA MAY BE OFF MILLIONS OF RECORDS
Is there a way to perform comparison of two Arrays whose size are non-identical ?
Matched rows can be Ignored
Is there a way to apply some Join() functions and read a complete row from Array1 and compare them with Array2 ?
Sorry If im violating the Forum rules by Any means !
I hope there is no Discussion on Comparing Two sheets with different Dynamic ranges to find the mismatches either here or anywhere
I mostly see ppl limiting the comparison to only one Column , or just for fixed ranges
My ULTIMATE AIM IS TO DO SOME VBA CODE WHICH OPERATES KIND OF "BEYOND COMPARE"(Well not exact behavior, read it a like)
I have been working on a similar kind of vba macro.
hope the below code helps !
All you need is to picture and include is
1)Row level comparison if possible
2)Looping through arrays and finding the Difference
** I am
Code shown below :
'Comparison Code
Sub CompareMacro()
Application.ScreenUpdating = True
Dim sheet1, sheet2, sheet4, sheet3 As Worksheet
Dim rComp, rcomp1 As Range, addy As String, addz As String
Dim iRow As Long, jCol As Long
Dim kRow As Long, lCol As Long
Dim strFileRange As String
Dim strDBRange As String
Set sheet1 = Sheets("File")
Set sheet2 = Sheets("DB")
Set sheet3 = Sheets("File(-)DB")
Set sheet4 = Sheets("DB(-)File")
sheet1.Select
Set rComp = sheet1.UsedRange
sheet2.Select
Set rcomp1 = sheet2.UsedRange
addy = rComp.Address
addz = rcomp1.Address
ary1 = rComp
ary2 = rcomp1
ary3 = sheet3.Range(addy)
ary4 = sheet4.Range(addz)
'*********File MINUS DB code goes here*********
'Step 1
'VALIDATE IF THE ROW1 OF File matches with Any of the Row in DB
'This step should include Iteration of Rows in DB range
'Step 2
'Proceed with Next row (ROW1+1 OF File) if Match Occurs
'This step contains incremental of File row & Starting comparison from First Row of DB
'Step 3
'If no Match occurs , Add the Specific Row to ary3
'This step captures the complete Mismatch record of File Row
'*********DB MINUS File code goes here*********
'Similar to the Flow of File MINUS DB
'adding the Array3 & 4 resultant Sheets
sheet3.Range(addy) = ary3
sheet4.Range(addz) = ary4
End Sub

Vb.Net Sort 3x One Dimensional Arrays

I have 3 one dimensional arrays.
Each contains information that corresponds to the other 2 arrays.
e.g Array 1 contains a customer first name
Array 2 contains a customer last name
Array 3 contains the customer phone number.
This is not my actual example but is easiest to explain.
How do I sort all three arrays so that they are sorted in order by say customer last name.
If Mr Smith is sorted and has moved from position 10 to position 5 in the lastname array, I would expect his phone number and first name to also be in position 5 in the respective arrays.
I am dealing with arrays with 10,000's of items so I would like to avoid looping (my current method) as this is incredibly slow.
Hoping to use the array.sort methods.
Can someone help me?
Ok - So I have tried to use a new data Type but am still at a loss how I can instantly filter using this. Below is my sample code which has a couple of issues. If someone can resolve - it would love to learn how you did it.
The purpose of the code is to return an array containing grouped issues.
For simplicity I have assumed in the example that each constant found is an issue.
If an issue is found, combine it with other issues found on that same worksheet.
e.g The number 2 is found in both cells A1 and A2 on sheet 1. The array should return A1:A2.
If the issues are found in A1 on sheet 1 and A2 in sheet 2, two seperate array entries would be returned.
Test File and Code Here
Public Type Issues
ws_Sheet As Integer
rng_Range As String
s_Formula As String
s_Combined As String
d_ItemCount As Double
End Type
Sub IssuesFound()
Dim MyIssues() As Issues
Dim i_SheetCount As Integer
Dim s_Formula As String
Dim rng_Range As Range
Dim d_IssueCounter As Double
Dim s_SearchFor As String
Dim a_TempArray() As Issues
Dim d_InsertCounter As Double
d_IssueCounter = -1
' Loop All Sheets Using A Counter Rather Than For Each
For i_SheetCount = 1 To ActiveWorkbook.Sheets.Count
' Loop all Constants On Worksheet
For Each rng_Range In Sheets(i_SheetCount).Cells.SpecialCells(xlCellTypeConstants, 23)
If d_IssueCounter = -1 Then
' First Time and Issue Is Found, Start Recording In An Array
d_IssueCounter = d_IssueCounter + 1
ReDim MyIssues(0)
MyIssues(0).ws_Sheet = i_SheetCount
MyIssues(0).rng_Range = rng_Range.AddressLocal
MyIssues(0).s_Formula = rng_Range.Value
MyIssues(0).s_Combined = i_SheetCount & "#" & rng_Range.Value
MyIssues(0).d_ItemCount = 0
Else
' Going To Look For Issues Found On The Same Sheet with The Same Constant Value
s_SearchFor = i_SheetCount & "#" & rng_Range.Value
' HELP HERE: Need To Ideally Return Whether The Above Search Term Exists In The Array
' Without looping, and I want to return the position in the array if the item is found
a_TempArray = MyIssues 'Filter(MyIssues.s_Combined, s_SearchFor, True, vbTextCompare)
If IsVarArrayEmpty(a_TempArray) = True Then
' New Issue Found - Increase Counter By + 1
d_IssueCounter = d_IssueCounter + 1
' Increase The Array By 1
ReDim Preserve MyIssues(d_IssueCounter)
' Record The Information About The Constant Found. Sheet Number, Constant, Range, and also a combined string for searching and the array position
MyIssues(0).ws_Sheet = i_SheetCount
MyIssues(0).rng_Range = rng_Range.AddressLocal
MyIssues(0).s_Formula = rng_Range.Value
MyIssues(0).s_Combined = i_SheetCount & "#" & rng_Range.Value
MyIssues(0).d_ItemCount = 0
Else
' Get The Array Position Where Other Issues With The Same Worksheet and Constant are Stored
d_InsertCounter = a_TempArray.d_ItemCount
' Add The New Found Constant To The Range Already Containing The Same Constants on This Worksheet
MyIssues(d_InsertCounter).rng_Range = Union(rng_Range, Range(MyIssues(d_InsertCounter).rng_Range)).AddressLocal
End If
End If
Next
Next
End Sub
Function IsVarArrayEmpty(ByRef anArray As Issues)
Dim i As Integer
On Error Resume Next
i = UBound(anArray, 1)
If Err.Number = 0 Then
IsVarArrayEmpty = False
Else
IsVarArrayEmpty = True
End If
End Function
Sample Test File and Code Here
As suggested, you should not be using concurrent arrays at all. You should be defining a type with three properties and then creating a single array or collection of that type.
To answer your question though, there is no way to sort three arrays in concert but there is a way to sort two. What that means is that you can create a copy of the array that you want to use as keys and then use the copy to sort one of the other arrays and the original to sort the other. Check out the documentation for the Array.Copy overload that takes two arrays as arguments.
That said, copying the array and then sorting twice is a big overhead so you may not gain much, if anything, from this method. Better to just do it the right way in the first place, i.e. use a single array of a complex type rather than concurrent arrays of simple types. It's not 1960 any more, after all.

Resources