Populate Drop Down List Box (DDLB) with two values in PowerBuilder - string

I've created a Drop Down List Box (DDLB) in my window (I'm using PowerBuilder 10.5). Once I would call my function, the DDLB would fill with all the different cities from my table. This is the code I've used:
FOR li_i=1 TO ii_br_red
ls_city = dw_city.GetItemString(li_i, 'city')
IF ddlb_city.FindItem(ls_city, 1) = -1 THEN
ddlb_city.AddItem(ls_city) END IF; NEXT
Next part of the code is in the ddlb "selectionchanged" event...
dw_city.SetFilter("city = '" + this.text + "'")
dw_city.Filter()
This works great, and after calling my function (via click on a command button) I'd get a list of all different cities in my table, ex.
Paris
London
New York
Washington
No town would be listed twice.
What I need to do now is add a country next to every city in my DDLB. So that after clicking my command button I would get this in my DDLB:
Paris (France)
London (GB)
New York (USA)
Washington (USA)
Any advice? Thanks in advance...
SECOND QUESTION, similar to this subject: I have an SQL code:
SELECT distinct name FROM table1;
This gives me 8 different names. What I want to do is fill another DDLB, ddlb_1 with these names, but this must occur on the open event of my program. This is what I've written in the open event of my program:
string ls_name
SELECT distinct name INTO :ls_name FROM tabel1;
ddlb_1.AddItem(ls_name)
But this only gives me the first name. I'm guessing I need some kind of count, but I just can't pull it off.

If you do not want to change the design of the program, and as you states that the country is in the same DW, you could hack the code a little to add the country to the ddlb (I suppose that the country is available on the same row of the dw):
String ls_country
FOR li_i=1 TO ii_br_red
ls_city = dw_city.GetItemString(li_i, 'city')
IF ddlb_city.FindItem(ls_city, 1) = -1 THEN
ls_country = dw_city.GetItemString(li_i, 'country')
ddlb_city.AddItem(ls_city + ' (' + ls_country + ')')
END IF
NEXT
A quick and dirty hack to get back the value in the event to filter the DW would be
int p
string ls_city
ls_city = this.text
p = pos(ls_city, '(')
if p > 0 then ls_city = left(ls_city, p - 2) //skip the "space + (country)" part
dw_city.SetFilter("city = '" + ls_city + "'")
dw_city.Filter()
But this kind of code is difficult to maintain and should be replaced by something else, as the processing of the city value is strongly coupled to its representation in the list.
A better solution would be a dropdowndatawindow, or (worse) an array of the cities names where the index of a city + country in the ddlb would correspond to the index of the bare city name suitable for filtering the DW

I think you should modify the "source" datawindow' select, and you should get the final result which you want, and you would only need to copy the datas from the datawindow to the ddlb. You should use distinct in the select something like this:
select distinct city + ' (' + country_code + ')' from cities_and_countries_table
of course you should replace the "city", "country_code" to the actual column name in your table as well the table-name. With this you will get every city only once and they will be already concatenated with the country code.
Br. Gábor

Does it really have to be DDLB? I would give the user a Single Line Edit for the city name and filter the DW as the user types.

To answer my own second question, this is how I've done it finally...
String ls_name
DECLARE xy CURSOR FOR
SELECT distinct name FROM table1;
OPEN xy;
FETCH xy INTO :ls_name;
do until sqlca.sqlcode <> 0
ddlb_1.AddItem(ls_name);
FETCH xyINTO :ls_name;
loop
CLOSE xy;

I'm new to PowerBuilder, But I just used that kind of scenario, however I used a DDW (Drop Down Data Window) Instead of a List Box On this case, you can display more than one column as soon as the DW gets the focus and you'd be able to dynamically populate the data. Give it a try. It worked for me, DW's are a pain in the neck when you're just starting (as in my case) but you can do a lot with it

Related

Filter phone numbers from open text field - Power BI, excel, VBA

