Use IFERROR, INDEX, MATCH if the amount of column is limited - excel

I would like to know if it would be possible to use the IFERROR, INDEX, MATCH function on below scenario.
D2:=INDEX($A$2:$A$16, MATCH(0, COUNTIF($D$1:$D1, $A$2:$A$16), 0))
E2: =IFERROR(INDEX($B$2:$B$16, MATCH(0, COUNTIF($D2:D2,$B$2:$B$16)+IF($A$2:$A$16<>$D2, 1, 0), 0)), "")
H2: =IFERROR(INDEX($C$2:$C$16, MATCH(0, COUNTIF($D2:D2,$C$2:$C$16)+IF($A$2:$A$16<>$D2, 1, 0), 0)), "")
I2: =IFERROR(INDEX($C$2:$C$16, MATCH(0, COUNTIF($D2:H2,$C$2:$C$16)+IF($A$2:$A$16<>$D2, 1, 0), 0)), "")
Based on the data of Countries and Cities filled in yellow on the left, by using the IFERROR, INDEX, MATCH formula I managed to get all the data I need. Now if there are more than 3 City, I want for the excel to continue the list of cities by creating another row under it as example of row filled in red.
I hope it makes sence. Let me know if it's possible.

You did tag vba as well as excel-formula so give this a try
Sub condense()
Dim src, dest(), ws As Worksheet, srcRange As Range, i As Long, j As Long, countryCount As Long, rowNum As Long
Set ws = ActiveSheet
Set srcRange = ws.Cells(1, 1).Resize(ws.Cells(ws.Rows.Count, 1).End(xlUp).Row, 3)
src = srcRange.Value2
ReDim dest(1 To UBound(src, 1) - 1, 1 To 7)
rowNum = 1
i = 2
Do While i <= UBound(src, 1)
countryCount = Application.CountIf(srcRange.Columns(1), src(i, 1))
For j = 1 To countryCount
dest(rowNum + Int((j - 1) / 3), 1) = src(i + j - 1, 1)
dest(rowNum + Int((j - 1) / 3), 2 + ((j - 1) Mod 3)) = src(i + j - 1, 2)
dest(rowNum + Int((j - 1) / 3), 5 + ((j - 1) Mod 3)) = src(i + j - 1, 3)
Next j
i = i + countryCount
rowNum = rowNum + 1 + Int((countryCount - 1) / 3)
Loop
ws.Cells(2, 4).Resize(rowNum, 7).Value2 = dest
With ws.Cells(1, 4).Resize(1, 7)
.Value2 = Strings.Split("Country,City1,City2,City3,Image1,Image2,Image3", ",")
.EntireColumn.AutoFit
End With
End Sub
EDIT 17-Jul-2022 (per comment from OP)
Sub condenseInto4cols()
Dim src, dest(), ws As Worksheet, srcRange As Range, i As Long, j As Long, countryCount As Long, rowNum As Long
Set ws = ActiveSheet
Set srcRange = ws.Cells(1, 1).Resize(ws.Cells(ws.Rows.Count, 1).End(xlUp).Row, 3)
srcRange.Sort key1:=ws.Cells(2, 1), order1:=xlAscending, Header:=xlYes
src = srcRange.Value2
ReDim dest(1 To UBound(src, 1) - 1, 1 To 9)
rowNum = 1
i = 2
Do While i <= UBound(src, 1)
countryCount = Application.CountIf(srcRange.Columns(1), src(i, 1))
For j = 1 To countryCount
dest(rowNum + Int((j - 1) / 4), 1) = src(i + j - 1, 1)
dest(rowNum + Int((j - 1) / 4), 2 + ((j - 1) Mod 4)) = src(i + j - 1, 2)
dest(rowNum + Int((j - 1) / 4), 6 + ((j - 1) Mod 4)) = src(i + j - 1, 3)
Next j
i = i + countryCount
rowNum = rowNum + 1 + Int((countryCount - 1) / 4)
Loop
ws.Cells(2, 4).Resize(rowNum, 9).Value2 = dest
With ws.Cells(1, 4).Resize(1, 9)
.Value2 = Strings.Split("Country,City1,City2,City3,City4,Image1,Image2,Image3,Image4", ",")
.EntireColumn.AutoFit
End With
srcRange.Sort key1:=ws.Cells(2, 2), order1:=xlAscending, Header:=xlYes
End Sub

Related

Multiple columns in ListBox (Userform) VBA

