I am using Office 365 and I am trying to get a data table from the web and import it in an Excel sheet together with the images. Here is the table which I am trying to import:
https://royaleapi.com/clan/90R9VPP9/war/analytics
http://i63.tinypic.com/2s655kx.jpg
As you can see from the table, there are images in cells representing certain statuses which contain meaningful data:
Medal = win
Cross = loss
Empty medal slot = missing in action
Empty cell = didn't participate
I click on data and select From Web, where I paste the link. Excel brings up the following, where I select Table 0 for the info I need.
http://i67.tinypic.com/2lmb4u0.jpg
After I click load, the generated table is as below. As you can see, there are no images which denote the status of the person, this method only gets the texts etc. but the cells which should have contained the images are not pulled.
http://i67.tinypic.com/n3kzz5.jpg
After searching online, I've managed to put together a code to isolate the images in another query (Query1) which you can find below. This query gives the images but doesn't place them in cells, I've just managed to get to the images themselves :)
let
Source = Table.FromColumns({Lines.FromBinary(Web.Contents("https://royaleapi.com/clan/8P2V9VYL/war/analytics"), null, null, 65001)}),
#"Filtered Rows" = Table.SelectRows(Source, each Text.Contains([Column1], "src=""/static/img/ui")),
#"Split Column by Delimiter" = Table.SplitColumn(#"Filtered Rows", "Column1", Splitter.SplitTextByEachDelimiter({"src=""/"}, QuoteStyle.None, true), {"Column1.1", "Column1.2"}),
#"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Column1.1", type text}, {"Column1.2", type text}}),
#"Split Column by Delimiter1" = Table.SplitColumn(#"Changed Type", "Column1.2", Splitter.SplitTextByEachDelimiter({""""}, QuoteStyle.None, false), {"Column1.2.1", "Column1.2.2"}),
#"Changed Type1" = Table.TransformColumnTypes(#"Split Column by Delimiter1",{{"Column1.2.1", type text}, {"Column1.2.2", type text}}),
#"Removed Columns" = Table.RemoveColumns(#"Changed Type1",{"Column1.1", "Column1.2.2"}),
#"Added Custom" = Table.AddColumn(#"Removed Columns", "https", each "https://royaleapi.com/"),
#"Reordered Columns" = Table.ReorderColumns(#"Added Custom",{"https", "Column1.2.1"}),
#"Merged Columns" = Table.CombineColumns(#"Reordered Columns",{"https", "Column1.2.1"},Combiner.CombineTextByDelimiter("", QuoteStyle.None),"Merged"),
#"Renamed Columns" = Table.RenameColumns(#"Merged Columns",{{"Merged", "Images"}}),
#"Duplicated Column" = Table.DuplicateColumn(#"Renamed Columns", "Images", "Images - Copy"),
#"Renamed Columns1" = Table.RenameColumns(#"Duplicated Column",{{"Images - Copy", "ImageURLs"}})
in
#"Renamed Columns1"
So, is there any way to simply get the correct images in their correct cells whenever I refresh the table? Unfortunately, I have very limited coding knowledge so I am open to your suggestions and assistance :)
Thanks in advance!
Oandic
This shows how you can gather the links for the images into a 2d array that can be overlaid onto your data range in the sheet as the dimensions (number of rows and number of columns) match. It means you can loop the rows and columns of the array and use them to index into your data range to have the right location to then add your image from the image URL to the cell.
You can use .Top and .Left to position. Generic outline code given at bottom. You will need to size images appropriately and space rows and columns as well.
Option Explicit
Public Sub GetTable()
Dim sResponse As String, html As New HTMLDocument
With CreateObject("MSXML2.XMLHTTP")
.Open "GET", "https://royaleapi.com/clan/90R9VPP9/war/analytics", False
.send
sResponse = StrConv(.responseBody, vbUnicode)
End With
With html
.body.innerHTML = sResponse
Dim hTable As HTMLTable
Set hTable = .getElementsByTagName("table")(0)
End With
Dim numRows As Long, numColumns As Long, r As Long, c As Long, tr As Object, td As Object
numRows = hTable.getElementsByTagName("tr").Length
numColumns = hTable.getElementsByTagName("tr")(2).getElementsByTagName("td").Length
Dim arr()
ReDim arr(1 To numRows, 1 To numColumns)
For Each tr In hTable.getElementsByTagName("tr")
r = r + 1: c = 0
For Each td In tr.getElementsByTagName("td")
c = c + 1
arr(r, c) = GetImgLink(td.outerHTML)
Next
Next
[A1].Resize(numRows, numColumns) = arr '<== Just for example to see how would map to sheet
Stop
End Sub
Public Function GetImgLink(ByVal outerHTML As String) As String
On Error GoTo Errhand
GetImgLink = "https://royaleapi.com/" & Split(Split(outerHTML, "IMG class=""ui image"" src=""about:")(1), Chr$(34))(0)
Exit Function
Errhand:
Err.Clear
GetImgLink = vbNullString
End Function
Adding images and positioning (assuming data starts in A1 otherwise add an adjustment to the row, column indices of the link array you are looping.)
With ActiveSheet.Pictures.Insert(imageURL) ' <== Change to your sheet
.Left = ActiveSheet.Cells(1,1).Left '<== row and column argument to cells will come from loop position within array. Adjust if required.
.Top = ActiveSheet.Cells(1,1).Top
.Placement = 1
End With
Sample of how links map to sheet:
Related
I have this table that looks similar to this:
And I want to transform it so that it looks like this:
The idea is to unpivot( or transpose) the table so that it can be feed into other BI tools, and be readable for analysis.
I have around 20 tables like this with 100+ cols, so of course to do it manually is nearly impossible.
How do I get this done with PowerQuery? I have tried using unpivot feature, but I am stuck as it displayed NYC1, NYC2, etc. VBA, macros don't work also. Any other suggestions are appreciated, but I am at my wits end now. Help!
Here's a pretty generic depivot approach which handles multiple row/column headers.
Select a cell in the source table before running (note - this uses CurrentRegion so will fail if your table has completely blank rows or columns).
Sub UnpivotIt()
Dim numRowHeaders As Long, numColHeaders As Long
Dim numRows As Long, numCols As Long, rng As Range
Dim rngOut As Range, r As Long, c As Long, i As Long, n As Long
Dim arrIn, arrOut, outRow As Long
arrIn = Selection.CurrentRegion.Value
numRowHeaders = Application.InputBox("How many header rows?", Type:=1)
numColHeaders = Application.InputBox("How many header columns?", Type:=1)
Set rngOut = Application.InputBox("Select output (top-left cell)", Type:=8)
Set rngOut = rngOut.Cells(1) 'in case >1 cells selected
numRows = UBound(arrIn, 1)
numCols = UBound(arrIn, 2)
ReDim arrOut(1 To ((numRows - numRowHeaders) * (numCols - numColHeaders)), _
1 To (numRowHeaders + numColHeaders + 1))
outRow = 0
For r = (numRowHeaders + 1) To numRows
For c = (numColHeaders + 1) To numCols
'only copy if there's a value
If Len(arrIn(r, c)) > 0 Then
outRow = outRow + 1
i = 1
For n = 1 To numColHeaders 'copy column headers
arrOut(outRow, i) = arrIn(r, n)
i = i + 1
Next n
For n = 1 To numRowHeaders '...row headers
arrOut(outRow, i) = arrIn(n, c)
i = i + 1
Next n
arrOut(outRow, i) = arrIn(r, c) '...and the value
End If
Next c
Next r
rngOut.Resize(outRow, UBound(arrOut, 2)).Value = arrOut
End Sub
Before loading into PowerQuery, concatenate the headers to the empty row after [Programe Name] using a delimiter (space). You can use the TEXTJOIN function to do this if you use office365. The result looks something like this (I did not copy all your data):
Import this range into PowerQuery and perform the following steps (Do not check the my table has headers checkbox)
Remove top 2 rows (Home tab > Remove Rows)
Use first row as headers (Home tab > Use First Row as Headers)
Select the first column
Unpivot other columns (dropdown menu unpivot columns on Transform Tab)
Split the [Attribute] column by delimiter (space) (Home tab > Split Column)
Change column names
Move City column to the left (rightclick column > move > left)
The script looks like this:
let
Source = Excel.CurrentWorkbook(){[Name="table"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Column1", 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}}),
#"Removed Top Rows" = Table.Skip(#"Changed Type",2),
#"Promoted Headers" = Table.PromoteHeaders(#"Removed Top Rows", [PromoteAllScalars=true]),
#"Changed Type1" = Table.TransformColumnTypes(#"Promoted Headers",{{"Program Name", type text}, {"NY Budget", Int64.Type}, {"NY Revenue", Int64.Type}, {"NY Cost", Int64.Type}, {"NY Margin", Int64.Type}, {"LA Budget", Int64.Type}, {"LA Revenue", Int64.Type}, {"LA Cost", Int64.Type}, {"LA Margin", Int64.Type}}),
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Changed Type1", {"Program Name"}, "Attribute", "Value"),
#"Split Column by Delimiter" = Table.SplitColumn(#"Unpivoted Other Columns", "Attribute", Splitter.SplitTextByDelimiter(" ", QuoteStyle.Csv), {"Attribute.1", "Attribute.2"}),
#"Changed Type2" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Attribute.1", type text}, {"Attribute.2", type text}}),
#"Renamed Columns" = Table.RenameColumns(#"Changed Type2",{{"Attribute.1", "City"}, {"Attribute.2", "Description"}}),
#"Reordered Columns" = Table.ReorderColumns(#"Renamed Columns",{"City", "Program Name", "Description", "Value"})
in
#"Reordered Columns"
And this is the result (in the Power Query Editor)
I have 7 sets of 5 columns with similar data. Currently, I manually copy and paste each 5-column set below the previous set so that all 7 sets are in 5 columns. I need a macro that will turn this:
into this:
Can anyone help?
This macro works well for stacking multiple columns into just one column, but I can't get it to work for 5 columns at a time:
Sub CombineColumns()
Dim rng As Range
Dim iCol As Integer
Dim lastCell As Integer
Set rng = ActiveCell.CurrentRegion
lastCell = rng.Columns(1).Rows.Count + 1
For iCol = 2 To rng.Columns.Count
Range(Cells(1, iCol), Cells(rng.Columns(iCol).Rows.Count, iCol)).Cut
ActiveSheet.Paste Destination:=Cells(lastCell, 1)
lastCell = lastCell + rng.Columns(iCol).Rows.Count
Next iCol
End Sub
Try:
Sub test()
Dim MiMatriz As Variant
Dim FinalArray As Variant
Dim i As Long
Dim zz As Long
Dim PosSet As Long
Dim rngDestiny As Range
Set rngDestiny = Range("A14") 'Change this to top left cell of data destiny
MiMatriz = Range("A1").CurrentRegion.Value 'A1 is top left cell of complete dataset
ReDim FinalArray(1 To Range("A1").CurrentRegion.Columns.Count + 1, 1 To 5) As Variant
For zz = 1 To 5 Step 1
PosSet = 0
Do Until 1 + PosSet > UBound(FinalArray) - 1
For i = 2 To 6 Step 1
FinalArray(i + PosSet, zz) = MiMatriz(i, zz + PosSet)
Next i
PosSet = PosSet + 5
Loop
Next zz
'add headers in index 1
FinalArray(1, 1) = "Title 1"
FinalArray(1, 2) = "Title 2"
FinalArray(1, 3) = "Title 3"
FinalArray(1, 4) = "Title 4"
FinalArray(1, 5) = "Title 5"
'paste data
rngDestiny.Resize(UBound(FinalArray), 5).Value = FinalArray
Erase FinalArray, MiMatriz
End Sub
This is how it works:
You can also do this in Power Query available in Windows Excel 2010+ and Office 365
Algorithm
Demote the header row
Transpose the table
Split the table into "pages" where each "page" has the same number of rows as the desired number of columns
Transpose each subtable, removing the first row from all except the very first subtable
The first row of each subtable is the original column header, but we only want to retain the headers of the first five columns
Expand the subtables into one, and promote the first row to the Header position
To enter this code:
Select some cell in the original table
Data => Get&Transform => From Table/Range
In the Power Query Editor: Home => Advanced Editor
In that window, examine Line 2 and record the actual Table Name
Paste the M Code below into that window, replacing what is there
Edit Line 2 to reflect the actual Table Name
Examine the code, comments, and the Applied Steps window to understand what is going on.
Close and Load
M Code
let
Source = Excel.CurrentWorkbook(){[Name="Table4"]}[Content],
//Demote headers and transpose
#"Demoted Headers" = Table.DemoteHeaders(Source),
#"Transposed Table" = Table.Transpose(#"Demoted Headers"),
//split rows into tables of what will be number of columns
// pagesize argument <= number of columns "5"
#"Split into Separate Tables" = Table.FromList(
Table.Split(#"Transposed Table",5),
Splitter.SplitByNothing(),null, null, ExtraValues.Error),
//Transpose the subtables
// Except for first table, delete the first row after transposing:
// That would be the old column headers
#"Added Index" = Table.AddIndexColumn(#"Split into Separate Tables", "Index", 0, 1, Int64.Type),
#"Added Custom" = Table.AddColumn(#"Added Index", "Custom", each
if [Index] = 0 then Table.Transpose([Column1])
else Table.RemoveFirstN(Table.Transpose([Column1]))),
//Remove unneeded columns
#"Removed Columns" = Table.RemoveColumns(#"Added Custom",{"Column1", "Index"}),
//Expand the tables and promote first row to header row
#"Expanded Custom" = Table.ExpandTableColumn(#"Removed Columns", "Custom",
{"Column1", "Column2", "Column3", "Column4", "Column5"}, {"Column1", "Column2", "Column3", "Column4", "Column5"}),
#"Promoted Headers" = Table.PromoteHeaders(#"Expanded Custom", [PromoteAllScalars=true]),
//Set the types to text
#"Changed Type" = Table.TransformColumnTypes(#"Promoted Headers",
{{"Title 1", type text}, {"Title 2", type text}, {"Title 3", type text}, {"Title 4", type text}, {"Title 5", type text}})
in
#"Changed Type"
I have a dataset where contact information and names are correlated to companies that the individual has worked for. 1 individual can be associated with many companies. I want to consolidate information of the individuals but keep information on the different company names.
I have a VBA function that can remove duplicates of rows (name and contact info) and another VBA function that can merge two separate cells (company names) into 1 merged cell. The data isn't sorted by any particular field.
I would like to create a function that will remove duplicates of rows AND THEN merge the company name cells BUT ONLY FOR individuals that have duplicate rows removed (meaning that the individual is associated with more than 1 company).
Thanks for any help!
Sample of raw data format:
this is the function and result of VBA function 1:
Sub RemoveDuplicates()
'UpdatebyExtendoffice20160918
Dim xRow As Long
Dim xCol As Long
Dim xrg As Range
Dim xl As Long
On Error Resume Next
Set xrg = Application.InputBox("Select a range:", "Kutools for Excel", _
ActiveWindow.RangeSelection.AddressLocal, , , , , 8)
xRow = xrg.Rows.Count + xrg.Row - 1
xCol = xrg.Column
'MsgBox xRow & ":" & xCol
Application.ScreenUpdating = False
For xl = xRow To 2 Step -1
If Cells(xl, xCol) = Cells(xl - 1, xCol) Then
Cells(xl, xCol) = ""
End If
Next xl
Application.ScreenUpdating = True
End Sub
Function 2 is below and the module just concatenates and merges cells, but I don't know how to write a function that will only apply where the individual has had duplicate rows removed (meaning that individual is associated with multiple companies).
Sub MergeCells()
Dim xJoinRange As Range
Dim xDestination As Range
Set xJoinRange = Application.InputBox(prompt:="Highlight source cells to merge", Type:=8)
Set xDestination = Application.InputBox(prompt:="Highlight destination cell", Type:=8)
temp = ""
For Each Rng In xJoinRange
temp = temp & Rng.Value & " "
Next
xDestination.Value = temp
End Sub
I would approach this differently and use Power Query, available in Excel 2010+.
Power Query as a "Group By" method where you can select the columns you want to group by -- in your case it would be all the columns except the Company column. You can then concatentate the company column using linefeeds, and obtain the result you desire.
Data --> Get & Transform Data --> From Table/Range
Select all the columns except Company and Group By
The Operation is All Rows
Add Custom Column (to split out the company names with formula:
Table.Column([Grouped],"Company")
Select the Double-headed arrow at the top of the custom column
Extract values from list
Use the line feed for the separator #(lf)
Close and Load to
You may have to do some custom formatting for the phone number, and also set Word Wrap for the company column.
Here is the generated MCode:
let
Source = Excel.CurrentWorkbook(){[Name="Table3"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Email", type text}, {"Phone", Int64.Type}, {"First Name", type text}, {"Last Name", type text}, {"Company", type text}}),
#"Grouped Rows" = Table.Group(#"Changed Type", {"Email", "Phone", "First Name", "Last Name"}, {{"Grouped", each _, type table [Email=text, Phone=number, First Name=text, Last Name=text, Company=text]}}),
#"Added Custom" = Table.AddColumn(#"Grouped Rows", "Company", each Table.Column([Grouped],"Company")),
#"Extracted Values" = Table.TransformColumns(#"Added Custom", {"Company", each Text.Combine(List.Transform(_, Text.From), "#(lf)"), type text})
in
#"Extracted Values"
And here are the results:
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 :)
First time here so apologies in advance if I'm asking this incorrectly.
I have a pretty good understanding of excel but I've only ever really used it for some easy to moderate formulas. I'm trying to figure out where to start with a problem I have but I'm not even sure what to search for to find the answer. From what I have been able to find - it should be achievable with either Power Query or a Excel-VBA macro?
I have maybe 400 rows of data on a sheet. I need to separate each row of data based on the values in 4 of the columns.
This is the screenshot I've made with a brief example of what I'm trying to achieve.
The top part of the screenshot is how the data is now. The bottom part is how I want the data to be modified. A new row is created for any fee thats been incurred (base fee for every row, then if they have incurred an order fee then a new row is added, if a priority fee has been incurred then a new row is added etc. If it's '0' then no new row).
If any one has any guidance on how to do this it'd be great. I'm not asking for the solution, but just some tips on what I can research or what I should be learning to accomplish something like this!
I'm in a hurry, so this code is rushed. Can come back and explain things if needed. But it gives me the output you've shown.
Option Explicit
Private Sub UnpivotColumns()
Dim sourceSheet As Worksheet
Set sourceSheet = ThisWorkbook.Worksheets("Sheet1")
Dim lastSourceRow As Long
lastSourceRow = sourceSheet.Cells(sourceSheet.Rows.Count, "A").End(xlUp).Row
Dim lastSourceColumn As Long
lastSourceColumn = sourceSheet.Cells(1, sourceSheet.Columns.Count).End(xlToLeft).Column
Dim sourceData As Range
Set sourceData = sourceSheet.Range("A2", sourceSheet.Cells(lastSourceRow, lastSourceColumn))
Dim destinationSheet As Worksheet
Set destinationSheet = ThisWorkbook.Worksheets("Sheet2")
destinationSheet.Cells.Clear
destinationSheet.Range("A1:D1").Value = Array("Name", "Description", "Currency", "Fee")
Dim destinationRowIndex As Long
destinationRowIndex = 1 ' Skip header row
Dim outputArray(1 To 4) As Variant ' Re-use same array throughout procedure
Dim sourceRowIndex As Long
For sourceRowIndex = 2 To lastSourceRow
' Base fee always needs writing (unconditionally)
outputArray(1) = sourceSheet.Cells(sourceRowIndex, "A") ' Name
outputArray(2) = "Base Fee" ' Description
outputArray(3) = "USD" ' Currency
outputArray(4) = 150 ' Fee amount
destinationRowIndex = destinationRowIndex + 1
destinationSheet.Cells(destinationRowIndex, "A").Resize(1, 4).Value = outputArray
Const FIRST_COLUMN_TO_UNPIVOT As Long = 4 ' Skip first three columns
Dim sourceColumnIndex As Long
For sourceColumnIndex = FIRST_COLUMN_TO_UNPIVOT To lastSourceColumn Step 2
outputArray(2) = sourceSheet.Cells(1, sourceColumnIndex) ' Unpivoted description
outputArray(3) = sourceSheet.Cells(sourceRowIndex, sourceColumnIndex + 1) ' Unpivoted currency
outputArray(4) = sourceSheet.Cells(sourceRowIndex, sourceColumnIndex) ' Unpivoted fee amount
' Skip amount if nil/zero
If outputArray(4) > 0 Then
destinationRowIndex = destinationRowIndex + 1
destinationSheet.Cells(destinationRowIndex, "A").Resize(1, 4).Value = outputArray
End If
Next sourceColumnIndex
Next sourceRowIndex
End Sub
The code makes some assumptions (which are true for your screenshot, but might not be true if the layout of your data changes).
Bad things about this code:
It is rigid and not very flexible/dynamic
It will be slow as it read/writes cells one at a time.
There is no referential integrity (column indexes/offsets are assumed and never actually checked/asserted). This becomes a problem if the layout/position of your data changes.
Good things about this code:
It hopefully works (for the example you've shown in your screenshot).
This is my data before the code (on Sheet1):
This is my data after the code (on Sheet2):
Also, something like this is equally possible in Power Query too (and probably in fewer lines of code too).
Assuming the column headers in your example (Name, Email, Order Date, Order Amount, Currency, Expedite Fee, Currency, Courier Fee, Currency) with the source data in named range with headers Table1, the powerquery code would be below.
let Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
#"Added Custom" = Table.AddColumn(Source, "Base Fee", each "USD:150"),
#"Merged Columns" = Table.CombineColumns(Table.TransformColumnTypes(#"Added Custom", {{"Order Amount", type text}}, "en-US"),{"Currency", "Order Amount"},Combiner.CombineTextByDelimiter(":", QuoteStyle.None),"Order Amount1"),
#"Merged Columns1" = Table.CombineColumns(Table.TransformColumnTypes(#"Merged Columns", {{"Expedite Fee", type text}}, "en-US"),{"Currency2", "Expedite Fee"},Combiner.CombineTextByDelimiter(":", QuoteStyle.None),"Expedite Fee1"),
#"Merged Columns2" = Table.CombineColumns(Table.TransformColumnTypes(#"Merged Columns1", {{"Courier Fee", type text}}, "en-US"),{"Currency3", "Courier Fee"},Combiner.CombineTextByDelimiter(":", QuoteStyle.None),"Courier Fee1"),
#"Removed Other Columns" = Table.SelectColumns(#"Merged Columns2",{"Name", "Order Amount1", "Expedite Fee1", "Courier Fee1","Base Fee"}),
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Removed Other Columns", {"Name"}, "Description", "Value"),
#"Split Column by Delimiter" = Table.SplitColumn(#"Unpivoted Other Columns", "Value", Splitter.SplitTextByDelimiter(":", QuoteStyle.Csv), {"Currency", "Fee"}),
#"Replaced Value" = Table.ReplaceValue(#"Split Column by Delimiter","1","",Replacer.ReplaceText,{"Description"})
in #"Replaced Value"