Related
I'm an intern and I've been struggling to find a solution since this monday...
I'm new to VBA and I don't clearly see how I can sum cells from a column based on some conditions..I tried multiple code but as soon as my codes didnt work I deleted them.
So what I'm trying to do is the following;
I've got a worksheet called worksheets("Amounts") in which I've got a data base.
What I've been struggling to do since this Monday :
Sum the amounts value in column Q ( "AMOUNT") Only if rows of COL A, col B, col C, col , col E, col F have equivalent cells value.
Then, I'd like to sum in col Q the amounts based on the previous condition and put the total in one single row in the place of the rows that contain common values.
Right after I'd like to delete each rows that were matching to one another to display the agregated amount with the common values. Like the following example;
My data base;
COL A
COL B
COL C
COL E
COL F
COL Q
CODE
STATUE
ATTRIBUTE
Country
Capital
AMOUNT
A1
OK
Z1
ENGLAND
LONDON
400
C1
NOK
R2
SPAIN
MADRID
50
A1
OK
Z1
ENGLAND
LONDON
300
D1
PENDING
X
CANADA
OTTAWA
10
the Output expected;
COL A
COL B
COL C
COL E
COL F
COL Q
CODE
STATUE
ATTRIBUTE
Country
Capital
AMOUNT
A1
OK
Z1
ENGLAND
LONDON
700
C1
NOK
R2
SPAIN
MADRID
50
D1
PENDING
X
CANADA
OTTAWA
10
==> So here we have only 2 rows with common value on col A, B, C, E and F. I'd like to sum the amounts of these two rows and delete these two rows to make a single one with these common values like the up-above example.
Obviously for the other rows that dont match with other rows I'd like to let them as they were.
the database in worksheets("Amount") can vary and can get more or less rows, so I will need to automatize this process.
Here is my last saved code:
Option Explicit
Sub agreg()
Dim i As Long
Dim ran1 As Range
ran1 = ThisWorkbook.Worksheets("Values").Range("A" & Worksheets("Values").Rows.Count).End(xlUp).row + 1
For Each i In ran1
If Cells(i, 1) = Range("A1", Range("A1").End(xlDown)).Value Then
cells(i,4) + range("D1",range("D1").End(xlDown)).Value
End If
Next i
End Sub ```
Please, test the next code:
Sub SumUnique5Cols()
Dim sh As Worksheet, lastRow As Long, arr, arrQ, rngDel As Range, i As Long, dict As Object
Set sh = Worksheets("Amounts")
lastRow = sh.Range("A" & sh.rows.count).End(xlUp).row 'last row based on A:A column
arr = sh.Range("A2:Q" & lastRow).Value2 'place the range in an array to make the code faster
Set dict = CreateObject("Scripting.Dictionary") 'set a dictionary to keep the unique keys combination value
For i = 1 To UBound(arr) 'iterate between the array elements
If Not dict.Exists(arr(i, 1) & arr(i, 2) & arr(i, 3) & arr(i, 5) & arr(i, 6)) Then 'if the combination key does not exist:
dict.Add arr(i, 1) & arr(i, 2) & arr(i, 3) & arr(i, 5) & arr(i, 6), arr(i, 17) 'it is created (and take the value of Q:Q cell)
Else 'if the key aleready exists, it adds the value in the key item:
dict(arr(i, 1) & arr(i, 2) & arr(i, 3) & arr(i, 5) & arr(i, 6)) = dict(arr(i, 1) & arr(i, 2) & arr(i, 3) & arr(i, 5) & arr(i, 6)) + arr(i, 17)
'range of the rows to be deleted is filled in this way:
If rngDel Is Nothing Then
Set rngDel = sh.Range("A" & i + 1) 'if the range does not exist, it is set (i + 1, because of iteration starting from the second row)
Else
Set rngDel = Union(rngDel, sh.Range("A" & i + 1)) 'if it exists, a union between the previus range and the new one is created
End If
End If
Next i
If Not rngDel Is Nothing Then rngDel.EntireRow.Delete 'if there are rows to be deleted, they are deleted
lastRow = sh.Range("A" & sh.rows.count).End(xlUp).row 'recalculate the last row (after rows deletion)
arr = sh.Range("A2:Q" & lastRow).Value2 'place the remained range in an array
ReDim arrQ(1 To UBound(arr), 1 To 1) 'ReDim the final array (to keep the summ) according to the remained rows
For i = 1 To UBound(arr)
arrQ(i, 1) = dict(arr(i, 1) & arr(i, 2) & arr(i, 3) & arr(i, 5) & arr(i, 6)) 'put in the array the corresponind dictionary key value
Next i
sh.Range("Q2").Resize(UBound(arrQ), 1).value = arrQ 'drop the array content at once
End Sub
In Power Query it would be simple:
Group by those columns
Aggregate by the Sum of Amount
Power Query is available in Windows Excel 2010+ and Office 365
To use Power Query
Select some cell in your Data Table
Data => Get&Transform => from Table/Range
When the PQ Editor opens: Home => Advanced Editor
Make note of the Table Name in Line 2
Paste the M Code below in place of what you see
Change the Table name in line 2 back to what was generated originally.
Read the comments and explore the Applied Steps to understand the algorithm
M Code
let
//Change table name in next line to actual table name in the workbook
Source = Excel.CurrentWorkbook(){[Name="Table10"]}[Content],
//set data types
//note the columns named ColumnN which will be different with your real data
#"Changed Type" = Table.TransformColumnTypes(Source,{
{"CODE", type text},
{"STATUE", type text},
{"ATTRIBUTE", type text},
{"Column1", type any},
{"Country", type text},
{"Capital", type text},
{"Column2", type any}, {"Column3", type any}, {"Column4", type any}, {"Column5", type any}, {"Column6", type any}, {"Column7", type any}, {"Column8", type any}, {"Column9", type any}, {"Column10", type any}, {"Column11", type any},
{"Amount", Int64.Type}}),
//group by columns 1,2,3,5,6
//return Sum of the Amount Column
#"Grouped Rows" = Table.Group(#"Changed Type", {"CODE", "STATUE", "ATTRIBUTE", "Country", "Capital"},
{{"Amount", each List.Sum([Amount]), type nullable number}})
in
#"Grouped Rows"
Data
Results
If you want to do it with VBA I would use ADODB to group the rows
Public Sub SumGroup()
Dim connection As Object
Set connection = CreateObject("ADODB.Connection")
connection.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & ThisWorkbook.FullName & ";" & _
"Extended Properties=""Excel 8.0;HDR=Yes;"";"
Dim recordset As Object
Dim sSQL As String
sSQL = "SELECT Code, Statue, Attribute, Country, Capital, Sum(Amount) as SumAmount FROM [Database$] GROUP BY Code, Statue, Attribute, Country, Capital"
Set recordset = connection.Execute(sSQL)
Worksheets("Result").Range("A1").CopyFromRecordset recordset
recordset.Close
connection.Close
End Sub
The data should be in a sheet with the name Database. The result will be written to a sheet with the name Result. Both sheets should exist in the workbook before running the code.
A Pivottable would give you what you want without any coding nor in VBA or M
I have an Excel table source (extract is below):
Models are changing (adding new names, removing), so does change Week number in Columns (from Now till end of the year)
I need to restructure it so that it could look like this:
So that it could be normally used for further querying.
In each row there must be model name, for each model there should be Week number and corresponding matching Quantity taken from the table itself (on the intersection of Model X Week from original table).
I was smashing my head against the wall on how it can be realized in VBA. Couldn't restructure it with simple code.
try this code, just change the name of your sheets I used 2, the first is the source and the second is the destination, I hope help you
good Luck
Sub RestructureTable()
Const SourceSheetName = "Source", DestinationSheetName = "Restructure" 'Your sheet names
Dim nRowCounter As Double, nColumnSourceCounter As Double, nRowSourceCounter As Double
'--------
'-------Set the headers in destination sheet
Sheets(DestinationSheetName).Range("A1") = "Models" 'Replace "Models" as you need
Sheets(DestinationSheetName).Range("B1") = "Week" 'Replace "Week" as you need
Sheets(DestinationSheetName).Range("C1") = "Qty" 'Replace "Qty" as you need
'--------
'----------------------------------------------------
Sheets(SourceSheetName).Select 'Select the source sheet
Range("A2").Select ' select the first cell with data
nRowCounter = 2 ' Start in 2 cuase headers
'---------------------------------------------------
nRowSourceCounter = ThisWorkbook.Application.WorksheetFunction.CountA(Range("A:A")) 'count rows
nColumnSourceCounter = ThisWorkbook.Application.WorksheetFunction.CountA(Range("1:1")) 'count columns
For r = 2 To nRowSourceCounter
For c = 2 To nColumnSourceCounter
'Model
Sheets(DestinationSheetName).Range("A" & nRowCounter) = Sheets(SourceSheetName).Cells(r, 1)
'Header:Week
Sheets(DestinationSheetName).Range("B" & nRowCounter) = Sheets(SourceSheetName).Cells(1, c)
'Qty
Sheets(DestinationSheetName).Range("C" & nRowCounter) = Sheets(SourceSheetName).Cells(r, c)
nRowCounter = nRowCounter + 1
Next c
Next r
End Sub
You could do this with VBA but you could also do it with only a few steps using Power Query.
VBA Method
Here's code to do it with VBA, it assumes the data to restructure is in a sheet named Data and starts at A1.
In this code the restructured data is put on a new sheet but you could change that to put it on an existing sheet.
Option Explicit
Sub RestructureData()
Dim ws As Worksheet
Dim arrDataIn As Variant
Dim arrDataOut() As Variant
Dim cnt As Long
Dim idxCol As Long
Dim idxRow As Long
arrDataIn = Sheets("Data").Range("A1").CurrentRegion.Value
ReDim arrDataOut(1 To (UBound(arrDataIn, 1) - 1) * (UBound(arrDataIn, 2) - 1), 1 To 3)
For idxRow = LBound(arrDataIn, 1) + 1 To UBound(arrDataIn, 1)
For idxCol = LBound(arrDataIn, 2) + 1 To UBound(arrDataIn, 2)
cnt = cnt + 1
arrDataOut(cnt, 1) = arrDataIn(idxRow, 1)
arrDataOut(cnt, 2) = arrDataIn(1, idxCol)
arrDataOut(cnt, 3) = arrDataIn(idxRow, idxCol)
Next idxCol
Next idxRow
Set ws = Sheets.Add ' can be set to existing worksheet
ws.Range("A1:C1").Value = Array("Model", "Week", "Quantity")
ws.Range("A2").Resize(cnt, 3).Value = arrDataOut
End Sub
Power Query Method
Go to the sheet with the data, then go to the Data>Get & Transform Data tab.
Select From Table/Range, make sure all the data is selected and Does your data have headers? is ticked.
In Power Query select the Model column, right click and select Unpivot Other Columns.
Rename the Attribute column 'Week' and the value column 'Quantity' by double click each column header.
Click Close & Load to return the data to Excel.
This is the M Code those steps produce.
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Models", type text}, {"2021-03-29_(W13)", Int64.Type}, {"2021-04-05_(W14)", Int64.Type}, {"2021-04-12_(W15)", Int64.Type}, {"2021-04-19_(W16)", Int64.Type}, {"2021-04-26_(W17)", Int64.Type}}),
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Changed Type", {"Models"}, "Attribute", "Value"),
#"Renamed Columns" = Table.RenameColumns(#"Unpivoted Other Columns",{{"Attribute", "Week"}, {"Value", "Quantity"}})
in
#"Renamed Columns"
I'm over my head here, but hoping someone can help.
Would like C3 to display all the strings from C15 to C60 for rows where column E matches B3, and remove duplicates / only display uniques.
Also, I would like D3 to display the strings from D15 to D60 as the prior question, however the cells potentially contain comma separated values - is there a way I can do as above but treat the comma separated values as separate strings?
screenshot and spreadsheet to download.
Thanks in advance for anyone taking a look.
Joe
excel have a function to remove duplicate data, did you try it?
just copy your origin data then remove duplicate.
Retrieve Possibly Separated Unique Data
Usage:
Copy the code into a standard module and in Excel use the following formulas:
in Cell C3 =CritJoe(C$15:C$60,$E$15:$E$60,$B3)
in Cell D3 =CritJoe(D$15:D$60,$E$15:$E$60,$B3,",")
The Code
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: Retrieves unique ((comma) separated) (ResultSeparator) data
' determined by a criteria (Criteria) in a specified column
' (CriteriaRange) from another specified column (SourceRange)
' possibly containing (comma) separated (StringSeparator) strings.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function CritJoe(SourceRange As Range, CriteriaRange As Range, _
Criteria As String, Optional StringSeparator As String = "", _
Optional ResultSeparator As String = ", ") As String
Dim vntS ' Source Array (1-based, 2-dimensional)
Dim vntC ' Criteria Array (1-based, 2-dimensional)
Dim vntSS ' Source String Array (0-based, 1-dimensional)
Dim vntR ' Resulting Array (0-based, 1-dimensional)
Dim i As Long ' Source & Criteria Array Elements Counter
Dim j As Long ' Resulting Array Elements Counter
Dim k As Long ' Source String Array Elements Counter
Dim UB As Long ' Current Resulting Array's Upper Bound
Dim strS As String ' Current Source String
Dim strR As String ' Resulting String
' Check if SourceRange and CriteriaRange have the same number of rows and
' have the same first row number.
If SourceRange.Rows.Count <> CriteriaRange.Rows.Count Or _
SourceRange.Rows(1).Row <> CriteriaRange.Rows(1).Row Then GoTo RowsError
' Note: The relevant data has to be in the first column of each range
' if (accidentally) more columns have been selected.
' Copy first column of the Ranges to Arrays.
vntS = SourceRange.Cells(1).Resize(SourceRange.Rows.Count)
vntC = CriteriaRange.Cells(1).Resize(CriteriaRange.Rows.Count)
' Write relevant data to Resulting Array.
For i = 1 To UBound(vntS)
' To avoid "Case/Space issues", "parts" of the following can be used:
' If Upper(Trim(vntC(i, 1))) = Upper(Trim(Criteria)) Then
' ... instead of the following line:
If vntC(i, 1) = Criteria Then
strS = vntS(i, 1)
If StringSeparator <> "" Then
' Write Resulting String to Resulting Array using
' StringSeparator.
GoSub SplitString
Else
' Write Resulting String to Resulting Array without
' using StringSeparator.
GoSub StringToArray
End If
End If
Next
' Write relevant data from Resulting Array to Resulting String.
If IsArray(vntR) Then
strR = vntR(0)
If UBound(vntR) > 0 Then
For j = 1 To UBound(vntR)
strR = strR & ResultSeparator & vntR(j)
Next
End If
End If
CritJoe = strR
Exit Function
' Write Resulting String to Resulting Array using StringSeparator.
SplitString:
vntSS = Split(strS, StringSeparator)
For k = 0 To UBound(vntSS)
strS = Trim(vntSS(k))
GoSub StringToArray
Next
Return
' Write Resulting String to Resulting Array.
StringToArray:
If IsArray(vntR) Then
' Handle all except the first element in Resulting Array.
UB = UBound(vntR)
For j = 0 To UB
If vntR(j) = strS Then Exit For
Next
If j = UB + 1 Then
ReDim Preserve vntR(j): vntR(j) = strS
End If
Else
' Handle only first element in Resulting Array.
ReDim vntR(0): vntR(0) = strS
End If
Return
RowsError:
CritJoe = "Rows Error!"
End Function
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
As I wrote, it's relatively simple with Power Query, available in Excel 2010+. It takes longer to type out the steps then to create it :-).
All can be done from the UI
Remove the Scene and Video Columns
Split the Cast column by the comma delimiter
Select the Location and Cast columns and Transform/Trim to eliminate unwanted spaces
Select all three columns and Remove Rows/Duplicates
Group By Recording Block Operation:= All Rows
Name the Column Grouped
Add Custom Column for the Locations: Formula =
List.Distinct(Table.Column([Grouped],"Location"))
note the new column name in the dialog box
Add another Custom Column for the Cast: Formula =
List.Distinct(Table.Column([Grouped],"Cast"))
Select the double-headed arrow at the top of the new columns
Extract Values from the drop down and select a delimiter -- I used the Custom --> comma space
Close and Load to wherever you want on the worksheet.
Of note, this workbook can be saved as an xlsx file.
For interest, here is the M-Code generated.
M Code
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Video", Int64.Type}, {"Scene", Int64.Type}, {"Location", type text}, {"Cast", type text}, {"Recording Block", type text}}),
#"Removed Columns" = Table.RemoveColumns(#"Changed Type",{"Video", "Scene"}),
#"Split Column by Delimiter" = Table.ExpandListColumn(Table.TransformColumns(#"Removed Columns", {{"Cast", Splitter.SplitTextByDelimiter(",", QuoteStyle.Csv), let itemType = (type nullable text) meta [Serialized.Text = true] in type {itemType}}}), "Cast"),
#"Changed Type1" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Cast", type text}}),
#"Trimmed Text" = Table.TransformColumns(#"Changed Type1",{{"Cast", Text.Trim, type text}, {"Location", Text.Trim, type text}}),
#"Removed Duplicates" = Table.Distinct(#"Trimmed Text"),
#"Grouped Rows" = Table.Group(#"Removed Duplicates", {"Recording Block"}, {{"Grouped", each _, type table [Location=text, Cast=text, Recording Block=text]}}),
#"Added Custom" = Table.AddColumn(#"Grouped Rows", "Locations", each List.Distinct(Table.Column([Grouped],"Location"))),
#"Added Custom1" = Table.AddColumn(#"Added Custom", "Cast", each List.Distinct(Table.Column([Grouped],"Cast"))),
#"Extracted Values" = Table.TransformColumns(#"Added Custom1", {"Cast", each Text.Combine(List.Transform(_, Text.From), ", "), type text}),
#"Extracted Values1" = Table.TransformColumns(#"Extracted Values", {"Locations", each Text.Combine(List.Transform(_, Text.From), ", "), type text})
in
#"Extracted Values1"
I have a sheet that looks similar to this:
So column A and column B are combined along with a number in column C. What I am trying to do is add up each value in each column (for example: add each C column for each time "Cat" appears, and "Dog" and "Grass", etc) and then find the value in columns A and B that is the highest, and return that value. So for example, in my example above, Dog would be the formula result because it's C column totals to 28. Is there a formula (or, most likely, a combination of formulas) that can accomplish this?
Thank you!
just to show, the formula would be:
=INDEX(INDEX(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),N(IF({1},MODE.MULT(IF(ROW($1:$24)=MATCH(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),0),ROW($1:$24)*{1,1}))))),MATCH(MAX(SUMIFS(C:C,A:A,INDEX(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),N(IF({1},MODE.MULT(IF(ROW($1:$24)=MATCH(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),0),ROW($1:$24)*{1,1}))))))+SUMIFS(C:C,B:B,INDEX(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),N(IF({1},MODE.MULT(IF(ROW($1:$24)=MATCH(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),0),ROW($1:$24)*{1,1}))))))),SUMIFS(C:C,A:A,INDEX(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),N(IF({1},MODE.MULT(IF(ROW($1:$24)=MATCH(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),0),ROW($1:$24)*{1,1}))))))+SUMIFS(C:C,B:B,INDEX(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),N(IF({1},MODE.MULT(IF(ROW($1:$24)=MATCH(INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),INDEX(A1:B12,N(IF({1},INT((ROW($1:$24)-1)/2)+1)),N(IF({1},MOD((ROW($1:$24)-1),2)+1))),0),ROW($1:$24)*{1,1})))))),0))
This is an array formula and must be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode.
It gets a little more manageable with the new dynamic array formulas:
=INDEX(UNIQUE(INDEX(A1:B12,N(IF({1},INT(SEQUENCE(24,,0)/2)+1)),N(IF({1},MOD(SEQUENCE(24,,0),2)+1)))),MATCH(MAX(SUMIFS(C:C,A:A,UNIQUE(INDEX(A1:B12,N(IF({1},INT(SEQUENCE(24,,0)/2)+1)),N(IF({1},MOD(SEQUENCE(24,,0),2)+1)))))+SUMIFS(C:C,B:B,UNIQUE(INDEX(A1:B12,N(IF({1},INT(SEQUENCE(24,,0)/2)+1)),N(IF({1},MOD(SEQUENCE(24,,0),2)+1)))))),SUMIFS(C:C,A:A,UNIQUE(INDEX(A1:B12,N(IF({1},INT(SEQUENCE(24,,0)/2)+1)),N(IF({1},MOD(SEQUENCE(24,,0),2)+1)))))+SUMIFS(C:C,B:B,UNIQUE(INDEX(A1:B12,N(IF({1},INT(SEQUENCE(24,,0)/2)+1)),N(IF({1},MOD(SEQUENCE(24,,0),2)+1))))),0))
But we can use helper columns with the dynamic array formula.
In one cell we put:
=UNIQUE(INDEX(A1:B12,N(IF({1},INT(SEQUENCE(24,,0)/2)+1)),N(IF({1},MOD(SEQUENCE(24,,0),2)+1))))
I put it in E1, then I refer to that with the sumifs:
=SUMIFS(C:C,A:A,E1#)+SUMIFS(C:C,B:B,E1#)
I put that in F1, then use INDEX/MATCH:
=INDEX(E1#,MATCH(MAX(F1#),F1#,0))
Doing it the long way with normal formulas, one would need to copy paste the two columns one below the other and use Remove duplicate on the data tab to get a unique list:
Then use the formula in F1:
=SUMIFS(C:C,B:B,E1)+SUMIFS(C:C,A:A,E1)
And copy down the list. then use the INDEX/MATCH:
=INDEX(E:E,MATCH(MAX(F:F),F:F,0))
to return the desired value.
And just to be thorough here is why vba is better for this. Put this in a module:
Function myMatch(RngA As Range, RngB As Range, sumRng As Range)
If RngA.Cells.Count <> RngB.Cells.Count Or RngA.Cells.Count <> sumRng.Cells.Count Or RngB.Cells.Count <> sumRng.Cells.Count Then
myMatch = CVErr(xlErrValue)
Exit Function
End If
Dim arrA As Variant
arrA = RngA.Value
Dim arrB As Variant
arrB = RngB
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim j As Long
For j = 1 To 2
Dim i As Long
For i = 1 To UBound(arrA)
Dim uRec As String
uRec = IIf(j = 1, arrA(i, 1), arrB(i, 1))
Dim smRec As Double
smRec = Application.SumIfs(sumRng, RngA, IIf(j = 1, arrA(i, 1), arrB(i, 1))) + Application.SumIfs(sumRng, RngB, IIf(j = 1, arrA(i, 1), arrB(i, 1)))
On Error Resume Next
dict.Add uRec, smRec
On Error GoTo 0
Next i
Next j
Dim mx As Double
mx = 0
Dim temp As String
temp = ""
Dim key As Variant
For Each key In dict.Keys
If dict(key) > mx Then
temp = key
mx = dict(key)
End If
Next key
myMatch = temp
End Function
Then all you need to do on the worksheet is call it as a normal function listing the three areas:
=myMatch(A1:A12,B1:B12,C1:C12)
You can also solved the case using Power Query.
Steps are:
Add your source data (which is the three column table) to the Power Query Editor;
Use Merge Columns function under the Transform tab to merge the first two columns by a delimiter, say semicolon ;;
Use Split Columns function under the Transform tab to split the merged column by the same delimiter, say semicolon ;, and make sure in the Advanced Options choose to put the results into Rows;
Use Group By function to group the last column by the Merged column and set SUM as the function;
Lastly, sort the last column Descending.
You can Close & Load the result to a new worksheet (by default).
Here are the Power Query M Codes behind the scene for your reference only. All steps are using built-in functions which are straight forward and easy to execute.
let
Source = Excel.CurrentWorkbook(){[Name="Table2"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Column1", type text}, {"Column2", type text}, {"Column3", Int64.Type}}),
#"Merged Columns" = Table.CombineColumns(#"Changed Type",{"Column1", "Column2"},Combiner.CombineTextByDelimiter(";", QuoteStyle.None),"Merged"),
#"Split Column by Delimiter" = Table.ExpandListColumn(Table.TransformColumns(#"Merged Columns", {{"Merged", Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), let itemType = (type nullable text) meta [Serialized.Text = true] in type {itemType}}}), "Merged"),
#"Changed Type1" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Merged", type text}}),
#"Grouped Rows" = Table.Group(#"Changed Type1", {"Merged"}, {{"Sum", each List.Sum([Column3]), type number}}),
#"Sorted Rows" = Table.Sort(#"Grouped Rows",{{"Sum", Order.Descending}})
in
#"Sorted Rows"
Let me know if you have any questions. Cheers :)
How can I extract dates from String in Microsoft excel cell? I have the following information on Cell A
In Cell A2 I have: 360485
In Cell B2 I have (Note: Its wrap text in a single cell):
10/7 - comment 1
5/3/16 - comment 2
3/21/16 comment 3
1/26/16 - comment 4"
I want to to get something like this
Col A Col B Col C
360485 10/7/16 - comment 1
360485 5/3/16 - comment 2
360485 3/21/16 comment 3
360485 1/26/16 - comment 4"
#JNevill,
Data in Col A: 600537L
Data in Col B
6/21/17 - text comment 1
951396-LH/RH-951554
10/27 - text comment 2
normal text
2/5/16 - text comment 3"
Result
Col A Col B Col C
600537L 6/21/2017 - text comment 1
600537L 951396-LH/RH-951554
600537L 27-Oct - text comment 2
600537L normal text
600537L 2/5/2016 - text comment 3
Something like the following will get you in the ballpark:
Sub test()
'get that ugly b2 value into an array split by line
Dim b2Array As Variant
b2Array = Split(Sheet1.Range("B2"), Chr(10))
'grab the value in a2
Dim a2Value As String
a2Value = Sheet1.Range("A2").Value
'loop through the array (each line in B2 and output. Making use of more `split` here to grab values
Dim writeRow As Integer: writeRow = 1
For Each element In b2Array
Sheet2.Cells(writeRow, 1).Value = a2Value
Sheet2.Cells(writeRow, 2).Value = Trim(Split(Trim(element), " ")(0))
Sheet2.Cells(writeRow, 3).Value = Trim(Replace(element, Split(Trim(element), " ")(0), ""))
writeRow = writeRow + 1
Next
End Sub
That's assuming this is on Sheet1 and you are wanting to output to Sheet2.
In Excel 2010 or later, you can do this using Power Query (aka Get&Transform), and then refresh the query if you add more rows.
Split Column 2 by linefeed character into Rows
Trim Column 2 to remove leading and trailing spaces
Split by space into columns, but only the left-most space
Here is the M-Code
let
Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Split Column by Delimiter" = Table.ExpandListColumn(Table.TransformColumns
(Source, {{"Column2", Splitter.SplitTextByDelimiter("#(lf)", QuoteStyle.Csv),
let itemType = (type nullable text) meta [Serialized.Text = true] in type {itemType}}}), "Column2"),
#"Changed Type1" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Column2", type text}}),
#"Added Custom" = Table.AddColumn(#"Changed Type1", "Trim", each Text.Trim([Column2])),
#"Removed Columns" = Table.RemoveColumns(#"Added Custom",{"Column2"}),
#"Split Column by Delimiter1" = Table.SplitColumn(#"Removed Columns",
"Trim", Splitter.SplitTextByEachDelimiter({" "}, QuoteStyle.Csv, false), {"Trim.1", "Trim.2"})
in
#"Split Column by Delimiter1"
Original data
Results