My goal is to delete all identical rows from a sheet.
I created two arrays. One is the "current row" (1) and the second array is refilled with one row after the other. This part works.
Now I want to match the two arrays and if they are identical the corresponding row should be deleted.
Then the "current row" changes to 2.
For j = 1 To VarAnzahlZeilen
i = 1
For i = 1 To VarAnzahlSpalten
ReDim Preserve ArrAktuelleZeile(i - 1) As String
ArrAktuelleZeile(i - 1) = Worksheets("Filter").Cells(j, i).Value
Next i
(i+1)
For n = j + 1 To VarAnzahlZeilen
k = 1
For k = 1 To VarAnzahlSpalten
ReDim Preserve ArrDurchlaufZeile(k) As String
ArrDurchlaufZeile(k - 1) = Worksheets("Filter").Cells(n, k).Value
Next k
If Not IsError(WorksheetFunction.Match(ArrAktuelleZeile, ArrDurchlaufZeile, 0)) Then
Rows(n).Delete
End If
Next n
Next j
The Runtime Error 13 appears at line:
If Not IsError(WorksheetFunction.Match(ArrAktuelleZeile, ArrDurchlaufZeile, 0)) Then
I tried the idea of #GTPV and modified it slightly to fit my needs (variable Range and comparing columns 1-30).
A syntax error appears
Worksheets("Filter").Range(.Cells(1, 1), .Cells(VarAnzahlZeilen, VarNutzerSpalte))).RemoveDuplicates Columns:=Array(1, 2, 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30), Header _
:=xlNo
One possibility is to use the built-in function from Excel which removes duplicates:
Worksheets("Filter").Range("$A$1:$C$10").RemoveDuplicates Columns:=Array(1, 3), Header _
:=xlNo
The above example will remove all duplicates in the range A1:C10 where a duplicate means "same value in column A AND same value in column C". This last parameter is controlled by the "Columns:=Array(1,3)" argument.
Getting only unique values based on column A alone would be coded as:
Worksheets("Filter").Range("$A$1:$C$10").RemoveDuplicates Columns:=Array(1), Header _
:=xlNo
Getting unique values based on the combination of all columns:
Worksheets("Filter").Range("$A$1:$C$10").RemoveDuplicates Columns:=Array(1,2,3), Header _
:=xlNo
Additional help on this function can be found on Microsoft website.
Related
(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.
I try to join the elements of a vector in vba.
First I search with a for loop specific strings.
CE_addr = FindAll(Workbooks("raw_data.xlsm").Worksheets("IQ_Key_Dev_Type").Cells(k, (p - 1)).Value, _
Worksheets("Sheet1").Range("O:O"), xlValues, xlWhole).Address
Afterwards I use the address object to get the corresponding values one column to the right.
rn = Split(CE_addr, ",")
n = UBound(rn, 1) - LBound(rn, 1)
For w = 0 To n
ReDim CE_cod(n) As Variant
CE_cod((w)) = Workbooks("raw_data.xlsm").Worksheets("CE_List").Range(rn(w))
Next w
So in the CE_cod vector I save all the corresponding values (sometimes it's just one value, sometimes more than one)
And now I want to concatenate all the contained (text) values with ";" as delimiter:
Cells(j, 3) = Join(CE_cod, ";")
Let's assume CE_cod has two entries in the first loop: ["XXX" , "YYY"]
and just one in the second loop: ["XXX"]
For some reason the result in Cell(j,3) in the first case (multiple values in CE_cod) is: ";YYY" (note that a delimiter is added, but not the expression itself).
In the second case the result is: "XXX"
I checked if the vector CE_cod contains all values with:
Cells(j, (10 + w)) = CE_cod((w))
and indeed, when I do this "XXX" and "YYY" are contained.
So why does the join function skips the first entry of the vector when there are multiple entries?
PS: I found the "FindAll" function here:
http://www.tushar-mehta.com/publish_train/xl_vba_cases/1001%20range.find%20and%20findall.shtml#_The_FindAll_function
I think your CE_cod populating loop should be closer to this.
Dim CE_cod As Variant
ReDim CE_cod(LBound(rn) to UBound(rn))
For w = LBound(rn) to UBound(rn)
'if you want to put a redim here, use Preserve
'ReDim Preserve CE_cod(w)
CE_cod(w) = Workbooks("raw_data.xlsm").Worksheets("CE_List").Range(rn(w))
Next w
The rn array is populated from a Split operation and as such, it is a zero-based, 1-D array.
Additionally, you shouldn't be using ReDim inside a loop without the Preserve parameter if you want to retain your values. In any event, there is no need to ReDim inside the loop since you know the target boundaries beforehand.
EDIT: The marked solution is correct. My problem was that I was incrementing the values I was using in the FieldInfo parameter of .TextToColumns rather than using absolute positions of splits
For Example:
1, 3, 2, 5 instead of 1, 4, 6, 11
ORIGINAL:
I am building a tool that will various record types (that come in as one column, many rows) and split them into columns (depending on certain values in the lines of data).
The data could look like this:
062017000JohnDoe 777E
It may be specified as (recordtype 2 chars; YYYY; three zeroes filler; Name; Filler Spaces; ID)
The delimiters are predefined based on the record type (defined by user) and subtype (often defined by the first 1 or 2 numbers in the data, depending on the record type).
I am using the .TextToColumns function like so:
DataToParse.TextToColumns Destination:=DataToParse, DataType:=xlFixedWidth, _
FieldInfo:=FieldInfoString, TrailingMinusNumbers:=True
Earlier in the code, I build the string
FieldInfoString = "Array(Array(0,1), Array(5,1), Array(12,1))"
I build the string using concatenation, adding ", Array(x , 1)" for each delimiter (looping through a variable length list of x's).
The 'FieldInfoString' variable is throwing an error. What should go in the FieldInfo parameter is:
FieldInfo:=Array(Array(0,1), Array(5,1), Array(12,1))
The string is dynamically created for many different file types. Is there a way to use this string to define the arrays for use in .TextToColumns?
I also tried altering my approach and dynamically building the array:
Dim FieldInfoArray()
' Here there is code to define DelimitersToParse - which ends up being a 1 column by x rows
' group of numbers that will be the first value in the arrays > i.e. Array(___, 1)
ReDim Preserve FieldInfoArray(DelimitersToParse.Rows.Count)
The first entry is (0,1)
FieldInfoArray(0) = Array(0,1)
i = 1
For Each ParsedDelimiter In DelimitersToParse.Cells
FieldInfoArray(i) = Array(ParsedDelimiter.Value, 1)
i = i + 1
Next ParsedDelimiter
DataToParse.TextToColumns Destination:=DataToParse, DataType:=xlFixedWidth, _
FieldInfo:=FieldInfoArray, TrailingMinusNumbers:=True
The array approach doesn't throw an error, and the data is split, but not by any of the delimiters that I defined. I'm not sure how to audit FieldInfoArray to see what is going on.
EDIT: have attempted #jochen 's answer, auditing my array. still having issues - the 77777 should be placed in one column but is split as 777 | 77
Completely edited after comment/edit:
Your Building approach is alright. You can loop to build an 0-based array with arrays using variables as you did above.
Enter a stopin the line before your TextToColumnsto have a look at the values and structure of your array. Using some test values and a simple structure it worked perfectly with me:
Sub test()
Dim Arr()
Dim v1 As Integer, v2 As Integer
ReDim Arr(0 To 4)
v1 = 2: v2 = 6
Arr(0) = Array(0, 2)
Arr(1) = Array(v1, 1)
Arr(2) = Array(v2, 2)
Arr(3) = Array(9, 2)
Arr(4) = Array(19, 2)
Range("A2").TextToColumns Destination:=Range("A2"), DataType:=xlFixedWidth, _
FieldInfo:=Arr, _
TrailingMinusNumbers:=True
End Sub
EDIT:
As you are still encountering problems (now your code splits but not on the right positions as I understand) I give you another example.
In my example-sheet I have a Range called Data with Data in the structure of your provided example-data (062017000JohnDoe 777E). I have another Rangecalled Delimiters with a matrix of widths and types (I prefer to use Text in some cases, especially if I dont want to loose 0's and blanks. My Delimiter matrix (5 rows, 2 columns) looks like:
0 2
2 1
6 2
9 2
19 2
Then I use the following single-line-code to split Data into Columns:
Range("Data").TextToColumns Destination:=Range("Data"), DataType:=xlFixedWidth, _
FieldInfo:=Range("Delimiters").Value2, _
TrailingMinusNumbers:=True
I'm trying to display multiple values on my excel file based on first line and first column. I've made a 2D array for my values. MyValue is an InputBox variable and I loop over the first column trying to find MyValue and I want to get on display values that are on "PSC", "BCCH" and so on columns, basically the same thing as a filter based on 1st column but displaying only some values from other columns. The output that i have for this operation is in this image:
However 136, 144 and 152 which are PSC values are on line 2, 3, 4 in my source excel file, I'm not sure what are those zeros. I want to diplay those values one under each other, I tried without offset method but if I don't use it I get no value displayed.
Dim1 = wSht.Range("A2", wSht.Range("A1").End(xlDown)).Cells.Count
Dim2 = wSht.Range("A1", wSht.Range("A1").End(xlToRight)).Cells.Count
For i = 1 To Dim1
For j = 1 To Dim2
If wSht.Cells(i, 1) = MyValue Then
If wSht.Cells(1, j) = "PSC" Then
ReDim Preserve Matrice(0 To 5, 0 To Matrice_size)
Matrice(0, Matrice_size) = wSht.Cells(i, j).value
Matrice_size = Matrice_size + 1
End If
If wSht.Cells(1, j) = "BCCH" Then
ReDim Preserve Matrice(0 To 5, 0 To Matrice_size)
Matrice(1, Matrice_size) = wSht.Cells(i, j).value
Matrice_size = Matrice_size + 1
End If
End If
Next j
Next i
For k = LBound(Matrice, 1) To UBound(Matrice, 1)
For l = LBound(Matrice, 2) To UBound(Matrice, 2)
ThisWorkbook.Worksheets(1).Range("A2").Offset(k, l).value = Application.Transpose(Matrice(k, l))
Next l
Next k
switch the first If line with the For j and also the corresponding last End If with the Next j, so the inner loop only runs through the correct rows.
it seems the data starts at line 2, so start the For i loop with 2.
if you only need data from those two columns you can look for them in a separate loop first and save their position, so you don't need a nested loop. (in this case my first point doesn't apply).
you don't even need a loop, you can use Excel's MATCH function.
the matrix and transpose seems to be an overkill. since i determines k and l you can get rid of this part, and write to Worksheets(1) directly from the i-j loop.
you have five rows in the matrix and every fifth output is a correct value, isn't that suspicious?
you seem to put values into line 0 and 1 of the matrix then read from line 1 and 2. this might be the reason for the line full of 0s
if you can provide what exactly you would expect from what exact source data, I can help you more. but without knowing, my solution would look something like
PSC_col = Application.WorksheetFunction.Match("PSC", wSht.Rows(1).EntireRow, 0)
BCCH_col = Application.WorksheetFunction.Match("BCCH", wSht.Rows(1).EntireRow, 0)
For i = 2 To wSht.[A2].End(xlDown).Row
If wSht.Cells(i, 1) = MyValue Then
ThisWorkbook.Worksheets(1).[A2].Offset(k, 0).Value = wSht.Cells(i, PSC_col).Value
ThisWorkbook.Worksheets(1).[B2].Offset(k, 0).Value = wSht.Cells(i, BCCH_col).Value
k = k + 1
End If
Next i
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)))