google-sheetsgoogle-apps-script

How do I filter rows by whether or not certain columns have values?


I'm trying to filter rows by if certain columns aren't empty. I am determining those "certain columns" with a control row of true or false values. How can I do this with formula only (not custom functions) for speed and to keep the images/formulas working when the data spreads through the sheet?

Continue reading for more context and better a wording of the question.

EDIT: Follow this link for a copy of the actual Spreadsheet.

The Spreadsheet

I have a Google Spreadsheet with a sheet that has a table of characters in a video game. It looks something like this:

Sheet 1

Name Title Uses Mana Is Melee Is Ranged Assassin Tank
Ezra the Wanted ⚔️ 🛡️
Joel the Priest 💧 🏹
Kat the Hidden 💧 ⚔️ 🥷

Each of the icons used to indicate a true value, however, is a formula pointing to a cell with the appropriate image.

Sheet 1

Name Title Uses Mana Is Melee Is Ranged Assassin Tank
=image(💧) =image(⚔️) =image(🏹) =image(🥷) =image(🛡️)
Ezra the Wanted =icon above =icon above
Joel the Priest =icon above =icon above
Kat the Hidden =icon above =icon above =icon above

The next sheet in the Spreadsheet is supposed to make it easy to filter rows by using checkboxes:

Sheet 2

Name Title Uses Mana Is Melee Is Ranged Assassin Tank
=image(💧) =image(⚔️) =image(🏹) =image(🥷) =image(🛡️)
Clear: 🟦 <⏹️> 🟦 🟦 🟦 🟦
Joel the Priest =icon above =icon above
Kat the Hidden =icon above =icon above =icon above
_

Note: Ezra is gone because Uses Mana was selected in the filters.

The Problem

I got it to do what I want with a custom function, but it's very slow (there are a lot of characters in the game) and doesn't keep the images:

=filterRows("'Sheet 1'!A3:G", 'Sheet 2'!C3:G3, 2)

Note: I ignore the first two columns in the [filterrow] because I still need to include the Name and Title, but they'll never be filtered and the cells above can't just be left as "TRUE" checkboxes.

/**
 * Filters rows in a range by a control row containing boolean values indicating whether or not to check that column of each row for data.
 * 
 * @param {"A2:5"} rows A string in A1 notation indicating the desired range to filter.
 * @param {A1:A5} filterrow A range containing a single row of boolean values.
 * @param {2} offset The number of columns to ignore from the start of each row in [rows]. The first value will be compared with the first value in [filterrow].
 * @param {1, 3, ...} ...ignorecolumns The values to always treat as true in [filterrow]. 1 is the first value in the row.
 * @return The rows that had values in the matching true columns of [filterrow].
 * @customfunction
 */
function filterRows(rows, filterrow, offset = 0, ...ignorecolumns) {
    rows = SpreadsheetApp.getActiveSpreadsheet().getRange(rows);
    let data = [];
    for(var i = 1; i <= rows.getNumRows(); i++) {
        let matches = 0
        let match = 0;
        filterrow[0].forEach((filter, n) => {
            if(!ignorecolumns.includes(n+1) && filter) {
                matches++;
                if(rows.getCell(i, n+1+offset).getValue() != "") match++;
            }
        });
        if(matches == match) {
            let row = [];
            for(var n = 1; n <= rows.getNumColumns(); n++) {
                let cell = rows.getCell(i, n);
                row.push(cell.getFormula() || cell.getValue());
            }
            data.push(row);
        }
    }
    if(data.length == 0) return "No match";
    return data;
}

The Question

Is it possible to achieve the same effect as the custom function above with formula only?

Basically, If I have two sheets like this:

Sheet 1

Name Title Uses Mana Is Melee Is Ranged Assassin Tank
=image(💧) =image(⚔️) =image(🏹) =image(🥷) =image(🛡️)
Ezra the Wanted =icon above =icon above
Joel the Priest =icon above =icon above
Kat the Hidden =icon above =icon above =icon above

Sheet 2

Name Title Uses Mana Is Melee Is Ranged Assassin Tank
=image(💧) =image(⚔️) =image(🏹) =image(🥷) =image(🛡️)
Clear: 🟦 🟦 🟦 🟦 🟦 🟦
=filter(...)
_
_

How can I use formula, in cell A4 (=filter(...)), instead of a custom function, to filter the rows in Sheet 1 by which columns, marked in the checkboxes of Sheet 2, aren't empty?

It's a very niche need, I know. Any help would be appreciated!


Solution

  • You may try:

    =ifna(filter(All!A7:AG,byrow(filter(All!E7:AG,E6:AG6),lambda(Σ,countif(Σ,"")=0))),"-")
    

    enter image description here