Unique values in Excel - arrays

I need to get a value which has the maximum number of occurrences in the unique data set from a big column based on a condition. I just need one value and not the array.
See below the example data that I am working with. I have done it in MATLAB but want to know it in Excel.
So in the above data for example, I want to get the unique values for lanes based on the value of #safea. So if #safea=102 then unique values of lanes=(2,3,1). But I want the value from these set of data which has maximum number of occurrences. In this case it is 2 because 2 has come up 5 times whereas 3 has come up once and 1 has come up as only 1 time.
Another example:
If I select #safea as 162, then the number of unique values in lanes (5 and 4) but 5 has come up 4 times and 4 has come up as only 1 time so the final answer that I want is '5'.

If you don't mind using VBA, I've devised a Function you can use for what you want. Given the #safea values are in column A and the lane values are in column B, you can use this:
Function MODEIF(criteria As Integer) As Integer
Dim count As Integer
count = Application.WorksheetFunction.CountA(Range("A:A"))
Dim list() As Integer
Dim size As Integer
size = 0
Do While count > 0
If (Range("A" & count) = criteria) Then
ReDim Preserve list(size)
list(size) = Range("B" & count)
size = size + 1
End If
count = count - 1
Loop
MODEIF = Application.WorksheetFunction.Mode(list)
End Function
Just put this Function in a Module, go to the spreadsheet, and type =MODEIF(102) or whatever #safea value you want the mode for and it will give you the answer.

