Excel String - search and ordering with VBA - string

looking for a starting point for my task.
Situation:
I have three Excel worksheets that have the same structure and contain the following columns amongst others:
|ID|SubID|SubName|Description|Link1|Link2|
|1|1|SubName 1|Desc SubName1|P00001|P00002|
|1|2|SubName 2|Desc SubName2|P00002|P00003|
|2|1|SubName 1|Desc SubName1|P00001|P00002|
here comes a line that contains more than one P-Number inside a cell:
|3|1|SubName 1|Desc SubName1|P00001,P00002,P00003|P00001,P00002|
I now would like to look through the Link1 column and place those values with the first three columns in a sorted order to a "Link1" worksheet like so:
|P-Number|ID|SubID|SubName|
|P00001|1|1|SubName 1|
|P00001|2|1|SubName 1|
|P00001|3|1|SubName 1|
|P00002|1|2|SubName 2|
|P00002|3|1|SubName 1|
|P00003|3|1|SubName 1|
And I would like to do the same with the Link2 column also placing the values to a seperate worksheet.
The next step would be to concatenate the P-Number of the new sheet with a string that works like a link. That's what I have working so far.
Another idea would be to make the SubName clickable and have it jump to the according name in one of the three worksheets.
I'm not sure if a macro is the right choice or maybe a Pivot table could also do the trick.
Any ideas are appreciated.
Thanks.
UPDATE:
I tried to incorporate this concept to my main macro where I also process the columns mentioned above. However since I have several loops running through this part the output line will be over-written by the last P-number of the column. I use this Sub:
Sub PrintArray(Data As Variant, Cl As Range)
Cl.Resize(UBound(Data, 1), UBound(Data, 2)) = Data
End Sub
And this call for testing:
PrintArray NewArray, ActiveWorkbook.Worksheets("Link 1").Range("A2")
How can I "save" each P-number to the new sheet without having to use a loop inside my code?

Pivot tables can't split 1 cell into multiple rows, so the starting point of a VBA solution would be:
load input range into an array (my_array = Range(...).Value2)
for each row, split Link1 (link_array = Split(my_array(current_row, 5), ","))
for each link, append the link and other values into a results array
place the results onto a worksheet
sort the resulting range

Related

How to generate a three level dependable dropdown list

I am a complete VBA beginner and this is the first time I have had to deal with VBA. My project is simple- a user form which heavily relies on dependent drop down lists. I watched a ton of videos and wrote (more like copy-pasted) code which actually works just fine. My issue is that I need to edit part of my code to add a feature which I have trouble finding a video on (trial and error editing only took me this far).
In it's current state, my form has two dropdown lists drawing information from a sheet where data is arranged in columns as follows:
ITEM ID | ITEM | CATEGORY
The user picks a category and then the item list if filtered based on the previous selection. I now need to rearrange those columns are add another one, making it the 1st tier selection as follows:
LOCATION | CATEGORY | ITEM ID | ITEM
Just rearranging the columns alone breaks my code. On top of that I need to add the Location combobox, which would filter the Categories, which in turn filter the Items.
This is the code which handles the CATEGORY and ITEM list:
Private Sub cmbEquipCategory_Change()
Dim sh As Worksheet
Dim lastBlankRow As Long
Me.cmbEquipment.Clear
Set sh = Sheets("Equipment_List")
lastBlankRow = sh.Cells(Rows.Count, 3).End(xlUp).Row
For i = 2 To lastBlankRow
If sh.Cells(i, 3) = Me.cmbEquipCategory.value Then
Me.cmbEquipment.AddItem sh.Cells(i, 2)
End If
Next i
End Sub
It is my impression that I need to alter this code to draw data from columns 2 and 4 (it currently does so from 3 and 2) and write another almost identical block of code which handles LOCATION and CATEGORY. Any advice, resources or help would be greatly appreciated. Thanks!
The way I do this is to used named ranges. So selecting your ITEM ID would lead to one of several ITEM ranges (I name them according to the ITEM ID options) which would lead to one of several CATEGORY ranges (I name these according to the ITEM options). The more options you have the more ranges you need. Named ranges aren't broken by adding in columns.

Making a vector out of excel columns using python