I have a problem with displaying multiple columns in a ListBox in my UserForm.
Everything is working until my numbers of column is max 10.
My code:
Private Sub FindButton_Click()
ListBoxResult.Clear
ListBoxResult.ColumnCount = 14
Dim RowNum As Long
RowNum = 1
Do Until Sheets("db").Cells(RowNum, 1).Value = ""
If InStr(1, Sheets("db").Cells(RowNum, 2).Value, FindDMC.Value, vbTextCompare) > 0 Then
On Error GoTo next1
ListBoxResult.AddItem Sheets("db").Cells(RowNum, 1).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 2) = Sheets("db").Cells(RowNum, 2).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 3) = Sheets("db").Cells(RowNum, 3).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 4) = Sheets("db").Cells(RowNum, 4).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 5) = Sheets("db").Cells(RowNum, 5).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 6) = Sheets("db").Cells(RowNum, 6).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 7) = Sheets("db").Cells(RowNum, 7).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 8) = Sheets("db").Cells(RowNum, 8).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 9) = Sheets("db").Cells(RowNum, 9).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 10) = Sheets("db").Cells(RowNum, 10).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 11) = Sheets("db").Cells(RowNum, 11).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 12) = Sheets("db").Cells(RowNum, 12).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 13) = Sheets("db").Cells(RowNum, 13).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 14) = Sheets("db").Cells(RowNum, 14).Value
ListBoxResult.List(ListBoxResult.ListCount - 1, 15) = Sheets("db").Cells(RowNum, 15).Value
End If
next1:
RowNum = RowNum + 1
Loop
End Sub
ListBoxResult.ColumnCount and properties is 14, also Column widths is ok.
After runing my code the failure code is Run-time error '380': Could not set the List property. Invalid property value. At first, I was thinking that maybe ListBoxes have limits for columns, but I found ListBoxes with 60 columns on the Internet.
I am trying also this, but still doesn't work:
Private Sub Browser_RMA_Initialize()
ListBoxResult.RowSource = "db!a1:z1"
ListBoxResult.ColumnCount = 14
ListBoxResult.ColumnWidths = "50;50;50;50;50;50;50;50;50;50;50;50;50;50;"
ListBoxResult.ColumnHeads = True
End Sub
Could you support me, please?
Assigning to .Columnproperty avoids transposing
As late addition to #Dy.Lee 's valid and already accepted array approach (see my comment), I demonstrate a way how to avoid both repeated redimming [4] and transposing [5]:
Option Explicit ' declaration head of UserForm code module
Private Sub FindButton_Click()
'[0] where to search
Const SearchCol As Long = 2 ' get search items from 2nd column
'[1] define data set
Dim data As Variant
data = Tabelle1.Range("A1").CurrentRegion ' << change to your project's sheet Code(Name)
Dim ii As Long: ii = UBound(data, 1) ' row count
Dim jj As Long: jj = UBound(data, 2) ' column count
'[2] provide for sufficient result rows (array with converted row : columns order)
Dim results() As Variant
ReDim Preserve results(1 To jj, 1 To ii) ' redim up to maximum row count ii
'[3] assign filtered data
Dim i As Long, j As Integer, n As Long
For i = 1 To ii
If InStr(1, data(i, SearchCol), FindDMC.Value, vbTextCompare) > 0 Then
'' If data(i, SearchCol) = FindDMC.Value Then ' exact findings
n = n + 1
For j = 1 To jj
results(j, n) = data(i, j)
Next
End If
Next i
'[4] fill listbox with results
With ListBoxResult
.Clear
.ColumnCount = 14
.ColumnWidths = "50;50;50;50;50;50;50;50;50;50;50;50;50;50;"
If n Then
'[4] redimension only a 2nd time (& last time)
ReDim Preserve results(1 To jj, 1 To n)
'[5] assign results to listbox'es .Column property
.Column = results ' << .Column property avoids unnecessary transposing
End If
End With
End Sub
The column index of the listbox also starts at 0. The index number of additem should be 0, and you specified 15 at the end, then the number of columns becomes 16, so an error occurs because column 14 is exceeded.
It would be convenient to use an array.
Private Sub FindButton_Click()
Dim Ws As Worksheet
Dim vDB As Variant, vResult()
Dim i As Long, j As Integer, n As Long
Set Ws = Sheets("db")
vDB = Ws.Range("a1").CurrentRegion
For i = 1 To UBound(vDB, 1)
If InStr(1, vDB(i, 2), FindDMC.Value, vbTextCompare) > 0 Then
n = n + 1
ReDim Preserve vResult(1 To 14, 1 To n)
For j = 1 To 14
vResult(j, n) = vDB(i, j)
Next
End If
Next i
With ListBoxResult
.Clear
.ColumnCount = 14
.ColumnWidths = "50;50;50;50;50;50;50;50;50;50;50;50;50;50;"
If n Then
If n = 1 Then
.Column = vResult
Else
.List = WorksheetFunction.Transpose(vResult)
End If
End If
End With
End Sub