You could also use this worksheet function to get a conditional MODE:
=MODE(IF(**your #safea value here**=$A$2:$A$22,$B$2:$B$22))
This is an array formula. Confirm entry by pressing Ctrl+Shift+Enter (not just Enter).

Related

Is there a way to transfer all values from one array to another, then erase the original array?

I'm running into a problem with a block of code I'm trying to develop at my job. Essentially, I'm creating a userform in excel where folks will enter data for railcars as they get loaded at a certain location (we'll call these "spot 1, spot 2, spot 3, etc.").
Sometimes they'll have to move that car to a different spot, in which case I want them to be able to keep all the information on the railcar from the first/original entry, and then erase the data from the original spot once that's done.
To accomplish this in a more streamlined fashion, I've established arrays for each of the 5 spots that reference all the cells they're entering data into on the userform:
Dim spot1information(14)
spot1information(0) = UserForm.ProductType1.Value
spot1information(1) = UserForm.ProductID1.Value
spot1information(2) = UserForm.BatchID1.Value
etc....
Dim spot2information(14)
spot2information(0) = UserForm.ProductType2.Value
spot2information(1) = UserForm.ProductID2.Value
spot2information(2) = UserForm.BatchID2.Value
etc....
And so forth for all five spots. I don't know if this makes things more difficult, but note that these array values aren't all of the same type. For instance, index (0) will be a string, but index (10) is a DATETIME and index (12) is defined as Long.
So say that they are moving a car from spot 1 to spot 2. In short, I want the code to do the following:
Replace the values of indices 0 - 6 in spot2information (which is currently empty) with the values of indices 0 - 6 in spot1information (which the user has filled on the userform).
I'm only interested in carrying over indices 0-6 because they contain the pertinent railcar information
Empty every value of spot1information to ""
To accomplish this, I tried the following code and a few variations thereof:
If OriginalSpot.Value = 1 Then
If DestinationSpot.Value = 2 Then
For i = 0 to 6
spot2information(i) = spot1information(i)
Next
For Each i in spot1information
spot1information(i) = ""
Next
End If
End If
However, this keeps coming up with a type mismatch. I figure because the data in the spot2information array is empty, and the data in the spot1information array is not, but I'm not entirely sure of a way around this.
Update: I did what was suggested below and replaced: spot1information(i) = "" with Erase spot1information
The code now essentially works! The values of array "spot2information" are now the former values of "spot1information", with "spot1information" now empty.
The 2D array suggested below also works like a charm. New problem I've been facing is that array values are updating, but the userform isn't. (note: in the future I'll be posting this type of thing as a separate question, my apologies!)
Easier to manage this as a 2D array:
Sub Tester()
Dim spots(1 To 5, 0 To 14), n As Long, i As Long
'fill your spot arrays from the form....
For n = 1 To 5
spots(n, 0) = UserForm.Controls("ProductType" & n).Value
spots(n, 1) = UserForm.Controls("ProductID" & n).Value
spots(n, 2) = UserForm.Controls("BatchID" & n).Value
'etc etc
Next n
'swap a spot with another
Debug.Print spots(2, 1), spots(3, 1)
SwapSpots spots:=spots, fromSpot:=2, toSpot:=3
Debug.Print spots(2, 1), spots(3, 1)
End Sub
Sub SwapSpots(spots, fromSpot As Long, toSpot As Long)
Dim n As Long
For n = 0 To 6
spots(toSpot, n) = spots(fromSpot, n)
spots(fromSpot, n) = Empty 'empty the source slot value
Next n
End Sub
Assuming the DataType of the arrays is the same by Index i.e. index(0) is string for all spots, Index(2) is long for all spots, and so on.
If that is the case then this part should not produce any error:
For i = 0 to 6
spot2information(i) = spot1information(i)
Next
The error should be happening in this part more precisely in the line marked with #
For Each i in spot1information
spot1information(i) = "" '#
Next
and the reason for the error it seems to be that trying to assign a string value "" to a numeric type, given the "mismatch" error.
Using For Each i in spot1information indicates that you want to "Initiate" or Erase the entire array, therefore I suggest to use this line instead of the For…Next method.
Erase spot1information
In regards this:
But I've now run into a new problem, where the values on the userform haven't updated to reflect the new values stored in the array. Do I need to somehow "refresh" the userform?
You just updated the arrays, then you need to run the procedures used to update the values of the objects affected by both arrays in the UserForm.

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.

Excel VBA Should I use Arrays or a Queue or Other? w/ Criteria passed as parameters

I have a data set with 2500 rows and 65 columns. I have a specific set of criteria than involves Greater/Less than > / < ,AND, OR, and Like Operators that needs to be evaluated on EACH row.
For example:
If Column 2 equals 1007001500 OR 1007000510 AND Column 3 > 25 AND < 25 AND Column 4 equals "asset" AND Column 5 equals "S" Then I need to assign a unique value for this row or in other words I need to classify this row into the 1 of 10 categories I have for this 2500 R x 65 C data set (which is an imported excel file).
I am fairly familiar with 1D and 2D arrays and I have gotten to the point of importing all the Logic into either 1D or 2D array. I am at this point deciding whether to evaluate the logic in one long 1D Array String across 1 Row and 1 Column or a larger 2D array with each categories' logic segregated into a separate row within the 2D array.
But I have also arrived at the question, is there a better data structure approach? I have briefly research Excel VBA Queues and have turned here for more expertise.
My data set will always have the same Columns labels I can used to evaluate upon. However my logic criteria is always subject to change by the user of the macro. So I am looking for a way to basically pass all my criteria as parameters to a data structure.
An array/matrix is a good way to handle data in VBA. Here is the way that I store such data:
LRL = Cells(Rows.Count, 1).End(xlUp).Row 'COunts the rows
LCL = Cells(2, Columns.Count).End(xlToLeft).Column 'Counts the columns
ReDim Level_Array_Unsort(LRL, LCL) 'Dimensionalizes the matrix
'This stores the data
For i = 1 To LRL
For j = 1 To LCL
Level_Array_Unsort(i, j) = Cells(i, j)
Next j
Next

Loop through table columns and store max or min values in array variable

I am trying to loop through a table in excel using VBA and store the maximum value of each column in an array variable for later use in the program.
I have taken a number of approaches similar to the code snippet below without success. For reference: mainTable is a Public ListObject variable defined earlier in the script and mainCols is a Pubic Long variable, also previously defined, which stores the width of mainTable:
Dim maxVals() As Long
ReDim maxVals(mainCols)
For x = 0 To mainCols - 1
maxVals(x) = mainTable.ListColumns(x + 1).TotalsCalculation = xlTotalsCalculationMax
Next x
The code above executes without error, but always returns 0
It is important that I don't hard code the columns/table location so that users can copy/paste a dataset of varying dimensions and run the script without errors. Also, assume that the user could run this on a dataset with whatever column headers they want.
Your code simply changes the calculation used in the Totals row of the table - it doesn't actually return the max value. You could use:
Dim maxVals() As Long
ReDim maxVals(mainCols)
For x = 0 To mainCols - 1
maxVals(x) = WorksheetFunction.Max(mainTable.ListColumns(x + 1).Range)
Next x

the listing of values in a column that correspond to a specific value in another column in excel

the source data is in the following format
A B
1 0
2 0
4 1
5 0
6 0
8 1
I originally intended to list the missing items in the column A but since that did not quite work out for me, I intend to achieve the same thing via the method I will propose now.
What I want is a list that goes
C
3
7
essentially giving me the missing numbers of the sequence by using the values provided in the column B. But basically any solution that would give me the values listed under column C would be very appreciated.
It should be noted that I am working with a large list so that manual filtering and such are not preferable.
If column B is not needed (it can be computed from A), assuming that you have the data on sheet named worksheet1, the VBA sub that writes all the missing values to column C is the following:
Sub worksheet1c()
'this contains the position in column A
Dim i As Integer
'this contains the position in column C
Dim j As Integer
'the numbers from 1
Dim number As Integer
'initialization
i = 1
j = 1
number = 1
'check all numbers in column A from row 2
Do While Sheets("worksheet1").Cells(i, 1).Value <> ""
'if there is a difference between number and the value in cell write them to column C
Do Until number = Sheets("worksheet1").Cells(i, 1).Value
Sheets("worksheet1").Cells(j, 3).Value = number
j = j + 1
number = number + 1
Loop
i = i + 1
number = number + 1
Loop
End Sub
In cell G4 enter an address you want to look in, for example A2:A5 and then use the array formula below. The only assumption is that the list of numbers should begin from 1. I know it looks crazy, the formula. This formula has to be applied to range, so select a range, then go to address bar paste this formula and press CTRL + SHIFT + ENTER. In this particular case the range has to be in column shape.
I left the formula as not formatted text, so it was easier to copy.
=IF(INT(SQRT(MMULT((IF(TRANSPOSE(MMULT(INDIRECT(G4),1+0*TRANSPOSE(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))))=MMULT(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))),1+0*TRANSPOSE(ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))),0,ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))^2),1+0*ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))/COUNTA(INDIRECT(G4))))=SQRT(MMULT((IF(TRANSPOSE(MMULT(INDIRECT(G4),1+0*TRANSPOSE(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))))=MMULT(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))),1+0*TRANSPOSE(ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))),0,ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))^2),1+0*ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))/COUNTA(INDIRECT(G4))),SQRT(MMULT((IF(TRANSPOSE(MMULT(INDIRECT(G4),1+0*TRANSPOSE(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))))=MMULT(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))),1+0*TRANSPOSE(ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))),0,ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))^2),1+0*ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))/COUNTA(INDIRECT(G4))),"")
The formula could be read as
=IF(INT(A)=A,A, "")
where A is
SQRT(MMULT((IF(TRANSPOSE(MMULT(INDIRECT(G4),1+0*TRANSPOSE(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))))=MMULT(ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))),1+0*TRANSPOSE(ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))),0,ROW(INDIRECT("1:"&MAX(INDIRECT(G4)))))^2),1+0*ROW(INDIRECT("1:"&COUNTA(INDIRECT(G4)))))/COUNTA(INDIRECT(G4)))

Resources