everyone...
I just started on python a couple of days ago because I require to handle some excel data in order to automatically update the data of certain cells from one file into another.
However, I'm kind of stuck since I have barely programmed before, and it's my first time using python as well, but my job required me to find a solution and I'm trying to make it work even though it's not my field of expertise.
I used the "xlrd library", imported my file and managed to print the columns I'm needing... However, I can't find a way to put those columns into a matrix in order to handle the data like this:
Matrix =[DataColumnA DataColumnG DataColumnH] in the size [nrows x 3]
As for now, I have 3 different outputs for the 3 different columns I need, but I'm trying to join them together into one big matrix.
So far my code looks like this:
import xlrd
workbook = xlrd.open_workbook("190219_serviciosWRAmanualV5.xls");
worksheet = workbook.sheet_by_name("ServiciosDWDM");
workbook2 = xlrd.open_workbook("Potencia2.xlsx");
worksheet2 = workbook2.sheet_by_name("Hoja1");
filas = worksheet.nrows
filas2 = worksheet2.nrows
columnas = worksheet.ncols
for row in range (2, filas):
Equipo_A = worksheet.cell(row,12).value
Client_A = worksheet.cell(row,13).value
Line_A = worksheet.cell(row, 14).value
print (Equipo_A, Line_A, Client_A)
So I have only gotten, as mentioned above, the data in the columns which is what I'm printing which you can see.
What I'm trying to do, or the main thing I need to do is to read the cell of the first row in Column A and look for it in the other excel file... if the names match, I would have to validate that for the same row (in file 1) the data in both the ColumnG and ColumnH is the same as the data in the second file.
If they match I would have to update Column J in the first file with the data from the second file.
My other approach is to retrieve the value of the cell in ColumnA and look for it in the column A of the second file, then I would make an if conditional to see if ColumnsG and H are equal to Column C of 2nd file and so on...
The thing here is, I have no idea how to pin point the position of the cell and extract the data to make the conditional for this second approach.
I'm not sure if by making that matrix my approach is okay or if the second way is better, so any suggestion would be absolutely appreciated.
Thank you in advance!

How to label scatterplot points by name?

I am trying to figure out how to get labels to show on either Google sheets, Excel, or Numbers.
I have information that looks like this
name|x_val|y_val
----------------
a | 1| 1
b | 2| 4
c | 1| 2
Then I would want my final graph to look like this.
4| .(c)
3|
2| .(b)
1| .(a)
|__ __ __ __
0 1 2 3 4
Like why can't I label each of these points with its name? I can only seem to label the value, e.g, (c) would show 4
Is the only solution D3?
Well I did not think this was possible until I went and checked. In some previous version of Excel I could not do this. I am currently using Excel 2013.
This is what you want to do in a scatter plot:
right click on your data point
select "Format Data Labels" (note you may have to add data labels
first)
put a check mark in "Values from Cells"
click on "select range" and select your range of labels you want on the points
UPDATE: Colouring Individual Labels
In order to colour the labels individually use the following steps:
select a label. When you first select, all labels for the series should get a box around them like the graph above.
Select the individual label you are interested in editing. Only the label you have selected should have a box around it like the graph below.
On the right hand side, as shown below, Select "TEXT OPTIONS".
Expand the "TEXT FILL" category if required.
Second from the bottom of the category list is "COLOR", select the colour you want from the pallet.
If you have the entire series selected instead of the individual label, text formatting changes should apply to all labels instead of just one.
None of these worked for me. I'm on a mac using Microsoft 360. I found this which DID work:
This workaround is for Excel 2010 and 2007, it is best for a small number of chart data points.
Click twice on a label to select it.
Click in formula bar.
Type =
Use your mouse to click on a cell that contains the value you want to use.
The formula bar changes to perhaps =Sheet1!$D$3
Repeat step 1 to 5 with remaining data labels.
Simple
For all those who don't have the option in Excel (like me), there is a macro which works and is explained here: https://www.get-digital-help.com/2015/08/03/custom-data-labels-in-x-y-scatter-chart/ Very useful
Another convoluted answer which should technically work and is ok for a small number of data points is to plot all your data points as 1 series in order to get your connecting line. Then plot each point as its own series. Then format data labels to display series name for each of the individual data points.
In short it works ok for a small data set or just key points from a data set.
If using VBA is an option and assuming that you have a table named 'Table1' of the form:
Label|x_val|y_val
----------------
a | 1| 1
b | 2| 4
c | 1| 2
this routine should work:
Sub labelDatapoints()
Dim r As Integer
With ActiveSheet.ChartObjects(1).Chart 'The scatter plot
.SeriesCollection(1).ApplyDataLabels
For r = 1 To Range("Table1[Label]").Rows.Count 'iterate through all data points
.SeriesCollection(1).Points(r).DataLabel.Text = Range("Table1[Label]").Cells(r).Value 'add the custom label to the current datapoint
Next r
End With
End Sub
Modified from https://www.get-digital-help.com/dynamic-data-labels-in-a-chart/

