Parse a Boolean String Value: Truthy/Falsy Conventions Across Languages
“Parse a string into a boolean value” sounds trivial — just check if it equals “true” or “false.” In practice, configuration parsers, environment variable handling, JSON parsing, and CLI argument processing all need to handle “yes/no”, “1/0”, “T/F”, case variations, and edge cases. This guide covers the standard conventions, the language-specific quirks, and the safe parsing patterns interviewers expect when this question comes up.
The Problem
Given a string, return a boolean. Handle:
- Standard literals: “true”, “false”
- Case variations: “True”, “TRUE”, “FALSE”, “False”
- Numeric: “1”, “0”
- Yes/no: “yes”, “no”, “y”, “n”
- On/off: “on”, “off”
- Whitespace: ” true ” should still parse
- Edge cases: “”, “null”, “undefined”, non-boolean strings
Approach 1: Strict Match
Accept only exact “true” or “false” (lowercase, trimmed). Reject everything else.
def parse_bool_strict(s: str) -> bool:
"""Strict parse — only 'true' or 'false'."""
if s is None:
raise ValueError("None input")
s = s.strip().lower()
if s == "true":
return True
if s == "false":
return False
raise ValueError(f"Cannot parse '{s}' as boolean")
# Tests
print(parse_bool_strict("true")) # True
print(parse_bool_strict("True")) # True
print(parse_bool_strict("FALSE")) # False
# parse_bool_strict("1") # raises
Strict parsing is appropriate when the input format is well-defined (e.g., JSON booleans). Use this in API contracts where ambiguity should be an error.
Approach 2: Permissive Match (Common in Config Parsers)
Accept any common truthy / falsy variant. Default behavior in environment variable handling, CLI argument parsing, and most config libraries.
def parse_bool(s: str) -> bool:
"""Permissive parse for common truthy/falsy strings."""
if s is None:
return False
s = s.strip().lower()
if s in {"true", "t", "yes", "y", "on", "1"}:
return True
if s in {"false", "f", "no", "n", "off", "0", ""}:
return False
raise ValueError(f"Cannot parse '{s}' as boolean")
# Tests
print(parse_bool("yes")) # True
print(parse_bool("Off")) # False
print(parse_bool("1")) # True
print(parse_bool("")) # False (treated as falsy)
# parse_bool("maybe") # raises
Common in environment variable parsers (e.g., distutils.util.strtobool, python-dotenv, similar libraries in other languages).
Approach 3: Distutils-Style
Python’s standard library distutils.util.strtobool (deprecated in 3.12 but the pattern persists in many libraries) accepts a specific set:
def strtobool(s: str) -> int:
"""Mimics distutils.util.strtobool (returns 1 or 0)."""
val = s.strip().lower()
if val in ("y", "yes", "t", "true", "on", "1"):
return 1
elif val in ("n", "no", "f", "false", "off", "0"):
return 0
else:
raise ValueError(f"Invalid truth value {val!r}")
Language-Specific Conventions
Python’s bool(s)
Python’s built-in bool(s) on strings returns True for any non-empty string, even "false" and "0". This catches new Python developers off-guard:
print(bool("false")) # True (non-empty string)
print(bool("")) # False (empty string)
print(bool("0")) # True (non-empty)
Don’t use bool(s) for parsing user-provided strings; use a custom parser.
JavaScript’s Boolean(s) and !!s
Same trap: Boolean("false") is true because the string is non-empty. Same for !!s.
// JavaScript
console.log(Boolean("false")); // true
console.log(Boolean("")); // false
console.log(Boolean("0")); // true
JavaScript developers commonly use libraries (lodash’s toLower + custom logic, or JSON.parse for JSON-format booleans).
Java’s Boolean.parseBoolean
Java’s Boolean.parseBoolean(s) returns true only for the string “true” (case-insensitive); everything else is false:
// Java
Boolean.parseBoolean("true"); // true
Boolean.parseBoolean("True"); // true
Boolean.parseBoolean("yes"); // false
Boolean.parseBoolean("1"); // false
Boolean.parseBoolean(""); // false
Strict but doesn’t throw on invalid input. Some prefer Boolean.valueOf(s) which has identical semantics but returns Boolean object.
Go’s strconv.ParseBool
Go’s standard library accepts: 1, t, T, TRUE, true, True; 0, f, F, FALSE, false, False. Anything else returns an error.
// Go
v, err := strconv.ParseBool("yes") // error: invalid syntax
v, err := strconv.ParseBool("1") // true, nil
v, err := strconv.ParseBool("t") // true, nil
Rust’s str.parse::<bool>()
Rust accepts only “true” and “false” exactly (case-sensitive). Returns Err for anything else.
Common Mistakes
- Using language built-in
bool(s). In Python and JavaScript, this returns True for any non-empty string, including “false”. Always write a custom parser for user input. - Not handling None / null / empty. Decide explicitly: is None False, or an error? Empty string False, or an error? Document and stick to it.
- Inconsistent case handling. Some languages are case-insensitive (Java, Go); others aren’t (Rust). Pick one convention and apply consistently.
- Forgetting whitespace. ” true ” from environment variables or YAML files should parse correctly. Always trim before comparing.
- Returning the wrong type on error. Some implementations silently return False on invalid input; others raise. Document the contract; raise for production parsers where ambiguity is dangerous.
- Treating “0” as falsy in JSON contexts. JSON booleans are strictly “true” and “false”; “0” is a number. Mixing these conventions causes subtle bugs.
Common Variations
Three-state parsing (true / false / null)
Useful for “unknown” or “default” cases. Return Optional[bool] or three-state enum.
Locale-specific parsing
“Sí/No” (Spanish), “Oui/Non” (French), etc. Most production code sticks to English; locale-aware parsing is rare and error-prone.
JSON boolean parsing
Use json.loads(s) for JSON-format booleans. Strict; only “true” and “false” accepted.
YAML boolean parsing
YAML 1.1 accepts “yes”, “no”, “y”, “n”, “on”, “off” as booleans. YAML 1.2 narrowed to just “true”/”false”. Major source of bugs in YAML 1.1 parsers (the famous “Norway problem” — country code “NO” parses to false).
Frequently Asked Questions
What’s the expected interview answer?
Permissive parser: accept “true”/”false”, “yes”/”no”, “1”/”0″, “on”/”off”, case-insensitive, with whitespace trimming. Raise on unrecognized values. Walk through edge cases: None, empty string, “0” (truthy or falsy depending on convention). Strong candidates discuss the language-specific quirks (Python bool() returning True for “false”) and library-vs-manual trade-offs.
Why is bool(s) in Python wrong for parsing?
Because Python’s bool() on strings returns True for any non-empty string. bool("false") is True. bool("0") is True. bool("") is False. The only reliable Pythonic way is to compare against known truthy/falsy strings explicitly.
What’s the YAML “Norway problem”?
YAML 1.1 parses “NO” as a boolean false. Country code lists with “NO” (Norway) get silently converted to False. This caused real bugs in production YAML parsers. YAML 1.2 fixed it by restricting to “true”/”false”. Strong candidates know this anecdote when discussing config parsing pitfalls.
How do production parsers handle the choice between strict and permissive?
API responses (JSON): strict. Environment variables: permissive (accept “1”, “true”, “yes”). Config files: permissive. CLI flags: depends on framework (Click in Python is permissive; argparse with type=bool is broken — use a custom type=parse_bool). Document the choice; align with platform conventions.
Should I support locale-specific values?
Generally no. Stick to English. Programs talking to programs use English anyway. Programs talking to users typically rely on UI affordances (checkboxes, radio buttons), not free-text boolean parsing. Locale-specific parsing introduces bugs without much benefit.
See also: Remove a Character from a String • Detect Missing Country Code • Print String Permutations
💡Strategies for Solving This Problem
Expression Parsing and Evaluation
This is about parsing and evaluating boolean expressions from strings. Common at Google and Meta. Got a similar problem at Google in 2024.
The Problem
Given a string like "true AND false OR true", evaluate it to boolean result.
Complexity Levels
Level 1: Simple infix, no parentheses
Level 2: With parentheses
Level 3: With operator precedence (AND before OR)
Level 4: With NOT operator
Approach 1: Simple Left-to-Right
If no parentheses or precedence, evaluate left to right. Split by operators, process sequentially.
Approach 2: Recursive Descent
For precedence and parentheses, use recursive parsing:
- Parse expression
- Handle OR (lowest precedence)
- Handle AND (higher precedence)
- Handle NOT (highest precedence)
- Handle parentheses
Approach 3: Shunting Yard + Stack Evaluation
Convert infix to postfix (Reverse Polish Notation), then evaluate with stack. More complex but handles all cases.
Key Insight
Operator precedence matters! "true OR false AND false" depends on precedence:
- Left-to-right: (true OR false) AND false = false
- AND first: true OR (false AND false) = true
Standard: AND has higher precedence than OR.
At Google
Started with simple tokenization and left-to-right evaluation. Interviewer added parentheses, then asked about precedence. Had to rebuild with recursive parser.