I have a text field in a table where I need to substitute phone numbers where applicable.
For example the text field could have:
Call me on 08588812885 immediately
Call me on 07525812845
I need assistance please contact me
Good service
Sometimes a phone number will be in the text but not always and the phone number entered will always be different.
Is there a measure to use to replace the phone numbers with no text.
Ideally the solution would be Power BI, but can also be done in the raw data using excel or VBA
Regular expression in VBA (excel) or Python (Power BI) is a straightforward solution.
I have never used PowerBI with Python before but manage to make following python script.
In PowerBI transformation steps I created a new column that would copy [message] columns and named it [noPhoneNumber], then next step ran this python script
import re
def removePhone(x):
return re.sub('\d{10,11}', "**number removed**", x)
length = len(dataset["noPhoneNumber"])
for iRow in range(length):
dataset["noPhoneNumber"][iRow] = removePhone(dataset["noPhoneNumber"][iRow])
so column "noPhoneNumber"
Call me on 08588812885 immediately
Call me on 07525812845
I need assistance please contact me
Good service
becomes
Call me on **number removed** immediately
Call me on **number removed**
I need assistance please contact me
Good service
In VBA Preferable create UDF (user defined function) and don't create a subroutine, that would be too error prone for this kind of problem.
[Added]
If you need to make a Excel based solution, you can create a UDF function like so:
(remember early binding to import of VBScript_RegExp_55.RegExp in excel)
Function removePhoneNumber(text As String, Optional replacement As String = "**number removed**") As String
Dim regex As New RegExp
regex.Pattern = "\d{10,11}"
removePhoneNumber = regex.Replace(text, replacement)
End Function
...and then use excel function like so:
=removePhoneNumber(A2),
=removePhoneNumber(A3)
and so on...
A simple VBA function alternative
Function removePhone(s As String) As String
Const DELIM As String = " "
Dim i As Long, tokens As Variant
tokens = Split(s, DELIM)
For i = LBound(tokens) To UBound(tokens)
If IsNumeric(tokens(i)) Then
tokens(i) = "*Removed*" ' << change to your needs
Exit For ' assuming a single phone number per string
End If
Next
removePhone = Join(tokens, DELIM)
End Function
You can do this in Power Query. Create a custom column with this below code. I have considered the column name is Comments but please adjust this with your column name.
if Text.Length(Text.Select([comments], {"0".."9"})) = 11
then
Text.Replace(
[comments],
Text.Select([comments], {"0".."9"}),
""
)
else [comments]
Here is the output below. You can also replace phone numbers with other text like #### to make is anonymous.
NOTE
This will only work if there are only 1 number in the string with length 11 (You can adjust the length in code as per requirement).
This will Not work if there are more than one Numbers in the string.
If there are 1 number in the string but length not equal 11, this will keep the whole string as original.

Excel table names contain a '$' when pulled from a schema in VB.net

I have a list box that I'm trying to populate in an application to contain Excel tab names or Access tables. It's created by a simple schema grab:
dtSheet = OpenCon.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, New Object() {Nothing, Nothing, Nothing, "TABLE"})
It then gets put into a simple list (of String):
For Each Row In dtSheet.Rows
ListOfSheets.Add(Row("TABLE_NAME").ToString())
Next
This works fine if I open up an Access database, the names of the tables are listed nicely. However, when I use it for Excel, I get symbols such as $ and quotes "'". I'd like to trim this off to just have the tab names but so far I haven't been able to find anything to help my issue specifically.
My suspicion is that the answer is in the {Nothing...."TABLE"} object. I'm a little light on how filters like this work and I was having issues wrapping my head around it after reading the .NET documentation.
Another idea would be to do some post string alteration trimming but I'd like to see if there was an easier way to get the simple string name that I want from the start.
Any help would be appreciated.
Based on the discussion from jmcihinney, I built a slightly better code.
dtSheet = OpenCon.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, New Object() {Nothing, Nothing, Nothing, "TABLE"})
dtSheet.Columns.Add("Table_Text", Type.GetType("System.String"))
For i As Integer = dtSheet.Rows.Count - 1 To 0 Step -1
If Microsoft.VisualBasic.Strings.Right(dtSheet.Rows(i)("TABLE_NAME"), 1) <> "$" Then
dtSheet.Rows.RemoveAt(i)
Else
dtSheet.Rows(i)("TABLE_Text") = Replace(dtSheet.Rows(i)("TABLE_NAME"), "$", "")
End If
Next
I essentially add a column to the datatable that is built from the list pull. To remove anything that isn't a table (e.g. named ranges, Sheet views) I check the Table_Name column for the suffix of "$". Note I go backwards to keep from messing with any indexing.
Then in the Else statement I put a "Replace" to make the table_text a "$"less version of Table_Name
That way I can then build my listbox like this:
Me.lbTableList.DataSource = dtsheet
Me.lbTableList.DisplayMember = "TABLE_Text"
Me.lbTableList.ValueMember = "TABLE_NAME"
This makes my table look nice regardless if I loaded an Access database or Excel file. This also allows me to just pass the TABLE_NAME to the connection command and not worry whether there is a "$" in the name or not:
DBCmd.CommandText = "SELECT * FROM [" & strTable & "]"