Categorizing bank statements in Excel

I'm wanting to categorize a bank statement from a list of rules in excel. I've tried using a vlookup but I would like to be able to have non exact matches and as far as I know vlookup is not suited to this.
For instance if my statement looked like this and was located in worksheet "Statement"
Date | Transaction desciption | Amount
7/3/2013 | The Home Depot | $345.00
7/4/2013 | McDonald's #27 | $4.50
And I had a list of rules located in worksheet "Rules"
Rule | Category
The Home Depot | Home improvements
McDonald's * | Fast food
Is there a simple way to add another column using vba to the sheet "Statement" called Category that uses the rules to generate categories for each transaction?
Simple, no. I've done something similar in the past, this is how I did it.
1) Setup a rules page, I've called mine 'Patterns'. These patterns are setup with with the A column (from A2 on) being the 'name' and the B column being the regex pattern.
2) Load these into a public variable with the following code (I put a button on the patterns sheet to run this macro and load them into memory.):
Option Explicit
Public Patterns() As String
Sub LoadPatterns()
Dim cell As Range
Dim bRow As Range
Sheets("Patterns").Activate
'select column A, and load into first dimensino
Range("A2", Sheets("Patterns").Range("A" & Sheets("Patterns").Range("A:A").Rows.Count).End(xlUp).Address).Select
ReDim Patterns(Selection.Rows.Count - 1, 1)
For Each cell In Selection
Patterns(cell.Row - 2, 0) = cell.Value
Next
'select column B and load into the second dimension
Range("B2", Sheets("Patterns").Range("A" & Sheets("Patterns").Range("A:A").Rows.Count).End(xlUp).Address).Select
For Each cell In Selection
Patterns(cell.Row - 2, 1) = cell.Value
Next
End Sub
3) Create the following UDF, load the VB regex library as a reference in vba (Microsoft VBScript Regular Expressions 5.5, see http://www.macrostash.com/2011/10/08/simple-regular-expression-tutorial-for-excel-vba/) and call it on your transaction description as a formula after running step 2:
Public Function rxBanking(sName As String)
Dim x As Integer
'Get & load Patterns
Dim regex As New VBScript_RegExp_55.RegExp
Dim match
For x = 0 To UBound(Patterns)
regex.Pattern = Patterns(x, 1)
regex.ignorecase=True
match = regex.Test(sName)
If match Then
rxBanking = Patterns(x, 0)
Exit For
Else
rxBanking = "Unknown"
End If
Next
End Function
so for example, after you've loaded a pattern such as:
Category | RegEx pattern
--------------------------------
Home loan | INTEREST[\s]CHARGED
If your transaction data was in cell D1, then you could categorise it using the formula
=rxBanking(D1)
If you reload your pattern, you will need to re-copy your formulas down on the spreadsheet, as it doesn't automatically recalculate.
For help using regex (which even if you are familiar, you might need) I find a great testing ground is http://regexpal.com/

Excel 2010: Macro for hidden column groups

VBA is not my particular strength, but here we go:
I would like to trigger a macro once a group of columns is hidden or shown. How can I archive this?
The results of my previous research
The only good hint about this I could find is this discussion at MSDN. Here, a solution is using the following way is drafted:
From the root dir of the xlsx file create a file customUI\customUI.xml with the content
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" >
<commands >
<command
idMso="ColumnsHide"
onAction="ColumnHide_onAction"/>
<command
idMso="ColumnsUnhide"
onAction="ColumnUnhide_onAction"/>
</commands >
</customUI >
and add
<Relationship Id="edTAB" Type="http://schemas.microsoft.com/office/2006/relationships/ui/extensibility" Target="customUI/customUI.xml" />
to the _rels\_rels.xml. (All this probably is much easier using Visual Studio, but I have no access to such sophisticated tools in the microsoft world...) Now, the macro can be used the following way:
Public Sub ColumnHide_onAction(control As IRibbonControl, ByRef cancelDefault)
'
' Code for onAction callback. Ribbon control command
'
MsgBox "Ribbon Column Hide"
cancelDefault = False
End Sub
Public Sub ColumnUnhide_onAction(control As IRibbonControl, ByRef cancelDefault)
'
' Code for onAction callback. Ribbon control command
'
MsgBox "Ribbon Column Unhide"
cancelDefault = False
End Sub
This approach perfectly catches hiding and unhiding of columns, but not hiding and unhiding of groups. So, close, but not quite there.
Downloading the possible idMso values from here, I got notice of the GroupViewShowHide control. Using this the same way as ColumnsHide or ColumnsUnhide does not archive the desired result, though.
Any ideas?
For hiding groups of columns, I've noticed you haven't used the .Hidden property in your code example. It can be quite useful, especially if you define a group of columns in an array. For example:
For byti = 0 To UBound(cols())
If Hide Then
ActiveSheet.columns(cols(byti)).EntireColumn.Hidden = True
...and so on.
To check if a group of columns is already hidden or not, you could also use an array. Group your columns by assigning them to an array, then compare your worksheet's current status (what columns are hidden, what are not) to that array.
The code below is a suggestion for starting, and you could adapt to your project. It doesn't require the customUI.xml file you mentioned in your question.
The key parts are the MyColumnCheck variant, which is used to check if columns are hidden, and the .Match method. This is the VBA equivalent of the Match spreadsheet function.
Working on this code taught me much about how to search within arrays, and the ups and downs of using Match versus other methods - such as Find and just looping through an array! This has been discussed in several posts already, see this one for a good example. I chose to do Match rather than a For...Next loop, although it would be easy to include a For..Next loop that checks if a hidden column is in a group you assign.
If you're wondering about the IfError statement:
Application.IfError(Application.Match(MyColumnCheck.Column, MyColumnArray, 0),...
... this is because using Match in VBA code is often somewhat tricky as mentioned here. Also, as #Lori_m wrote here, "Using Application without .WorksheetFunction returns a variant which allows for arrays in arguments and results."
Also, I chose to change the values of the group array to -1 as they were checked, so when the procedure was done a little simple math would reveal if all the columns referenced by the array were hidden. A negative number is better for this check because I'm assuming you would refer to an actual column with only positive numbers.
So, to sum up, .Match can be used effectively to check if the hidden columns on a worksheet match a group of columns defined by an array.
'the column group you're looking for, dim as a dynamic array of column numbers
Dim MyColumnArray(1) As Long
'MyColumnArray(0) is column 2 or "B", MyColumnArray(1) is column 3 or "C", etc
MyColumnArray(0) = 2
MyColumnArray(1) = 3
Dim MyColumnCheck As Variant 'column check
For Each MyColumnCheck In Worksheets("Sheet1").columns
'if the column is hidden and exists in MyColumnArray array...
If columns(MyColumnCheck.Column).EntireColumn.Hidden = True And _
Application.IfError(Application.Match(MyColumnCheck.Column, MyColumnArray, 0), 0) > 0 _
Then
MyColumnArray(Application.Match(MyColumnCheck.Column, MyColumnArray, 0) - 1) = -1
'... set that element of the MyColumnArray array to -1.
End If
Next
If WorksheetFunction.Sum(MyColumnArray) = 0 - (UBound(MyColumnArray) + 1) Then
Debug.Print "group MyColumnArray is hidden"
'execute code here for when the group is hidden
Else
Debug.Print "group MyColumnArray is visible"
'execute code here for when the group is visible
End If

Resources