Modify a VBA code to drag down sumifs to a large number of rows and columns

I have the below code in part of my excel vba that I need to amend but could do with some help understanding.
In cells T, W, and Z there is a sum if formula in row 2, this VBA replicates this formula down to the last row. I am trying to update the formula so that it does this for column T,W,Z,AC and AF
I've changed the 1-3 to 1-5 but it is debugging at the doc(ii) line.
Please could anyone help me up understand and update it.
Dim a, k, i As Long, ii As Long, t As Long, w(1 To 3), x, dic(1 To 3) As Object
With Range("k2", Range("k" & Rows.Count).End(xlUp))
k = .Value
a = .Columns(8).Resize(, 10).Value
End With
For i = 1 To 3
Set dic(i) = CreateObject("Scripting.Dictionary")
dic(i).CompareMode = 1
ReDim x(1 To UBound(a, 1), 1 To 1) As Double: w(i) = x
Next
For i = 1 To UBound(a, 1)
For ii = 1 To 3
dic(ii)(a(i, (ii - 1) * 3 + ii + 1)) = i
Next
Next
For i = 1 To UBound(a, 1)
For ii = 1 To 3
t = (ii - 1) * 3 + ii
If dic(ii).exists(a(i, t)) Then
x = w(ii)
x(dic(ii)(a(i, t)), 1) = x(dic(ii)(a(i, t)), 1) + k(i, 1)
w(ii) = x
End If
Next
Next
For i = 1 To 3
Cells(2, (i + 4) * 4).Resize(UBound(a, 1)).Value = w(i)
Next
End Sub

INDEX(MATCH, MATCH) in VBA (Type 13 Type mismatch)

