Concatenate Rows with Matching IDs - vba

I have multiple rows of purchase details. Each purchase has a client ID. For presentation purposes I need to merge purchases with a similar client ID into a single cell so I can use a VLOOKUP to display this in another table that has client information. Any ideas?
In the example below, I'd like cell C2 to contain "1, 2", cell C3 to contain "3" and cell C4 to be empty (bill has made no purchases).
A B C
1 client_id name purchase_ids
2 1 jim
3 2 bob
4 3 bill
purchase_id purchase_client_id amount
1 1 100
2 1 500
3 2 50

You can create a Dynamic Pivot Table into new sheet, to summarize sales by ClientID, and then use that table with VLOOKUP (http://www.tips-for-excel.com/2011/06/how-to-make-a-pivot-table/).
Example data sheet
Pivot table summarized by ClientID

Here another suggestion, do a function that gathers the data in one cell with VBA. Done this some time ago, but you can use & edit it for your own purpose -
Option Explicit
Public Function STRINGCONCATENATEVLOOKUP(ByVal r As Range, ByVal criteria As Variant, Optional ByVal colnum As Long, Optional ByVal separator As String) As String
On Error GoTo err_hand
Dim n As Long
Dim result As String
If colnum = Empty Then colnum = r.Columns.Count
If colnum > r.Columns.Count Or colnum < 1 Then
STRINGCONCATENATEVLOOKUP = "#COLVALUE!"
Exit Function
End If
If separator = "" Then separator = ";"
For n = 1 To r.Rows.Count Step 1
If r.Cells(n, 1).Value = criteria Then result = result & r.Cells(n, colnum).Value & separator
Next
result = Left(result, Len(result) - Len(separator))
STRINGCONCATENATEVLOOKUP = result
Exit Function
err_hand:
STRINGCONCATENATEVLOOKUP = CVErr(xlErrValue)
End Function
Function works just like VLOOKUP, but with the difference it sums all data and returns a string separated by ";" or what you define.

I'm afraid you're going to have to get your hands dirty with VBA (macro programming) to do what you want to do.
There is no Excel function which can create a concenated list. The Excel function CONCATENATE doesn't do what you need:
=CONCATENATE(A1, "-", B1) # returns "foo-bar" if A1 has 'foo' and B1 has 'bar'
So VBA is what you'll need. Fortunately, others have been here before, including this SO answer.

my answer requires MOREFUNC addon*
Here I assume data in purchase "table" is in A9:C11. Adjust accordingly.
formula for C2:
{=MCONCAT(IF($B$9:$B$11=A2,$A$9:$A$11,""),",")}
notice the curly braces. This is an array formula you have to confirm using Ctrl+Shift+Enter, not just Enter
then copy the formula to C3 and C4
MOREFUNC ADDON
Morefunc Addon is a free library of 66 new worksheet functions.
HERE is some information (by original author)
here is the last working download link I found
here is a good installation walk-through video

Related

Excel VB - Multiple If Column Contains Strings Then

I have looked at a bunch of questions like this, but I have only found formulas, and VB examples that don't appear to check the values of cells in a column.
I was able to derive the following formula in Excel:
=IF(AND(ISNUMBER(SEARCH("Cat",R2)),OR(ISNUMBER(SEARCH("5E",R2)),ISNUMBER(SEARCH("6",R2))), ISNUMBER(SEARCH("Patch",R2)), ISNUMBER(SEARCH("Cables",R2))), "CAT 5E Ethernet Cables")
The problem is that this formula only checks for 1 out of 500 possible values. This is not productive. Plus, I have to make it one big formula when I check the entire row, because if I don't, the formula overwrites the result of the previous formula...
So, Visual Basic... I think I may have better luck scripting some kind of IF ELSE or CASE statement. I just do not understand how to do this in excel. I need to achieve the same thing as the formula above, but instead of checking for one set of conditions,
I need to check for multiple, and populate the S & T columns based on the result of each set of conditions.
I found this webpage that just mentions Excel and shows a VB IF - ELSE statement. How can I make this statement check Excel columns?
I tried the selected answer in this post with no luck:
Private Sub UpdateCategories()
Dim x As Long
For x = 1 To 5000
If InStr(1, Sheet1.Range("$B$" & x), "cat") > 0 And InStr(1, Sheet1.Range("$B$" & x), "5e") > 0 Then
Sheet1.Range("$T$" & x) = Sheet1.Range("$T$" & x) & "CAT 5E Ethernet Cables (Test)"
End If
Next
End Sub
Any help is appreciated. Thanks in advance!
Assuming you choose the route of using a data table sheet to compare to your string.
You would need to have a sheet looking like this (Maybe this is not what you want because I didn't thoroughly understand how your data looks like but the idea remains). You could have sub-category if you want, as well as category, in a third column.
column A | column B
keyword |category
CAT |ATX Cases
5e |Mini-ITX Cases
important words |MicroATX Cases
...
This would need to be filled manually. I'm not sure about the amount of data you're looking at. It can be pretty rapid if you can copy/paste stuff efficiently, depending on the form of your data.
When you have that, loop using this code. I assume the data table is in Sheet1, columns A and B and the values are in Sheet2, column A.
dim listLength as integer 'number of values to look at
dim i as integer
dim dataLength as integer 'number of keywords
dim j as integer
dim keyword as string
dim value as string
listlength = Sheet2.Cells(Rows.Count, "A").End(xlUp).Row - 1 'assuming you start on row 2
datalength = Sheet1.Cells(Rows.Count, "A").End(xlUp).Row - 1 'assuming you start on row 2
for i = 2 to listLength + 1
value = Sheet2.Range("A")(i)
for j = 2 to dataLength + 1
keyword = Sheet1.Range("A")(j)
if instr(value, keyword) <> 0 then
' what happens when the keyword is present goes here
end if
next j
next i

Excel VBA - Column count using variants

I have searched the forums but I am really struggling to get part of my code to work. Basically the idea is to search sheet 1 and copy one or more columns depending on the criteria to a specific worksheet.
i.e. if sheet 1 columns 1 and 3 contain "copy 01" then copy both columns to a sheet 2 and if sheet 1 columns 2 and 4 contain "copy 02" then copy both columns to a sheet 3 etc.
I can count rows fine using the code, but can't count columns. Seems to relate to not fiding the column range but I have no ideas to fix this! Any help would be much appreciated.
'Row
Dim NR As Long
Dim d As Variant
d = ws1.Range("A1:A" & Cells(Rows.Count, "A").End(xlUp).Row).Value
For NR = 1 To UBound(d, 1)
'column
Dim NC As Long
Dim e As Variant
e = ws1.Range(Cells(1, Columns.Count).End(xlToLeft).Column).Value
For NC = 1 To UBound(e, 1)
Thanks,
Stewart
You want this:
e = range("A1:" & split(cells(1,cells(1,columns.Count).end(xlToLeft).column).address(true,false), "$")(0) & "1").Address
The cells(1, columns.count).end(xlToLeft).column) gets the last column number (for example 13 for 'M').
Putting this into cells(1, lastcolNum) gets a cell that represents the cell in the first row of this column (for example Cell M1).
The address(true, false) method gets the cell reference with a dollar sign before the row but not before the column letter (for example "M$1"
The split function returns an array which splits the input string by the "$" character (for example array - ("M","1")
The (0) returns the 0th element in the returned array (for example "M")
Then putting this into the range function returns the range (for example) "A1:M1"
I'm not entirely sure what you're trying to do with the UBound function here. It would make more sense to make
e = cells(1,columns.count).end(xlToLeft).column
and then loop through
For N = 1 To e
As this will loop through each column.

VBA Lookup value, return all unique values associated with the lookup value (in userform textbox)

What I am trying to do is use VBA to get around Vlookup's limitations, and create a Userform that will display relevant items for a customer number. I have a column full of duplicate values (customer numbers), then the column next to it has unique values (customer events). Here's a short chunk of my table:
CustNum CustEvent
123 Called In
123 Placed Order
345 Filed Complaint
345 Called In
345 Refund Approved
I created a userform that allows our employees to search through column A, and it returns what is in column B. I did this with a vLookup, but everyone knows vLookup's limitations, as it will only return the first value in B that it runs into.
What I need is for my UserForm textbox to display the top five customer event hits:
Search: 123
Called In, Placed Order
Eventually this will be run through SQL tables that I don't have write access to, so changing the data is not an option.
My code:
Dim x As Long
x = cnum.Value
textbox1.Value = Application.WorksheetFunction.VLookup(x, Range("A2:B5"), 2, False)
You can use a VBA function to get this sorted. I have written the function, can be made more robust; but this should give you the basic idea.
The code behind this is a simple function that will take the ID as its argument and then return all related records. Idea based on Allen Browne's ConcatRelated method.
Public Function ConcatRelated(InputID As Long) As String
Dim lastRow As Long, iCtr As Long, retStr As String
lastRow = Range("A65536").End(xlUp).Row
For iCtr = 1 To lastRow
If Cells(iCtr, 1).Value = InputID Then
retStr = retStr & Cells(iCtr, 2).Value & ", "
End If
Next
If Len(retStr) > 0 Then _
retStr = Left(retStr, Len(retStr) - 2)
ConcatRelated = retStr
End Function
I hope this will be of some help ! Good luck.

Macro generic formula

I have an Excel sheet with four columns (A-D) as below:
1 XYZ 100 800
2 XYZ 250 820
3 XYZ 500 1100
4 XYZ 4,000 1200
I want to compute the column E values as below :
E1 = (D1-D1)*C1
E2 = (D2-D1)*C1
E3 = (D3-D1)*C1+(D3-D2)*C2
E4 = (D4-D1)*C1+(D4-D2)*C2+(D4-D3)*C3
and so on if there are additional rows
Expected Results: E1=0, E2=2000, E3=100000, E4=185000
Is it possible to generalize this formula? Any help is highly appreciated.
You're not going to be able to generalize this formula in the cell, because the formula needs to grow. Also, there is a character limit for formula in Excel cells (see #pnuts comment, below), so you can't reliably use VBA to "build" the formula because after a certain number of rows, you'll exceed that threshold. And while your use-case may not run in to this limitation, in cases like this I would prefer the simplicity of a UDF over a VBA subroutine that "builds" a long formula string.
You can write a custom function that computes the value by iterating the range. This works on your example data. Place the code in a standard code module.
Public Function GetValue(ByVal clStart As Range, ByVal clEnd As Range) As Variant
'Pass only the cell address for the first cell ("D1") and the last cell ("D4")
Dim rng As Range
Dim r As Range
Dim i As Long
Dim myVal As Double
Application.Volatile
If Not clStart.Column = clEnd.Column Then
'These cells should be in the same column, if not
' display an error
myVal = CVErr(2023)
GoTo EarlyExit
End If
Set rng = Range(clStart.Address, clEnd.Address)
For i = 1 To rng.Rows.Count - 1
Set r = rng.Cells(i)
myVal = myVal + _
((clEnd.Value - r.Value) * r.Offset(0, -1).Value)
Next
EarlyExit:
GetValue = myVal
End Function
quite late, but why couldn't you just use
..
E4 = D4*SUM(C$1:C3)-SUMPRODUCT(C$1:C3,D$1:D3)
..
instead of
E4 = (D4-D1)*C1+(D4-D2)*C2+(D4-D3)*C3
To prevent a performance hit, if it's possible to use temp columns, you could create columns to store partial sums of C and C*D.

Excel pulling data from certain cells

I have a file that I only want to extract cells B9, B19, B29, etc etc etc in a pattern throughout the entire file. I would preferably like it to be extracted to a different excel file or someway so that I can do stuff with only those cells in another excel worksheet.
Potentially, I may have several excel files that I may need to do this sort of thing so if there were a way where I had the same format throughout a lot of files that I could always extract cells B9, B19, B29 that would be great. any help appreciated
I looking for syntax if possible
EDIT
Was thinking if I could somehow make an excel IF statement saying if Row has a 9 in it and the row is B then print it somewhere but I want it printed in a column
EDIT 2
I just want column B not A like I mentioned before.
B9, B19,B29,B39 through the whole file
Just in case you want to do it with code:
Sub Test()
'Assumes Sheet1 has your values and Sheet2 will be the data extracted from every row ending in 9
Dim iCounter As Long
Dim newSheetRow As Long
Dim aValue As String
Dim bValue As String
newSheetRow = 1
'Start and nine and increment by 10 till you reach end of sheet
For iCounter = 9 To Sheet1.Rows.Count - 1 Step 10 'NOTE: You may not want to do it by RowCount, but just showing you could
aValue = Sheet1.Range("A" & iCounter)
bValue = Sheet1.Range("B" & iCounter)
Sheet2.Range("A" & newSheetRow).Value = "We were on row: " & iCounter
Sheet2.Range("B" & newSheetRow).Value = aValue
Sheet2.Range("C" & newSheetRow).Value = bValue
newSheetRow = newSheetRow + 1
Next iCounter
MsgBox "Done"
End Sub
You could use the INDIRECT function. It takes a cell reference as a text string and returns the value in that cell. So instead of using
=data!a9
to get the value in sheet "data" in cell a9, you use
=indirect("data!a9")
You can also use r1c1 notation, like this:
=indirect("data!r9c1",false)
From there you can use the ROW and COLUMN functions to go in steps of 10:
=INDIRECT("data!r"&-1+10*ROW()&"c"&COLUMN(),FALSE)
If you put this formula in A1 of your output sheet and then copy and paste it down and across, it will give you the values in data!A9, data!A19, data!A29,... in cells A1, A2, A3... Depending on how you want your output arranged, you might have to modify the cell reference string.
Depending on how often you want to do this depends on how you need to do it, if it's a one of them some simple excel commands might help.
e.g.
In Cell C1 put the following:
=MOD(ROW(),10)
then replicate this down to the bottom of your data. the command will return the numbers 1 through to 0. You can then filter the data on column C where value is 9 then select the visible rows and copy the data to a new sheet.
ROW() ' this returns the ROW number of cell the command is in.
MOD(number, divisor) ' this basically divides one number by the other and returns the remainder. so row 9 / 10 = 0 remainder of 9, row 19 / 10 = 1 remainder of 9.
Hope this helps.