How do I sum all products of the columns in table with another table?
To make it more clear, look at the image attached. I want the column Cost of table TableA to be equal to
=sum([A]*Lookup([[A];[#Headers]]; Parameters[What]; Parameters[Cost]); ....)
And so on for every column of TableA.
I am however pretty much reluctant of doing it manually and trying to come up with formula to make it automatically, so if I add another column I don't have to modify the formula in column Cost
EDIT
What I have come up so far is something like this:
=sum(
[A]*Lookup([[A];[#Headers]]; Parameters[What]; Parameters[Cost]);
[B]*Lookup([[B];[#Headers]]; Parameters[What]; Parameters[Cost]);
[C]*Lookup([[C];[#Headers]]; Parameters[What]; Parameters[Cost])
)
I want to have a formula that will cover new column if I add one. So, let's say I've added a column named NEW, so the formula should automatically pick it up and effectively work like this:
=sum(
[A]*Lookup([[A];[#Headers]]; Parameters[What]; Parameters[Cost]);
[B]*Lookup([[B];[#Headers]]; Parameters[What]; Parameters[Cost]);
[C]*Lookup([[C];[#Headers]]; Parameters[What]; Parameters[Cost]);
[NEW]*Lookup([[NEW];[#Headers]]; Parameters[What]; Parameters[Cost])
)
The Parameters table will of course include a row with value NEW
If you want it to be (50*4+5*100+1*150) for item 1 then you can apply this formulae in cell B3 assuming that the word "cost is in cell B2
INDEX($B$7:$B$9,MATCH($C$2,$A$7:$A$9,0))*$C3+INDEX($B$7:$B$9,MATCH($D$2,$A$7:$A$9,0))*$D3+INDEX($B$7:$B$9,MATCH($E$2,$A$7:$A$9,0))*$E3
If the parameters in tables TableA and Parameters are in the same order, then you can use the following in column Cost:
=SUMPRODUCT(TRANSPOSE(TableA[#[A]:[C]])*Parameters[Cost])
Entered as array formula (i.e. type the formula then instead of pressing Enter, press Ctrl+Shift+Enter)
EDIT:
If the columns in TableA are not ordered, but those in Parameters are, then you can use the following (I'm not 100% sure it always works, but I've been testing a bit and it seems to work fine):
=SUMPRODUCT(TableA[#[A]:[C]]*LOOKUP(TableA[[#Headers],[A]:[C]],Parameters[What],Parameters[Cost]))
Doesn't need to be entered as array formula.
This answer is amended to take account of your statement that the columns of TableA do not necessarily match entries in the rows of the Parameters table.
If you don't mind using macro code, you could define your own function to do the job:
Option Explicit
Public Function TotalCost(rItemCodes As Range, rCostTable As Range, rItemCounts As Range) As Double
'// this function returns the total cost of an item using
'// a lookup into a table of costs from a code, multiplying
'// the item count by the corresponding cost
Dim rItemCount As Range
Dim rCost As Range
Dim rCode As Range
Dim rMatchingCode As Range
'// Define the code list as the first column of the cost table
Dim rCodeList As Range
Set rCodeList = rCostTable.Columns(1)
TotalCost = 0
Dim ix As Integer
'// Loop through every item code
For ix = 1 To rItemCodes.Columns.Count
Set rItemCount = rItemCounts.Cells(1, ix)
Set rCode = rItemCodes.Cells(1, ix)
Set rMatchingCode = Nothing
On Error Resume Next
'// Find the item that matches in the cost list
Set rMatchingCode = rCodeList.Find(rCode.Value, LookAt:=xlWhole)
'// Check it was found, and calculate the additional cost
If Not rMatchingCode Is Nothing Then
Set rCost = rMatchingCode.Cells(1, 2)
TotalCost = TotalCost + rItemCount.Value * rCost.Value
End If
On Error GoTo 0
Next ix
End Function
If you named the table of costs in the parameters table, say, CostTable (range A8:B10 in your example), and the heading row of table A, say, CodeList (range C1:E1 in your example) then the formula in B2 would be =TotalCost(CodeList,CostTable,C2:E2).
Related
I've got a few large (500-1000) datasets in the following format using only the first two rows.
id
value
a-b
number
a-c
number
a-d
number
...
number
b-c
number
b-d
number
and so on
They compare two values and save their difference while skipping previously done comparisons. I want to put them in a table like this:
id
a
b
c
d
e
a
/
number
number
number
number
b
number
/
number
number
number
c
number
number
/
number
number
d
number
number
number
/
number
e
number
number
number
number
/
The lower left half of this table is easily prepared with offset, but how do I feed the values into the upper right half?
Is there a way to mostly automate doing this?
If i understand what you are trying to do, I would suggest to do this in 2 steps:
Set up formulas in the results table to "read" data
Have a macro to "save" data
First fill your results table with this formula (example for cell B2) - keep your offset formula in bottm left half
=IF(B$2=$A3, "\", VLOOKUP(IF(B$2>$A3,$A3&"-"&B$2, B$2&"-"&$A3), $H$3:$I$35, 2, FALSE))
This will give you \ if the row and column id or the same or vlookup on row_id-column_id/column_id-row_id if they are different ensuring they are ordered "low-high" always to give you your mirror across the diagonal. Some might argue the duplication of vlookup is inefficient but more inefficient than an offset and figuring our how to paste one formula into one diagonal and a different one in the other? Who really knows and it is simple and it works IMHO
Next put the data for the current "pass" into the observations table and your values will appear in the table via the VLOOKUP
Finally you need a little macro to replace the formula with the value and run it after every set of results is acquired, so that you don't lose this data when you put a subsequent set of new values in your results table
Option Explicit
Sub replace_formula_with_value()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("Sheet1")
Dim r_in As Range
' this is where my input data start
Set r_in = Range(ws.Range("B3"), ws.Range("B3").End(xlDown).End(xlToRight))
Dim r As Range
For Each r In r_in
If Not IsError(r.Value) Then
If r.Value <> "\" Then
'overwrite formula with value by setting value to the evaluation of the formula in the cell
r.Value = r.Value
End If
End If
Next r
End Sub
If you dont want to have a macro then you need to keep ALL data in your observations table and just build it up over time.
strong textSample table
I want to get the total of the columns "Value" & "Quantity" which is in a dynamic table (in which number of rows will be different each time) at the end of the last row.
If you select a cell in your table the Table Design menu will appear on the Ribbon. In it is a checkbox for Total Row. Check it.
This will add a total row to your table. In my test it contained a total only for the last column on the left. However, you can use that same formula, modified for the appropriate column name, in other columns as well. The row will automatically stay at the bottom of the table when you add rows.
To add rows at the bottom manually, place the cursor in the last cell on the right (not in the totals row but above it) and hit Tab. You can also select any cell in the table and click Insert Table Row either above or below the clicked cell.
All of the above works for a true table. You exhibit doesn't look like a true table. If it isn't, please consider one of two options. Either replace the table you already have with what is called a "Table" in Excel and enjoy the advantages that offers, or modify your question to clarify that your question applies to a list of data which are not in a table.
you could try this:
dim TotalValue as Long
dim i, value as integer
value = 1 'use the column number of Value
i = 1
Do until isempty(workbooks("yourworkbook.xlsm").worksheets(1).cells(i,Value)) = True
Totalvalue = TotalValue + workbooks(yourworkbook.xlsm).worksheets(1).cells(i,Value)
i = i + 1
Loop
i = i + 1
(workbooks(yourworkbook.xlsm).worksheets(1).cells(i,Value).value = TotalValue
Then do the same with Quantity column
In Excel formula become often very long and, consequently, unreadable. For example my formula looks like this:
=PERCENTRANK([vol];[#vol])-PERCENTRANK([pos];[#pos])
I would like to write just
=SCORE("vol", "pos")
How can I create a user defined function from the original worksheet formula?
You can do this relatively easily using VBA's WorksheetFunction. The most difficult part is actually getting the column of the [#Range] so you don't have to enter it - but that's not too complicated either.
In the VBE, create a new standard module and paste the following UDF:
Public Function SCORE(vol As Range, pos As Range) As Double
' We need to get the entire column of the table for each argument
Dim tbl As ListObject, volRng As Range, posRng As Range
Set tbl = vol.ListObject
Rem -> Table columns are relative to the table - not the worksheet
Set volRng = tbl.Range.Columns(vol.Column - tbl.Range.Cells(1).Column + 1)
Set posRng = tbl.Range.Columns(pos.Column - tbl.Range.Cells(1).Column + 1)
With Application.WorksheetFunction
SCORE = .PercentRank(volRng, vol) - .PercentRank(posRng, pos)
End With
End Function
You have to keep in mind that table column numbers are not always the same as the worksheet's (if your table doesn't start in column A). So you essentially have to offset by the number of worksheet columns by subtracting the column from your arguments vol and pos from the column number of the first cell of the entire table tbl.range.cells(1). Then you add one because columns numbers in VBA is "base 1".
You will still have to send the range of the individual cell - so using string arguments are not going to do well here. With all that being said, your new worksheet formula would look like:
=SCORE([#vol],[#pos])
or
=SCORE([#vol];[#pos])
depending on if you separate arguments using , or ;.
I've run into a bit of a road block. I get a .PDF output from an accounting program and copy/paste the data into excel, then convert text to columns. I am trying to match the GL code with the totals for that specific account. Columns A, B, and C show the state of my data prior to sorting it, and the lines under Intended Output show how I would like the data to output.
I am trying to automate this process, so I can paste data into columns A, B, & C in the raw format and have it automatically spit out the required numbers in the format of the Intended Output. The GL codes remain the same, but the numbers and the number of rows will change. I've color coded them for ease of review.
Thank you very much in advance!
Using a combination of the following formulas you can create a list of filtered results. It works on the principal that you Data1 text that you want to pull is the only text with a "-" in it, and that the totals you are pulling from Data2 and Data3 are the only numbers in the column. Any change to that pattern will most likely break the system. Note the formulas will not copy formatting.
IFERROR
INDEX
AGGREGATE
ROW
ISNUMBER
FIND
Lets assume the output will be place in a small table with E2 being the upper left data location.
In E2 use the following formula and copy down as needed:
=IFERROR(INDEX(A:A,AGGREGATE(15,6,ROW($A$1:$A$30)/ISNUMBER(FIND("-",$A$1:$A$30)),ROW(A1))),"")
In F2 use the following formula and copy to the right 1 column and down as needed:
=IFERROR(INDEX(B:B,AGGREGATE(15,6,ROW($A$1:$A$30)/ISNUMBER(B$1:B$30),ROW(A1))),"")
AGGREGATE performs array like calculations. As such, do not use full column references such as A:A in it as it can lead to excess calculations. Be sure to limit it to the range you are looking at.
Try this procedure:
Public Sub bruce_wayne()
'Assumptions
'1. Data spreadsheet will ALWAYS have the structure shown in the question
'2. The key word "Total" (or whatever else it might be) is otherwise NOT found
' anywhere else in the 1st data column
'3. output is written to the same sheet as the data
'4. As written, invoked when data sheet is the active sheet
'5. set the 1st 3 constants to the appropriate values
Const sData2ReadTopLeft = "A1" 'Top left cell of data to process
Const sData2WriteTopLeft = "J2" 'Top left cell of where to write output
Const sSearchText = "Total" 'Keyword for summary data
'*******************
Const sReplaceText = "Wakanda"
Dim r2Search As Range
Dim sAccountCode As String
Dim rSearchText As Range
Dim iRowsProcessed As Integer
Set r2Search = Range(sData2ReadTopLeft).EntireColumn
sAccountCode = Range(sData2ReadTopLeft).Offset(1, 0).Value
iRowsProcessed = 0
Do While Application.WorksheetFunction.CountIf(r2Search, sSearchText) > 0
Set rSearchText = r2Search.Find(sSearchText)
Range(sData2WriteTopLeft).Offset(iRowsProcessed, 0) = sAccountCode
Range(sData2WriteTopLeft).Offset(iRowsProcessed, 1) = rSearchText.Offset(0, 1).Value
Range(sData2WriteTopLeft).Offset(iRowsProcessed, 2) = rSearchText.Offset(0, 2).Value ' add this if there are more summary columns to return
'last two lines could be collapsed into a single line; at the expense of readability..
rSearchText.Value = sReplaceText 'so that next search will find the next instance of the trigger text
iRowsProcessed = iRowsProcessed + 1
sAccountCode = rSearchText.Offset(1, 0).Value
Loop
r2Search.Replace what:=sReplaceText, Replacement:=sSearchText
End Sub
I have 2 tabs of data with a unique identifier. The identifier is not in any particular order. I need my vlookup / index / match to show me all the identifiers that are not present in tab 2.
Reason: I am working where the systems they used failed a data transfer. I have to see what data there was compared to what data is currently on the system. Any data that is missing, i will need to add to the new system.
Example;
Tab1 Column A:
123456,
654321,
789456,
456789.
Tab2 Column B:
654321,
123456,
456789.
In Tab 3, I want excel to tell me that 789456 is not present in Tab 2.
As you can see in the above example, the unique identifier could be in any order, therefore i cannot put both columns side by side and ask to do a match between the 2 - i need it to look through the whole column.
All the tutorials i have seen assume that column A matches in order of column B
I have 70,000 rows to go through.
Any help would be appreciated.
Thanks in advance.
To do it with a formula you will want a helper column in the First tab.
In an empty column, I used column B, put the following in the second row:
=IF(ISERROR(VLOOKUP(A2,Sheet2!B:B,1,FALSE)),MAX($B$1:B1)+1,"")
This will create a column of numbers that increment on the ones not found in sheet two.
At this point you can simply filter on the new column for anything that in non blank and get your list.
If you want to do it with a formula in the Third tab then use this formula that refers to the helper column on the first tab:
=IFERROR(INDEX(Sheet1!A:A,MATCH(ROW(1:1),Sheet1!B:B,0)),"")
Then copy/drag down sufficient to get blanks.
With 70,000 items I would avoid array formulas as it will slow the calculation down and may even crash excel.
You could try using something like this:
=IFERROR(VLOOKUP(<value cell>, 'Tab2'!B:B, 1, FALSE), FALSE)<>FALSE
Copy all the values from tab 1 column A into tab 3 column A. In tab 3 column B, paste the above formula in every row where there is a value in column A, using referencing the cell from column A and the same row as the value cell. The formula will attempt to look up the value from tab 1 in tab 2. If it is missing, it will generate an error which is caught by the IFERROR function, which will return FALSE instead of letting the error escape. Finally, that FALSE is negated to return TRUE if the value is present in tab 2, and FALSE if the value is missing in tab 2.
From this point you can use a column filter in tab 3 to only see those rows with a TRUE value, that will only show you values that are present in both tab 1 and tab 2.
Soulution for this is COUNTIF() the formula would be:
=COUNTIF(Sheet1!A:A,Sheet2!A1)
After applying that for all rows, just filter those that have value 0.
This macro will produce a compact list in Sheet3:
Sub WhatsMissing()
Dim s1 As Worksheet, s2 As Worksheet, s3 As Worksheet
Dim r1 As Range, N As Long, K As Long, i As Long
Dim v As Variant
Set s1 = Sheets("Sheet1")
Set s2 = Sheets("Sheet2")
Set s3 = Sheets("Sheet3")
Set r2 = s2.Range("B:B")
K = 1
N = s1.Cells(Rows.Count, "A").End(xlUp).Row
With Application.WorksheetFunction
For i = 1 To N
v = s1.Cells(i, "A").Value
If .CountIf(r2, v) = 0 Then
s3.Cells(K, "A").Value = v
K = K + 1
End If
Next i
End With
End Sub