Convert long formula into an ARRAYFORMULA

Document: https://docs.google.com/spreadsheets/d/1N4cGw5eUq_3gCJh1w39qVatX9KV1_Hr-AqRHj_nbckA/edit#gid=1770960621
Question
How can I convert the following simple formulas at Schedule!C20:I29 into a single, simple ARRAYFORMULA at Schedule!C20?
=Count(Filter(Students!$B$5:$B, Find(C6, Filter(Students!$J$5:$O,Students!$J$4:$O$4 = 'Current Class'!$B$3))))
.
NOTE:
The above code is only a partial solution. I will substitute the ARRAYFORMULA version of the code into the correct part of the code at Current Class!L6
The C6 reference above can take on any cell between Schedule!C6:I15. I have named that range Timetable_Code. I thought I could do the following, but I was wrong...
=Arrayformula(Count(Filter(Students!$B$5:$B, Find(Timetable_Code, Filter(Students!$J$5:$O,Students!$J$4:$O$4 = 'Current Class'!$B$3)))))
Background
Originally, I created a table that now resides at 1st Version - Current Class!L6. This tab is only for your reference and will be deleted soon. Each cell has a formula with a slight modification. This formula works correctly; however, it is a behemoth and would be hard to modify...
=if(COUNTIF(Meta!$B$5:$B, CONCATENATE("=",if(L$5 = "THURSDAY", "TH", if(L$5 = "SUNDAY", "SU", left(L$5,1))), if(left($K6, 2) = "12", 0, left($K6, 1)))), CONCATENATE(if(L$5 = "THURSDAY", "TH", if(L$5 = "SUNDAY", "SU", left(L$5,1))), if(left($K6, 2) = "12", 0, left($K6, 1)), " ( ", Count(Filter(Students!$B$5:$B, Find(CONCATENATE(if(L$5 = "THURSDAY", "TH", if(L$5 = "SUNDAY", "SU", left(L$5,1))), if(left($K6, 2) = "12", 0, left($K6, 1))), Filter(Students!$J$5:$O,Students!$J$4:$O$4 = $B$3)))), " )") ,"")
.
Pros
I don't have to create any helper data.
All calculations are "in-memory"
Cons
Too large
Hard to modify
I like the output, but I don't like the cons, so I started to create a more edit-friendly version of the code that I am mostly OK with. This code is located at Current Class!L6 (and a secondary copy at Schedule!C33 - it will be deleted.) It has a single formula at Current Class!L6...
=arrayformula(if(COUNTIF(Meta!$B$5:$B, ("=" & Timetable_Code)), (Timetable_Code & " ( " & Timetable_StudentCount & " )") ,""))
.
Pros
Very easy to understand
Very easy to modify
No need to copy formula over to other cells
Cons
Two ( 2 ) helper tables were created ( one of which I think is unneeded)
Again, I like the output, but I really don't like the second helper table (Schedule!C20). I feel like this table can be eliminated, but I have not been able to figure out how.
If you really want to use arrayformula, here it is. For Schedule!C20.
=arrayformula((len(concatenate(index(Students!J5:O, , match('Current Class'!$B$3, Students!J4:O4, 0))))-len(substitute(concatenate(index(Students!J5:O, , match('Current Class'!$B$3, Students!J4:O4, 0))),C6:I15,"")))/len(C6:I15))
Probably you can use filter(as you did before) instead of index & match part, but I prefer index & match and don't want to dig more. Also you can use one help cell to store filter or index & match result to shorten the formula.
The core idea is from counting occurrences of given character in a string, ie len(a1) - len(substitute(a1, .... You can find many documents about it in the net.
Anyway, if I were you, I'd be satified with the current state. Just lock and hide the help tables or sheets. Nobody cares hidden sheets and if something bad happens, you can revert any change.
After getting a good answer from #Sangbok Lee, I decided to break apart each part of the function he gave to me. While doing that I found a highly unlikely connection to some work I did in the Google Sheets last week. A helper column I had in another tab kind of did what Sangbok Lee was trying to do. All I had to do was split that helper column into two columns (1 for the previous final calculation, 1 for) and calculate an additional count column
After reworking both of our formulas, and testing the result, I found a solution that I am even more satisfied with!
=arrayformula(if(countif(Meta!$B$5:$B, (Timetable_Code)), (Timetable_Code & " ( " & vlookup(Timetable_Code, StudentCount_Lookup, 2, false) & " )") ,""))
.
Check out the differences in the Google Sheet
Look at 1st Version - Current Class!L6 tab for the 1st version
Look at Current Class!L6 for the 2nd version
Look at Current Class!U6 for the 3rd and final version
Also look at tab Meta and Schedule for the differences.
Note: Green is old data, Red is new data

Tkinter selecting text by index

I am implementing a search function in python with Tkinter and would like to select the first match it comes to. I have seen many examples with creating a tag_config to highlight the background of the indexed range, however I would like to select the text (the same way one would by clicking at the first index, then shift clicking the last index).
Thus far I have got both the start and end index of the area I need to select, I just don't know the command to "select" the text with that information.
My current code (that uses a highlight approach) is:
def search_command():
word = askstring("Search", "Enter word to search")
length = len(str(word))
pos = textPad.search(word, '1.0', stopindex=END)
row, col = pos.split('.')
endlen = int(col) + length
end = row + '.' + str(endlen)
textPad.tag_add("found", pos, end)
The "found" tag just highlights the background of the text rather than selecting it.
Any help with finding the correct function would be greatly appreciated.
The selection is defined by the "sel" tag. Apply that tag to the range of text you want selected:
textPad.tag_add("sel", pos, end)

How to compare matlab array with entries in a data structure

I am trying to write code in Matlab that will allow me to do the following. There is a part of the code that generates an array D and uses an input file to create this structure called EEG which contains a lot of information. Specifically I am interested in a "labels" field of the chanlocs field of the EEG structure. It contains entries like 'F7', 'F8', 'FP1'... and 17 such entries. The array D that is generated also contains entries like this but in a different order.
So for e.g. D = ['F7','F8', 'FP1'] and EEG.chanlocs.labels = ['FP1','F7','F8']
they contain the same entries but they are in a different order and for what I am trying to do the order is important.
What I basically want to do is to have Matlab scan all entries of D and find that particular index of EEG.chanlocs.labels to which that entry corresponds.
Example: If D(1) = 'F7' I want it to return for e.g. i = 2 because F7 is the 2nd entry in EEG.chanlocs.labels. In this way I want it to scan all of D and return the indices in EEG.chanlocs.labels.
What I have tried so far is:
for i=1:17
if any(strcmp(D(:),[EEG.chanlocs(i).labels]))
msgbox(sprintf('i is: %d',i));
else
msgbox(sprintf('Error'));
end
end
But it does not work and it returns weird things... I am not entirely sure what to try...
Can anybody help? Any help would be greatly appreciated!!
Thanks.
Edited:
The following code shows how I obtain D. I give the user 3 prompt windows to input certain data. I then store the inputs from each of these in "data" or "data2" or "data3" and then I put all of them together in D.
uiwait(msgbox(sprintf('Please enter your new references for each electrode.\nFor FP1, FP2, O1 and O2 provide two references.')));
prompt = {'Fp1','F7','T3','T5','O1'};
prompt2 = {'FP2','F8','T4','T6','O2'};
prompt3 = {'C3','CP3','Cz','CPz','C4','CP4'};
dlg_title = 'Input references';
num_lines = 1;
%def = {'20','hsv'};
answer = inputdlg(prompt,dlg_title,num_lines );
answer2 = inputdlg(prompt2,dlg_title,num_lines );
answer3 = inputdlg(prompt3,dlg_title,num_lines );
for i=1:5
data(i,:) = answer(i,:);
data2(i,:) = answer2(i,:);
end
for i=1:6
data3(i,:) = answer3(i,:);
end
D(1:5)=data(:);
D(6:10)=data2(:);
D(11:16)=data3(:);
D=D';

Resources