Forum Discussion
Filter Function to be optional depending on blank and non blank values
Jn12345
Without seeing your data structure, it is hard to say, but here is a general approach that I refer to as "masking." Rather than using filter like filter(target_array, filter_array=filter_value), you do let(filter_mask, filter_array=filter_value, filter(target_array, filter_mask). In this way, you are giving the filter function an array of boolean values (true or false) rather than asking it to evaluate whether some comparison/operation is true or false.
The reason this is useful is because in your let formula, you can pick and name each of your three (or four or five) filter columns, pick and name each of your filter values, and then use boolean functions to combine them. So (f_array_1 = "blue")*(f_array_2>5) is the same thing as saying "tell me which rows in these two arrays have "blue" in the first array and a number greater than 5 in the second. If you want to combine the filters with "and", but you haven't yet decided what the filter value should be, you can just multiply your f_array_1 mask by TRUE to have it return true for all rows satisfying your one filter. The formula below basically says, if there is a value in any one of these three columns in the current row, use that value for a filter mask on the specified column, and return all values in the target_array where it satisfies all of the required filters. This means that the first, second, or third filter values can be blank or have a value and the filtered array returned will be everything (if no values supplied) all the way down to nothing (if no value meets all of the criteria you have specified). So you can fill in the third value first, last, or second - it makes no difference. Your filtered list will always represent the values that meet all of the specified criteria.
If you prefer that your filter be that the return list simply meets one of the filter criteria (i.e. f1 or f2 or f3), then you can add the filter masks instead of multiplying them. You can also do whatever other boolean algebra you want on them.
You can change the names to your preference or add/remove filter columns and values. You must, however, properly reference the cells where your filter values are, the ranges where your filter columns and target columns are, and your operators for each filter (=, <>, >, <, etc.). The isblank can be temperamental if the cell referred to is not truly blank (has no value whatever) but is something like an empty string, i.e. =""
let(
value_array_for_list, SERIAL_NUMBERS_REF,
first_column_to_filter_by, PRODUCT_TYPE_COLUMN_REF,
second_column_to_filter_by, PRODUCT_STYLE_COLUMN_REF,
third_column_to_filter_by, PRODUCT_COLOR_COLUMN_REF,
value_to_filter_first_column_by, [@[type]],
value_to_filter_second_column_by, [@[sytle]],
value_to_filter_third_column_by, [@[color]],
first_column_mask, if(isblank(value_to_filter_first_column_by), TRUE, value_to_filter_first_column_by = first_column_to_filter_by),
second_column_mask, if(isblank(value_to_filter_second_column_by), TRUE, value_to_filter_second_column_by = second_column_to_filter_by),
third_column_mask, if(isblank(value_to_filter_third_column_by), TRUE, value_to_filter_third_column_by = third_column_to_filter_by),
combined_mask, first_column_mask*second_column_mask*third_column_mask,
filtered_serial_numbers, filter(value_array_for_list, combined_mask, "No matching serial numbers"),
filtered_serial_numbers
)
- joelb95Sep 24, 2024Brass Contributor
I spent way more time on this than I should have, but I think there isn't a great answer due to validation limitations. My final suggestion to you is in some ways a poor second to one that PeterBartholomew1 appears to have come up with involving using the cell() formula to return the address of the last active cell for purposes of setting your filters and generating your validation arrays. What I would have done is created a helper column that requires you to check the row that you want to validate in order to update your filter arrays.
In any event, here is a file that has some of what I tried for you. You could, theoretically, adapt my formulas to your purpose relatively easily and expand them as necessary.validation_filter=lambda( choice, equip_val, make_val, model_val, serial_val, =LET( equip_col, tbl_equipment_list[Equipment Type], make_col, tbl_equipment_list[Make], model_col, tbl_equipment_list[Model], serial_col, tbl_equipment_list[Serial Number], mask_func, LAMBDA(value, column, IF(ISBLANK(value), EXPAND(TRUE, ROWS(column), 1, TRUE), value = column) ), equip_mask, mask_func(equip_val, equip_col), make_mask, mask_func(make_val, make_col), model_mask, mask_func(model_val, model_col), serial_mask, mask_func(serial_val, serial_col), combined_mask, equip_mask * make_mask * model_mask * serial_mask, no_box_checked, IFERROR( AND(NOT(equip_val), NOT(make_val), NOT(model_val), NOT(serial_val)), FALSE ), equip_choices, UNIQUE(FILTER(equip_col, combined_mask, "")), make_choices, UNIQUE(FILTER(make_col, combined_mask, "")), model_choices, UNIQUE(FILTER(model_col, combined_mask, "")), serial_choices, UNIQUE(FILTER(serial_col, combined_mask, "")), pick_choice_list, LAMBDA(choice, CHOOSE(choice, equip_choices, make_choices, model_choices, serial_choices) ), return_all, LAMBDA(choice, CHOOSE(choice, equip_col, make_col, model_col, serial_col)), result, IF(no_box_checked, return_all(choice), pick_choice_list(choice)), result )) valid_makes=XLOOKUP("x", tbl_results[filter_row], tbl_results[make], FALSE) valid_model=XLOOKUP("x", tbl_results[filter_row], tbl_results[model], FALSE) valid_serial_numbers=XLOOKUP("x", tbl_results[filter_row], tbl_results[serial_number], FALSE) valid_types=XLOOKUP("x", tbl_results[filter_row], tbl_results[type], FALSE)
- Jn12345Oct 03, 2024Brass ContributorLegend, Thank you! I wasn't able to do everything I wanted but this definitely set me in the right direction! thank you a ton.
- Jn12345Sep 23, 2024Brass Contributor
Hey, Thanks for the response. Basically what i have are different tables that will have various pieces of equipment in them. See attached. just for an example the headers for calibration information are "Equipment type" "Curvature" "Thickness" "Serial Number" "Material" and then i have four rows so that i can add 4 separate pieces of equipment.
I have a picture of how i created the spill ranged in a hidden sheet where each of the values seen in the formula reference a master list of equipment with similar headings. please see attached.
Basically i have them all blank at the start aside from the "Equipment type" column. then when they put that info in, they then have filtered options where the curvature column list is now available to select from based on equipment filtered by the equipment type, then when they put a curvature the thickness options are available based on filtering by selected Equip type, and curvature. Then the serial number based on those etc. Basically I would like the used to be able to do things out of order for flexibility. so if they type in the thickness first, all other columns will be updated to filter for only those thicknesses, then say they pick from the curvature, then they rest of the column lists will filter in the fact that they are looking for what they are supposed to be looking for based on those two filter constraints etc. Does this make sense? im probably doing a terrible job explaining something that should be pretty simple