cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Browse apps to extend the software in the new JMP Marketplace
Choose Language Hide Translation Bar
FN
FN
Level VI

Searching elements in listbox

I would like to know if there is an easy way to find elements in GUI having a Listbox.

 

Something like the column search filter but for a Listbox.

1 ACCEPTED SOLUTION

Accepted Solutions
pmroz
Super User

Re: Searching elements in listbox

This solution will update the listbox with every character you type.  First shown to me by Justin Chilton.

filter_items = Function( {this, searchText},
	{filtered_items, i},
	// only attempt to filter if there is any text
	If( searchText != "",
		// new list for groups that match searchText
		filtered_items = {};
		// Check if each group matches the given text
		For( i = 1, i <= N Items( item_list ), i++,
			// Insert to our list if it contains our search text (case insensitive)
			If( Contains( Lowercase( item_list[i] ), Lowercase( searchText ) ),
				Insert Into( filtered_items, item_list[i] );
			)
		);
		,
	// else show all groups
		filtered_items = item_list;
	);
	item_disp_box << Set Items( filtered_items );
);		// end filter_items function

item_list = {"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida",
"Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine",
"Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska",
"Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio",
"Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas",
"Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"};

nw = new window("Demonstrate Listbox Search",
	hlistbox(
		icon box("SearchIndex"),
		spacer box(size(5, 10)),
		item_search_teb = text edit box("", 
				<< set width(300),
// The filter_items function does the work
				<< set text changed(filter_items)),
		reset_button = button box("", 
				<< set icon("DebuggerDeleteBreakpoint"),
				<< set script(
// Clear the filter and call the callback function
				item_search_teb << set text("");
				filter_items(item_search_teb, "");
				),
				<< set tip("Clear filter"),
		),
	),
	panelbox("Select a state:",
		item_disp_box = listbox(item_list, width(200), nlines(10)),
	),
)

pmroz_0-1619614212375.png

pmroz_1-1619614242680.png

 

View solution in original post

9 REPLIES 9
txnelson
Super User

Re: Searching elements in listbox

Here is a simple example of a search in a List Box().

Names Default To Here( 1 );
New Window( "Example",
	fontobj = lb = List Box(
		{"First Item", "Second Item", "Third Item"},
		width( 200 ),
		max selected( 2 ),
		nlines( 6 )
	)
);
show(contains(lb<<get items,"Second Item"))

If you can provide more details, a more exacting answer may be available

Jim
FN
FN
Level VI

Re: Searching elements in listbox

Thanks. I am after an interactive text box. For example, when you type "Sec" the list removes elements and shows only "Second Item".
ian_jmp
Level X

Re: Searching elements in listbox

Building on @txnelson thoughts:

NamesDefaultToHere(1);

lbi = {"First Item", "Second Item", "Second Item Duplicate", "Third Item"};

New Window( "Searchable List Box",
	PanelBox("Type in the box below and hit Enter to select matching list items",
			teb = TextEditBox("", << setScript(updateLB), << setWidth(200)),
			lb = List Box(lbi, << setWidth(200))
			)
	);

updateLB =
Expr(
	txt = teb << getText;
	lbiNew = lbi;
	if(txt != "",
		// Remove non-matching items according to some logic
		for(i = NItems(lbi), i>= 1, i--, if(!StartsWith(lbi[i], txt), RemoveFrom(lbiNew, i)));
		);
	// Set new items
	lb << setItems(lbiNew);
);
pmroz
Super User

Re: Searching elements in listbox

This solution will update the listbox with every character you type.  First shown to me by Justin Chilton.

filter_items = Function( {this, searchText},
	{filtered_items, i},
	// only attempt to filter if there is any text
	If( searchText != "",
		// new list for groups that match searchText
		filtered_items = {};
		// Check if each group matches the given text
		For( i = 1, i <= N Items( item_list ), i++,
			// Insert to our list if it contains our search text (case insensitive)
			If( Contains( Lowercase( item_list[i] ), Lowercase( searchText ) ),
				Insert Into( filtered_items, item_list[i] );
			)
		);
		,
	// else show all groups
		filtered_items = item_list;
	);
	item_disp_box << Set Items( filtered_items );
);		// end filter_items function

item_list = {"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida",
"Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine",
"Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska",
"Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio",
"Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas",
"Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"};

nw = new window("Demonstrate Listbox Search",
	hlistbox(
		icon box("SearchIndex"),
		spacer box(size(5, 10)),
		item_search_teb = text edit box("", 
				<< set width(300),
// The filter_items function does the work
				<< set text changed(filter_items)),
		reset_button = button box("", 
				<< set icon("DebuggerDeleteBreakpoint"),
				<< set script(
// Clear the filter and call the callback function
				item_search_teb << set text("");
				filter_items(item_search_teb, "");
				),
				<< set tip("Clear filter"),
		),
	),
	panelbox("Select a state:",
		item_disp_box = listbox(item_list, width(200), nlines(10)),
	),
)

pmroz_0-1619614212375.png

pmroz_1-1619614242680.png

 

