VBA - Index / Match function with multiple criteria - vba

I am facing an issue when trying to compile a multiple criteria Index/Match code in VBA. This might be simple - but i am fairly new to VBA and nothing i have found around here worked.
Example:
I have a large amount of data in a specified range: Sheets("CustomerAccounts").Range(CustomerSheetRange)) - I need VBA to return data from column titled "Values" by checking three criteria: Customer = X, Type = External, OriginCountry = UAE (columns are not adjacent in the original spreadsheet)
The criteria are stored in separate variables set by user of the macro beforehand.
Customer | Type | Origin | Destination | Values
X | Internal | UAE | SA | Value 1
Y | Internal | UAE | SA | Value 2
X | External | UAE | SA | Value 3
X | External | ZA | UAE | Value 4
At the moment i have the following (quite bulky) code which finds the value using one criteria - OriginCountry variable.
The code searches for it in a pre-specified column - OriginCountryColumn.
ResultString = Application.Index(Sheets("CustomerAccounts").Range(CustomerSheetRange), Application.Match(OriginCountry, Sheets("CustomerAccounts").Range(OriginCountryColumn), 0), Application.Match("Values", Sheets("CustomerAccounts").Range(TitleRowCust), 0))
I would like to modify the code to also match the Type and The customer.
Is it possible to expand the above Index/Matxh function - or should i use a different approach?
Any advice is appreciated.

You may walk through rows checking matches:
Dim row as Long
With Sheets("CustomerAccounts").Range(CustomerSheetRange))
For row = 2 To .Rows.Count 'Starts in 2 to ignore header!
If .Cells(row, costumerCol).Value Like costumerCriteria And .Cells(row, typeCol).Value Like typeCriteria And .Cells(row, originCol).Value Like originCriteria Then
'This is a match!
Debug.Print .Cells(row, valueCol)
End if
Next
End With
You must replace costumerCol, typeCol, originCol and valueCol with corresponding column number and costumerCriteria, typeCriteria and originCriteria with criteria specified.
If column indexes are also variable, make a search for them in first row before walking through rows.