I'm trying to use an INDEX(MATCH,MATCH) to recreate this formula:
INDEX(Inflation!H$129:H$188,MATCH($J10,Inflation!$C$129:$C$188,0))
You may notice that the columns are not locked on the INDEX array field Inflation!H$129:H$188. I didn't know how to move over one column so I thought I would use an INDEX(MATCH,MATCH).
With my code below I get a error 13 Type Mismatch even if I swap out arrInflation_Bucket(I, 1) for "EWIP", a known entry in the searched range.
Sub Costs__Repossession_Costs()
Dim I, J, arrInflation_Bucket, arrInflation_Bucket_Label, arrNumber_of_Assets, arrQuarters, arrInflation_Label_Match, arrInflation_Quarter_Match
arrNumber_of_Assets = Range("Costs.Number_of_Assets")
arrQuarters = Range("Quarters_1to40")
arrInflation_Bucket = Range("Costs.Inflation_Bucket")
arrInflation_Bucket_Label = Range("Inflation.Inflation_Bucket_Label")
ReDim arrCosts__Repossession_Costs(1 To UBound(arrNumber_of_Assets, 1), 1 To UBound(arrQuarters, 2))
For I = LBound(arrInflation_Bucket, 1) To UBound(arrInflation_Bucket, 1)
For J = LBound(arrQuarters, 2) To UBound(arrQuarters, 2)
arrInflation_Label_Match(I, J) = Application.Match(arrInflation_Bucket(I, 1), Range("Inflation.Inflation_Bucket_Label"), 0)
arrInflation_Quarter_Match(I, J) = Application.WorksheetFunction.Match(arrQuarters(1, J), Range("Quarters_1to40"), 0)
arrCosts__Repossession_Costs(I, J) = Application.WorksheetFunction.Index(Range("Inflation.Cumulative"), arrInflation_Label_Match, arrInflation_Quarter_Match)
Next J
Next I
End Sub
This is by far the most complicated formula I will need to do in VBA so if I can get this it should be all smooth sailing.
OK, I've updated the code with he instructions of the first two comments, very helpful. It is now performing the two matches correctly (used to error out on the first, but now gives me row 48 adn column 1 which is correct) but Now Type 13 errors on the INDEX().
Sub Costs__Repossession_Costs()
Dim I As Long
Dim J As Long
Dim arrInflation_Bucket As Variant
Dim arrInflation_Bucket_Label, arrNumber_of_Assets, arrQuarters, arrInflation_Label_Match, arrInflation_Quarter_Match, arrInflation_Cumulative
arrNumber_of_Assets = Range("Costs.Number_of_Assets")
arrQuarters = Range("Quarters_1to40")
arrInflation_Bucket = Range("Costs.Inflation_Bucket")
arrInflation_Bucket_Label = Range("Inflation.Inflation_Bucket_Label")
arrInflation_Cumulative = Range("Inflation.Cumulative")
ReDim arrCosts__Repossession_Costs(1 To UBound(arrNumber_of_Assets, 1), 1 To UBound(arrQuarters, 2)), _
arrInflation_Label_Match(1 To UBound(arrNumber_of_Assets, 1), 1 To UBound(arrQuarters, 2)), _
arrInflation_Quarter_Match(1 To UBound(arrNumber_of_Assets, 1), 1 To UBound(arrQuarters, 2))
For I = LBound(arrInflation_Bucket, 1) To UBound(arrInflation_Bucket, 1)
For J = LBound(arrQuarters, 2) To UBound(arrQuarters, 2)
arrInflation_Label_Match(I, J) = Application.Match(arrInflation_Bucket(I, 1), Range("Inflation.Inflation_Bucket_Label"), 0)
arrInflation_Quarter_Match(I, J) = Application.WorksheetFunction.Match(arrQuarters(1, J), Range("Quarters_1to40"), 0)
arrCosts__Repossession_Costs(I, J) = Application.WorksheetFunction.Index(arrInflation_Cumulative, arrInflation_Label_Match, arrInflation_Quarter_Match)
Next J
Next I
End Sub
OK, so I caved like a you know what and asked my genius friend for help with this. His fantastic code is below:
Function firstMatchInRow(ByVal val, arr, ByVal col As Long) As Long
Dim i As Long
For i = LBound(arr, 1) To UBound(arr, 1)
If arr(i, col) = val Then firstMatchInRow = i: Exit Function
Next i
End Function
Sub Costs__Repossession_Costs()
Dim i As Long, j As Long, tempLabelMatch As Long
Dim arrInflation_Bucket, arrInflation_Bucket_Label, arrNumber_of_Assets, arrQuarters, arrInflation_Cumulative, arrCosts__Repossession_Costs
arrQuarters = Range("Quarters_1to40")
arrInflation_Bucket = Range("Costs.Inflation_Bucket").Value
arrInflation_Bucket_Label = Range("Inflation.Inflation_Bucket_Label").Value
arrInflation_Cumulative = Range("Inflation.Cumulative").Value ' + add values
ReDim arrCosts__Repossession_Costs(1 To UBound(arrInflation_Cumulative, 1), 1 To UBound(arrQuarters, 2))
For i = LBound(arrInflation_Bucket, 1) To UBound(arrInflation_Bucket, 1)
tempLabelMatch = firstMatchInRow(arrInflation_Bucket(i, 1), arrInflation_Bucket_Label, 1)
For j = LBound(arrInflation_Cumulative, 2) To UBound(arrInflation_Cumulative, 2)
arrCosts__Repossession_Costs(i, j) = arrInflation_Cumulative(tempLabelMatch, j)
Next j
Next i
End Sub

my average function is counting blank cells as 0

