So I need to add robust user controlled filtering options in the application I am working on. This problem has dogged me for a month or two, with my co-workers politely insisting on more and better ways to filter their data. This is completely understandable. When you are staring down a datagrid that is virtually 12 feet long, getting to what you actually want to see is important.
This 2006 article from Bruce Phillips was a big help in the basic concept of multiple input filtering. As is his normal style, it is littered with good references to the source material from his research. This is only considering two filter parameters, however, and I need to consider n parameters, that will probably shift as the needs of the end user grow.
The problem is that each new filtering parameter exponentially increases the complexity of the filterFunction. I can see the pattern, but I haven’t quite reached the epiphany that I need to break it down into one of those tight little functions I see real programmers creating (unfortunately, I haven’t seen any that address my specific problem).
I thought I’d share my naive, and perhaps confusing filterFunction. It would be great to hear how others approach this problem, and come up with a concise solution.
This function utilizes a TextInput to get text search input from the user. It uses
a series of CheckBoxes for various status condition to generate an array [ 1, 0, 1, 1 ]
to represent their on/off state. It also uses two combo boxes to filter by staff members
as well as clients.
filterFunction:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | private function filterCases(item:CaseVO):Boolean { //is the item visible or hidden var result:Boolean = false; //array of CheckBox.selected property values // in handy ones and zeroes. This is returned from a function // that takes the actual CheckBoxes as input and gives me this. var case_mask:Array = [ 1, 0, 1, 1 ]; //a couple VOs to verify the selectedItem in the ComboBoxes. var client:PersonVO; var expert:PersonVO; //array iteration variable var i:int; var dateFormat:DateFormatter = new DateFormatter( ) dateFormat.formatString = 'MM/DD/YY' //text search if ( caseGrid.filter.caseNumInput.text.length > 0 ) { var searchResult:Boolean = ( ( String( '0' + item.case_id.toString() ).search( caseGrid.filter.caseNumInput.text ) >= 0 ) || ( item.style.toLowerCase().search( caseGrid.filter.caseNumInput.text.toLowerCase() ) >= 0 ) ); if( item.description ) { searchResult = searchResult || ( item.description.toLowerCase().search( caseGrid.filter.caseNumInput.text.toLowerCase() ) >= 0 ) } if ( item.client_id ) { searchResult = searchResult || ( personProxy.personFromId( item.client_id ).fullName.toLowerCase().search( caseGrid.filter.caseNumInput.text.toLowerCase() ) >= 0 ) } if ( item.date_of_accident ) { searchResult = searchResult || ( dateFormat.format(item.date_of_accident).search( caseGrid.filter.caseNumInput.text ) >= 0 ) } for ( i = 0; i < case_mask.length; i++ ) { if ( caseGrid.filter.client.selectedItem is PersonVO ) { client = caseGrid.filter.client.selectedItem //is an expert selected for filtering also? if ( caseGrid.filter.expert.selectedItem is PersonVO && searchResult) { expert = caseGrid.filter.expert.selectedItem; //filter for client, expert, and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.client_id == client.id && item.expert_id == expert.id && searchResult ) result = true; } else { //filter for client and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.client_id == client.id && searchResult ) result = true; } } else if ( caseGrid.filter.expert.selectedItem is PersonVO ) { expert = caseGrid.filter.expert.selectedItem; for ( i = 0; i < case_mask.length; i++ ) { //filter for expert and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.expert_id == expert.id && searchResult ) result = true } } else { //filter for client and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && searchResult ) result = true; } } } //see if a client is selected for filtering else if ( caseGrid.filter.client.selectedItem is PersonVO ) { client = caseGrid.filter.client.selectedItem for ( i = 0; i < case_mask.length; i++ ) { //is an expert selected for filtering also? if ( caseGrid.filter.expert.selectedItem is PersonVO ) { expert = caseGrid.filter.expert.selectedItem; //filter for client, expert, and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.client_id == client.id && item.expert_id == expert.id ) result = true; } else { //filter for client and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.client_id == client.id ) result = true; } } } //client isn't selected, check the expert filter. else if ( caseGrid.filter.expert.selectedItem is PersonVO ) { expert = caseGrid.filter.expert.selectedItem; for ( i = 0; i < case_mask.length; i++ ) { //filter for expert and status if ( (case_mask[i] && item.status == String( i + 1 ) ) && item.expert_id == expert.id ) result = true } } //finally, filter for the status else { for ( i = 0; i < case_mask.length; i++ ) { //filter for status if ( case_mask[i] && item.status == String( i + 1 ) ) result = true; } } return result } |
You can see that I am repeating myself from the top down to the final else statement, cutting the filter parameters down like a layer cake. It works, and is relatively quick on a grid with 1500 or so items. This just screams “REFACTOR ME” every time I see it.
The Complex DataGrid filterFunction in Flex/Air by Joel Hooks, unless otherwise expressly stated, is licensed under a Creative Commons Attribution 3.0 United States License.