ian_jmp
Level X

Re: Searching elements in listbox

Nice! I didn't know about '' << setTextChanged()'.

carole
Level III

Re: Searching elements in listbox

Hello, thanks for the solution but it doesn't work when we have several listboxes in the same window. Could you please help me ? I would like to have a generic function to replace listbox() native function with such a filter. I tried with {DefaultLocal} into my function, but it doesn't work. I would like to add an argument to the filter_items() function but I can't. Where am I wrong ?

filter_items = Function( {this, searchText},
	{filtered_items, i},
	// only attempt to filter if there is any text
	If( searchText != "",
		// new list for groups that match searchText
		filtered_items = {};
		// Check if each group matches the given text
		For( i = 1, i <= N Items( item_list ), i++,
			// Insert to our list if it contains our search text (case insensitive)
			If( Contains( Lowercase( item_list[i] ), Lowercase( searchText ) ),
				Insert Into( filtered_items, item_list[i] );
			)
		);
		,
	// else show all groups
		filtered_items = item_list;
	);
	item_disp_box << Set Items( filtered_items );
);		// end filter_items function

listbox_with_filter = Function( {item},
	listbox = 
	vlistbox(
		hlistbox(
			icon box("SearchIndex"),
			spacer box(size(5, 10)),
			item_search_teb = 
				text edit box("", 
					<< set width(300),
					// The filter_items function does the work
					<< set text changed(filter_items )
				),
			reset_button = 
				button box("", 
					<< set icon("DebuggerDeleteBreakpoint"),
					<< set script(
						// Clear the filter and call the callback function
						item_search_teb << set text("");
						filter_items(item_search_teb, "");
					),
					<< set tip("Clear filter"),
				),
		),
		panelbox("Select a state:",
			item_disp_box = listbox(item, width(200), nlines(10)),
		)
	);
	return( listbox );
);

item_list1 = {"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida",
"Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine",
"Maryland", "Massachusetts", "Michigan", "Minnesota"};

item_list2 = {"Mississippi", "Missouri", "Montana", "Nebraska",
"Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio",
"Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas",
"Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"};

nw = new window("Two Listbox Search",
	HListBox(
		listbox_with_filter( item_list1 )
	,
		listbox_with_filter( item_list2 )
	)
);
jthi
Super User

Re: Searching elements in listbox

How I build these changes case by case, but using Filter Col Selector is fairly simple (usually I don't have to worry about the private tables, but I don't know your use case, so I did some trickery for them)

Names Default To Here(1);

create_filter = function({items}, {Default Local},
	dt = New Table("",
		New Column("a", Character, Values(items)),
		private
	);

	dt_split = dt << Split(
		Split By(:a),
		Split(:a),
		Output Table("temp"),
		Sort by Column Property,
		private 
	);
	Close(dt, no save);
	Eval(EvalExpr(
		fcs = Filter Col Selector(dt_split,  << Set Script(
			Expr(dt_split); // not sure how robust this will be, used to store the private table reference
		));	
	));
	return(fcs);
);

item_list1 = {"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida",
"Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine",
"Maryland", "Massachusetts", "Michigan", "Minnesota"};

item_list2 = {"Mississippi", "Missouri", "Montana", "Nebraska",
"Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio",
"Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas",
"Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"};

nw = New Window("demo",
	H List Box(
		a = create_filter(item_list1),
		b = create_filter(item_list2)
	)
);

You can also use V List Box and build it using Text Edit Box (you want to store the original list somewhere)

Names Default To Here(1);

create_filter = function({items}, {Default Local},
	Eval(EvalExpr(
			vlb = V List Box(
			Text Edit Box("", <<Set Text Changed(Function({this, value},
				all_items = Expr(items);
				new_list = Filter Each({name}, all_items, Contains(lowercase(name), lowercase(value)));
				If(N Items(new_list) == 0 & IsMissing(value),
					new_list = all_items;
				);
				(this << sib) << set items(new_list)
			))),
			List Box(items)
		);
	));
	
	return(vlb);
);

item_list1 = {"Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida",
"Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine",
"Maryland", "Massachusetts", "Michigan", "Minnesota"};

item_list2 = {"Mississippi", "Missouri", "Montana", "Nebraska",
"Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio",
"Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas",
"Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"};

nw = New Window("demo",
	H List Box(
		vlb1 = create_filter(item_list1),
		vlb2 = create_filter(item_list2)
	)
);

and there are also other methods depending on the application and use case.

 

-Jarmo
hogi
Level XII

Re: Searching elements in listbox

With a Combo Box you can combine both feature in one Display Box - a freely editable text edit box and a list which is linked to it:

hogi_0-1698172932450.png

 

suggestions={"some","values","to","pick"};

new window("test", cb = Combo Box( suggestions, editable,<<Set Width( 300 ) ))
carole
Level III

Re: Searching elements in listbox

Thank you very much for the two solutions !

I prefer the one with texteditbox because when we have lots of listboxes, I am afraid about the high number of private tables that won't be closed (memory charge).

Thanks a lot it's perfect for me the second solution;

Carole