As far as I know, Excel Average functions do not include cells that are blank. However, my this appears to be exactly what my code is doing:
Sub sumavg()
Dim rowCounter As Long
Dim colCounter As Long
Dim values() As Variant
Const START_COL As Long = 1
Const END_COL As Long = 6
Const OUTPUT_COL_START As Long = 20
With Worksheets("datasummary")
'Load the values into an array
values = .Range(.Cells(1, 1), .Cells(199, 18)).Value
For rowCounter = 1 To 40
ReDim rowresults(1 To 1, START_COL To END_COL)
For colCounter = START_COL To END_COL
'find average of AOIentries values
rowresults(1, colCounter) = Application.WorksheetFunction.Average(values((5 * rowCounter - 2), colCounter), values((5 * rowCounter - 2), colCounter + 6), values((5 * rowCounter - 2), colCounter + 12))
Next colCounter
'print row of results
.Range(.Cells(5 * rowCounter - 2, OUTPUT_COL_START), .Cells(5 * rowCounter - 2, OUTPUT_COL_START + END_COL - START_COL)).Value = rowresults
For colCounter = START_COL To END_COL
'find average of RT values
rowresults(1, colCounter) = Application.WorksheetFunction.Average(values((5 * rowCounter - 1), colCounter), values((5 * rowCounter - 1), colCounter + 6), values((5 * rowCounter - 1), colCounter + 12))
Next colCounter
'print row of results
.Range(.Cells(5 * rowCounter - 1, OUTPUT_COL_START), .Cells(5 * rowCounter - 1, OUTPUT_COL_START + END_COL - START_COL)).Value = rowresults
Next rowCounter
End With
End Sub
Here is the code to print values including blank cells:
For r = 1 To UBound(d, 1)
k = d(r, COL_BLOCK) & "|" & d(r, COL_TRIAL) 'create key
If d(r, 19) = 1 Then
dBT(k) = dBT(k) + IIf(d(r, COL_AOI) = "AOI Entry", 1, 0) 'get count
Else: dBT(k) = ""
End If
Next r
'populate array with appropriate counts for each row
For r = 1 To UBound(d, 1)
k = d(r, 1) & "|" & d(r, 2) 'create key
resBT(r, 1) = dBT(k) 'get the count
Next r
Call createsummarytable
Call PopSummaryAOI(dBT)
dBT.RemoveAll
For r = 1 To UBound(d, 1)
k = d(r, COL_BLOCK) & "|" & d(r, COL_TRIAL) 'create key
dBT(k) = d(r, COL_RT)
Next r
As far as I can tell, the cell is entirely blank, so it shouldn't be included in the average, but (20 + 17)/2 =/= 12.33, whereas (20 + 17 + 0)/3 = 12.33.
When you call .Range(.Cells(1, 1), .Cells(199, 18)).Value, you end up with an array of Variant. WorksheetFunction.Average treats Ranges and Variant arrays differently than it treats Variants. If you give it individual Variant for arguments, it casts them to Doubles, and casting Empty to a Double results in 0. If you want it to ignore empty cells, you need to pass it a Range or a Variant():
Sub Example()
Dim test As Variant
test = Empty 'This is what you get from an EmptyCell.Value
Debug.Print CDbl(test) 'Prints 0.
Debug.Print WorksheetFunction.Average(test, 0, 10) 'Prints 3.33333333333333.
Range("A1").ClearContents 'Nothing in A1 now.
Debug.Print Range("A1").Value = Empty 'Prints True
Debug.Print WorksheetFunction.Average(Range("A1"), 0, 10) 'Prints 5
End Sub

Excel list, 2000 rows to 250 page templates

Unfortunately i am not a master of excel. I have 2000 rows of information that i must fill out manually. Then i use vlookup to sort them into sheets with a template that can be printed out. My problem is i cannot find a way to make the vlookup less manual. I am hoping i can be helped to find a way not to manually add 250 sheets and do the vlookup for each of the 2000 rows of information.
Is it possible to add a new sheet automatically or even tell the vlookup to look at the next row, like an index would with php/mysql?
Any help is appreciated.
This is a working example of what i am trying to achieve.
There are a variety of ways of solving this. I got the following to work.
Note: this assumes you start with one sheet in your s/sheet that has the main data list starting in A1
See if this works for you...
Sub ParseData()
Dim Entries As Range, entry As Range, cnt As Integer, rowID As Integer, col As Integer, rw As Integer
Set Entries = Worksheets(1).Range("A2:A" & Worksheets(1).Range("A2").End(xlDown).Row)
cnt = 0
rowID = 0
For Each entry In Entries
cnt = cnt + 1
If cnt = 1 Or cnt Mod 10 = 1 Then
Worksheets.Add After:=Worksheets(Worksheets.Count)
rowID = 1
End If
With Worksheets(Worksheets.Count)
col = IIf(rowID <= 5, 1, 6)
rw = IIf(rowID <= 5, rowID, rowID - 5)
.Cells.HorizontalAlignment = xlCenter
.Cells((8 * rw - 7), col) = "Plan#"
.Cells((8 * rw - 7), col + 1) = entry
.Cells((8 * rw - 7), col + 2) = "Vol#"
.Cells((8 * rw - 7), col + 3) = entry.Offset(0, 2)
With .Range(.Cells((8 * rw - 7), col), .Cells((8 * rw - 7), col + 3))
.Font.Bold = True
.Borders(xlEdgeBottom).LineStyle = xlContinuous
End With
.Cells((8 * rw - 7) + 2, col) = "To:"
.Cells((8 * rw - 7) + 2, col + 1) = entry.Offset(0, 3)
.Cells((8 * rw - 7) + 4, col) = "Date:"
.Cells((8 * rw - 7) + 4, col + 1) = entry.Offset(0, 4)
.Cells((8 * rw - 7) + 4, col + 1).NumberFormat = "m/d/yyyy"
rowID = rowID + 1
End With
Next entry
End Sub

Resources