Skip to content

Boolean Variants → Canonical true/false

View on GitHub

What

Fold boolean columns written every which way — Yes/No, Y/N, 1/0, true/false, TRUE/T/F, mixed case — into a canonical true/false, with blanks and unrecognised junk left empty.

Synthetic / teaching example

The data here is constructed, not sourced — sample.csv plants one of each boolean spelling plus a blank and a junk value. The problem class is real and universal; the rows are not.

Why interesting

"Boolean" is the least standardised column type in practice: every system invents its own truthy/falsy spelling, and a join or filter on the raw text silently splits Yes from Y from true. Normalising to one representation is routine — but doing it correctly runs into two numeric coercion traps worth knowing.

flowchart TD
    X["raw cell"] --> G{"LEN(TRIM) = 0?"}
    G -->|yes| E["(empty)<br/><small>blank stays blank</small>"]
    G -->|no| L["LOWER(TRIM)"]
    L --> T{"IN truthy?<br/><small>yes·y·1·true·t</small>"}
    T -->|yes| TR["true"]
    T -->|no| F{"IN falsy?<br/><small>no·n·0·false·f</small>"}
    F -->|yes| FA["false"]
    F -->|no| U["(empty)<br/><small>junk dropped</small>"]

Problem class documented in. (sources for the problem class — not for the data)

The trick

(see inline comments in sample.json)

LOWER(TRIM(...)) then IN(...) against the truthy / falsy spellings. The two traps, both solved by guarding blanks with ISEMPTY([Active]) first:

  1. Blank vs '0'. bxp coerces an empty cell to 0 and the literal '0' to 0, so a blank would wrongly match the '0' in the falsy list and become false. Guarding blanks up front keeps them empty.
  2. The guard itself. A bare TRIM([Active]) = '' also coerces ("0"→0 == ""→0), so it would wrongly treat a real "0" as blank. ISEMPTY compares the trimmed length — no coercion — so "0" (length 1) survives to be read as false. Compare the two on show all: the 0 row is where they disagree.

Final result

Ten spellings collapse to three states; the 0 is a real false, the blank stays blank, and the junk unknown is dropped to empty:

Yes / TRUE   →  true  / true
N   / 0      →  false / false
1   / false  →  true  / false
(blank) / unknown → (empty) / (empty)

Now a WHERE active = 'true' filter catches every truthy spelling at once.

Sample data

Run it with bxp-cli --config ./sample.json --template boolean_variants_clean:

{
  // Teaching example — synthetic data. Boolean columns arrive in every spelling
  // imaginable: Yes/No, Y/N, 1/0, true/false, TRUE/T/F, mixed case, plus blanks
  // and junk. This template folds them to a canonical "true"/"false" (blank and
  // unrecognised → ""), and shows the two numeric-coercion traps that make a
  // naive version wrong.
  conversion_templates: {
    boolean_variants_clean: {
      data_dir:          ".",
      file_pattern_in:   ".csv",
      file_pattern_out:  ".csvx",

      input_schema: {
        $account: "[Account]",

        // THE TRICK — LOWER+TRIM, then IN() against the truthy / falsy spellings.
        //
        // Two coercion traps, both fixed by the ISEMPTY() guard:
        //   * empty cell vs the '0' in the falsy list — bxp coerces ""→0 and
        //     '0'→0, so a blank would WRONGLY match '0' and become "false";
        //   * a bare [x] = '' guard would ALSO coerce "0"→0 == ""→0 and wrongly
        //     treat a real "0" as blank. ISEMPTY([x]) tests the trimmed length
        //     (no coercion): blank / whitespace → true, "0" → false.
        // So: guard blanks with ISEMPTY first, THEN it's safe to keep '0' as falsy.
        $active:   "IF(ISEMPTY([Active]), '', IF(IN(LOWER(TRIM([Active])), 'yes', 'y', '1', 'true', 't'), 'true', IF(IN(LOWER(TRIM([Active])), 'no', 'n', '0', 'false', 'f'), 'false', '')))",
        $verified: "IF(ISEMPTY([Verified]), '', IF(IN(LOWER(TRIM([Verified])), 'yes', 'y', '1', 'true', 't'), 'true', IF(IN(LOWER(TRIM([Verified])), 'no', 'n', '0', 'false', 'f'), 'false', '')))"
      },

      row_rules: [ { when: "1 = 1", rows: [ {} ] } ],

      output_schema: {
        account:  "$account",
        active:   "$active",
        verified: "$verified"
      }
    }
  }
}
Account,Active,Verified
A001,Yes,TRUE
A002,N,0
A003,1,false
A004,true,Y
A005,No,F
A006,,unknown
account,active,verified
A001,true,true
A002,false,false
A003,true,false
A004,true,true
A005,false,false
A006,,