First, format the range containing your data to a Table (See http://office.microsoft.com/en-001/excel-help/quick-start-create-an-excel-table-HA010359200.aspx on how to do that). Once done, use the following VBA code:
SomeCustomer = Range("...").Value
SomeType = Range("...").Value
SomeOrigin = Range("...").Value
ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=1, Criteria=SomeCustomer
ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=2, Criteria=SomeType
ActiveSheet.ListObjects("Table1").Range.AutoFilter Field:=3, Criteria=SomeOrigin
Notes:
You might have to customize this macro for your specific needs
Name of table can be found/modified through Formulas>Name Manager
ActiveSheet might be modified to the actual sheet you are using

Related

Using Indirect within Advanced Filter

Hello Stack Overflow Community,
I am currently creating an automated report that imports two or more CSV files from a given folder. The unique identifier that I need to use for a VLOOKUP is in an obscure column, so what I needed to do was Cut and Insert the found column onto Column 'A'. Example Below.
With wksRawData
'~~> Move Process ID Column to Column 'A' for VLOOKUP
.Columns(WorksheetFunction.Match("pid", .Range("1:1"), 0)).Cut
.Columns(1).Insert Shift:=xlToLeft
'~~> Remove Additional Headers
.Cells.RemoveDuplicates Columns:=Array(1)
End With
This part works great, but I have a formula in another worksheet that is supposed to reference $A2 and it gets changed to $B2. The reason why it should reference $A2 is because it is being used in an Advanced Filter Formula, and is the very first data point. Formula Below.
=IF(COUNTIF($C:$C, 'Raw Data'!$A2)=0, TRUE, FALSE)
When used in tandem with the other Advanced Filter columns this formula will whitelist any manually added unique identifiers. Example Below.
PID Whitelist | | user | proc | PID Whitelisted?
---------------+------+--------+----------------+------------------
182 | | root | | < FORMULA_ABOVE >
11 | | test | | < FORMULA_ABOVE >
1776 | | | Jazzy Rabbit | < FORMULA_ABOVE >
I've attempted to use INDIRECT, ADDRESS, and INDEX in the place of 'Raw Data'!$A2, but since they reference an absolute destination the Advanced Filter always uses the very first unique identifier value; instead of the current row's value.
Thank you in advance for your time, and I really appreciate any assistance provided!
NOX
I would suggest simply rewriting the formula immediately after the VBA column insert. Example (with improved (?) formula:
With wksRawData
'~~> Move Process ID Column to Column 'A' for VLOOKUP
.Columns(WorksheetFunction.Match("pid", .Range("1:1"), 0)).Cut
.Columns(1).Insert Shift:=xlToLeft
'~~> Remove Additional Headers
.Cells.RemoveDuplicates Columns:=Array(1)
End With
sheets("Some_Sheet").cells(x, y).formula = "=NOT(COUNTIF($C:$C, 'raw data'!$A2))"
'aslo one additional variant of the #Jeeped idea, without formulas
With wksRawData
.Columns(Range("1:1").Find("pid").Column).Cut
.Columns(1).Insert
.Cells.RemoveDuplicates Columns:=Array(1)
End With
Sheets("Some_Sheet").Cells(x, y).Value = _
WorksheetFunction.CountIf(Columns(3), Sheets("raw data").Cells(2, 1).Value)

Getting quantity from a unique Item on Excel

I have a spredsheet on Excel and I must get the number of items from a row, somethink like this:
Item1 | Qty1 | Item2 | Qty2 | Item3 | Qty3 | ... | ItemZ | QtyZ |
Into a second sheet, I must populate with these data, but with just two Columns (Items and Qty).
There is any way to do that? I'd rather a solution that uses DGET or VLOOKUP (it seems faster), althought any feasable way to that would make me very happy :D
I've tried use a loop function, using Find + move to the left + copy and paste the qty. The problem was that this tooked like forever (plus my Excel crashed a few times).
So folks, could you help me?
If I understood correctly what you're after, then here's something basic to get you started (it's in VBA, not using Excel functions, but you'll hopefully get the idea):
Public Sub CopyDown()
Dim i As Long
Dim j As Long
j = 1
For i = 1 To Sheet1.Cells(1, Sheet1.Columns.Count).End(xlToLeft).Column Step 2
Sheet2.Cells(j, 1) = Sheet1.Cells(1, i)
Sheet2.Cells(j, 2) = Sheet1.Cells(1, i + 1)
j = j + 1
Next i
End Sub
It's not terribly robust as there's no error checking, but it should work for the case you've described. It assumes Items are arranged across row 1 in odd columns (1, 3, etc.) and Quantities are in even columns (2, 4, etc.) in "Sheet1". Then they are copied to ascending rows (from 1 onwards) in Columns A (for Items) and B (for Quantities) in "Sheet2". Let me know if you need more explanation.

Write a formula into a cell depending on another cell value

I was hoping to write a Macro that does a very repetitive task for me but entering VBA is harder than expected. I will learn how to program macros for excel when I have some time because it seem extremely useful, but I can't spend 5 to 12 hours this week.
Maybe someone here can help!
I have a few excel files that follow this pattern:
Column C - Column D
--------------------
text | (empty)
number | (empty)
number | (empty)
text | (empty)
number | (empty)
text | (empty)
text | (empty)
number | (empty)
text | (empty)
number | (empty)
Where text and number alternate randomly for a few thousand cells. I need column D to hold, when column C is a number, the difference with previous number, otherwise it must stay blank:
Column C - Column D
--------------------
text | (empty)
3 | (empty)
14 | (=C3-C2) : 11
text | (empty)
16 | (=C5-C3) : 2
text | (empty)
text | (empty)
21 | (=C8-C5) : 5
22 | (=C9-C8) : 1
So the algorithm is:
var previousNumberCell
var i = 1
for all (selected) cells/rows
if (Row(i).column(C) holds number) {
Row(i).column(D).value = "=C"+i+"-"C"+previousNumberCell
previousNumberCell = i;
}
i++
End
I don't care if for the first or last cell it doesn't work.
Thank you so much for the help, or if you can point me to where I can find the answer to this.
EDIT: this is a simplified version of the problem, there are 2 things I don't know how do well with excel macros: select a cell, and tell if cell is a number... for the record, number cells have been converted from text to number format.
Give this a shot:
Sub MyMacro()
Dim rng as Range
Dim cl as Range
Dim lastNum as Range
Set rng = Range(Selection.Address) 'Make sure that your active SELECTION is correct before running the macro'
If Not rng.Columns.Count = 1 Then
MsgBox "Please select only 1 column of data at a time.",vbCritical
Exit SUb
Else:
For each cl in rng
If IsNumeric(cl.Value) Then
If lastNum Is Nothing Then
cl.Offset(0,1).Formula = "=" & cl.Address
Else:
cl.Offset(0,1).Formula = "=" & cl.Address & "-" & lastNum.Address
End If
set lastNum = cl
End If
Next
End If
End Sub
Do you require VBA?
Insert a new Column before column C
Column C with your values becomes column D
You might need columnheaders..
In cell C2 put: =IF(E2=0;0;SUM(E$2:$E2)) this identifies rows with number
In cell E2 put: =IF(ISNUMBER(D2);1;0) this sets order for each row with a number to use next in vlookup
in cell F2 put: =IF(ISNUMBER(D2);ABS(D2-VLOOKUP(MAX($C$1:C1);$C$1:D1;2;0));"")
Autofill columns C, E and F.
In column F you get your results, except first, which is "#VALUE"
Hi you can do this with an if formula and a named formula . if (isnumber ,named formula,0)
named formula (=lookup formula)

VBA for checking rows and returning cells that match criteria to another sheet

I’m struggling trying to develop an automated solution for the following challenge (ie apart from a button press, no user intervention):
I have a master ‘sheet that contains Accrual figures in monthly columns and an associated Receipt Number for this in a column immediately to the right. The Receipt columns have the month in the form: ‘Feb’, ‘Jun’, ‘Dec’ at the top.
I need to return a variety of cell data to another summary ‘sheet in the workbook from each row that matches the following criteria:
Identify the Receipt column based on a match of the month with an entered month in cell ‘x’ in the summary ‘sheet, eg if I type ‘Mar’, find the column headed ‘Mar’ in the master ’sheet.
Ignoring blank rows (ie there are data in Column A), if the cell in the first row of the Receipt column is blank, identify the row, select 6 different cells and return the contents to 6 specific cells in the first blank row (from a given row number) in the summary ‘sheet – then move onto the next row in the column and continue this process until the end (or a row limit). NB: the 'blank' cell will have a fill colour (conditionally formatted).
If the cell contains data, move to the next row down and continue the process.
A VBA routine I can attach to an onscreen button to update the results each time would do the job nicely. Although I've picked up bits of really useful code here, eg for checking each row for given conditions, I can’t get my head around a working solution to include returning the relevant cells to the summary 'sheet...
Any and all feedback greatly appreciated.
(Sorry - can't post images / screenshots yet...)
Update
Thanks for the quick response, and apols I couldn't append/paste-in a sample of the spreadsheet - apparently I need 10 points... :)
Slightly Different summary sheet (Sheet1). The columns are: Month (Col A), then 4 cells (TBD) to be returned to cols B-E, then Receipt No (Col F). The data come from each identified row in the master sheet (Sheet4) and are placed in the next available blank row in Sheet1. I can mod any examples given to match the actual positions.
I have the criteria for selecting the row (this is from a loop I've got working to identify the row, but using a fixed column reference [26 - Col Z] for a specific month):
If Not Cells(TheRow, 3).Value = "" And Cells(TheRow, 26).Value = "" And Cells(TheRow, 6).Value < Cells(TheRow, 25).Value Then... (do the other bit I'm stuck on)
Hope this is a bit clearer. Appreciate your help.
Can't add a comment, or chat (!) so further update:
Thanks Tony
The month column in the summary is to confirm the month reported on, as this will change when the user overwrites the source cell for the month they wish to parse the master sheet with. The person this data goes to will get these 7 columns and nothing else.
The mechanism has to find the Receipt col (month), then identify that row entry is blank AND col 3 on that row is not blank AND that the Accrual value [Rng.Column - 1] is greater than the PO value (Col 4) on that row. Once these criteria are established, the Receipt, Accrual, PO and 3 other data are selected, copied and pasted into the summary sheet on the next available blank row.
The master sheet has Accrual and Receipt for each month, so there is only one column to identify.
Hope this helps...
I am struggling to visualise you worksheets. I see the master sheet as something like:
... |Accrual| Jan|Accrual| Feb|Accrual| Mar|Accrual| Apr| ...
... | 1.23|A12 | | | 23.67|A14 | | |
... | | | 56.78|C34 | 178.12|C56 | | |
... | 2.34|B678 | | | | | 123.82|B821 |
... | | | | | 96.52|D56 | | |
Somewhere within the Summary sheet there is a list of months of interest.
I need a variable:
Dim MonOfInt As String
and a loop within which a month of interest will be loaded from the worksheet into this variable:
With Sheets("Summary")
MonOfInt = .Cells(R, C).Value
End With
The following is a possible skeleton for the code to move values for the month of interest:
Dim ColCrnt As Long
Dim Rng As Range
Dim RowCrnt As Long
Dim RowLast As Long
:
With Sheets("Master")
' Look for month of interest in row 1
Set Rng = .Rows(1).Find(MonOfInt)
End With
If Rng Is Nothing Then
' No columns for this month in Master
Else
' Accrual and Receipt columns for this month found
ColCrnt = Rng.Column - 1 ' Accrual column for month
' Find last used row in Accrual column
RowLast = .Cells(Rows.Count, ColCrnt).End(xlUp).Row
For RowCrnt = 2 To RowLast
' Code to extract values from Master and move to Summary here
Next
End If
End With
If you can add information to your question to confirm my visualisation and to give more information about (1) how to detect is a row is blank (other than testing the whole row which is an option) and (2) the source and destination of the six cells then I will try to pad out this answer.

Excel formula to calculate diff b/w 1st value and last value in a column (Excel)?

I have the following columns : (Using Excel Formula)
A B
------------------------
1 | Date | Value |
------------------------
2 | 8/20/2011 | 92.8 |
3 | 8/21/2011 | 92.4 |
4 | 8/22/2011 | 91.8 |
5 | 8/23/2011 | 90.7 |
6 | 8/24/2011 | 91.1 |
7 | 8/25/2011 | |
8 | 8/26/2011 | |
9 | 8/27/2011 | |
------------------------
I want to calculate the difference between 1st Value (B2) and last Value (last populated row in column B)
Edited :
Using formula : =B2-B6 is not what's required. (I want diff in Cell C2)
I want when the user enters the value in B7 it automatically shows the difference between B2 and B7, when he populates B8 then it shows the diff between B2 and B8 and so on..
I don't want some direct method to do this in Excel and not by iterating all values to check the last value. Please suggest the best method to this in Excel.
This is more of a logic problem !
A Pure Excel solution is welcome, I can do that manually.
Edited:
Using Excel formula !
I hope I explained my problem properly..
If the headers are in A1 and B1, and your table is A2:B9 for instance, use the formula:
=$B$2-OFFSET($B$2,COUNTA($B$2:$B$9)-1,0,1,1)
To find the last number in a column you can use VLOOKUP on a single column where 1E+99 is the lookup_value and the optional range_lookup is left to the default. The data does not have to be sorted.
     
The formula in C2 is =VLOOKUP(1E+99,$B$2:$B$99,1)-B2. Similarly, you can locate the last non-blank alphabetic value with =VLOOKUP("zzzzz",$E$2:$E$99,1) (example in F2).
To find the last non-blank value where there are mixed alphabetic and numeric values, use INDEX and apply MAX to MATCH, e.g. =INDEX(E2:E99,MAX(IFERROR(MATCH(1E+99,E2:E99),0),IFERROR(MATCH("zzzz",E2:E99),0))). Any of these formulas could easily be adjusted to return a value from another column corresponding to the last value in the lookup column.
I understand you are not looking for a pure Excel solution but you can use it a different way >> you can add an Excel formula with vb/vba.
Something like =B7-$B$2 is the formula needed.
Thus, you can do something like:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B:B")) Is Nothing Then
Target.Offset(0, 1).Formula = "=" & Target.Address & "-$B$2"
End If
End Sub
Another less elegant way to do this with Excel formulas alone, would be to use a helper column, which is a telescoping series.
In column C2, enter 0.
In columns C3, use the formula
=IF(B3="",0,B2-B3)
Copy down the length of the table.
At the bottom, sum everything in the helper column.