DEV Community

Shahrouz Nikseresht
Shahrouz Nikseresht

Posted on

Day 35: Python Morse Code Generator, Convert English Text to Morse with Full A-Z Mapping and Interactive Input

Welcome to Day 35 of the #80DaysOfChallenges journey! This intermediate challenge focuses on building a simple Morse code generator that translates English text to Morse signals, supporting the full alphabet from A to Z through a dictionary lookup, while managing user input in an interactive loop for repeated conversions. It incorporates clean string processing to handle words and letters with appropriate spacing, plus a placeholder for unknown characters, making it a robust utility for text encoding. This exercise strengthens skills in dictionary usage for mappings, string manipulation for case and splits, and loop-based interactivity with exit conditions, which are essential for creating user-friendly command-line tools or encoding apps. If you're advancing from basic dicts to more integrated scripts or need practice in modular translation logic, this "Python Morse code generator" walkthrough details a function that's extensible, perhaps to include numbers or punctuation, and demonstrates real-world handling of inputs like mixed case or unsupported chars.


💡 Key Takeaways from Day 35: Morse Code Generation Function

This project centers on a core function for translation, backed by a predefined dictionary for Morse codes, and wrapped in an interactive main block for user engagement. It's a well-rounded example of data-driven processing: define a mapping, process input step-by-step, output formatted results. We'll explore it thoroughly: dictionary setup for A-Z codes, function logic with case handling, splitting, and placeholders, interactive loop for repeated use, along with discussions on design choices for efficiency, readability, and potential expansions.

1. Morse Mapping: Comprehensive A-Z Dictionary

The foundation is a static dictionary that maps each uppercase letter to its Morse equivalent:

# International Morse code mapping A-Z
MORSE_MAPPING: Dict[str, str] = {
    "A": ".-",    "B": "-...",  "C": "-.-.", "D": "-..",
    "E": ".",     "F": "..-.",  "G": "--.",  "H": "....",
    "I": "..",    "J": ".---", "K": "-.-",  "L": ".-..",
    "M": "--",    "N": "-.",   "O": "---",  "P": ".--.",
    "Q": "--.-",  "R": ".-.",  "S": "...",  "T": "-",
    "U": "..-",   "V": "...-", "W": ".--",  "X": "-..-",
    "Y": "-.--",  "Z": "--.."
}
Enter fullscreen mode Exit fullscreen mode

Using from typing import Dict adds type hints for clarity, hinting that keys and values are strings. This dict is efficient for O(1) lookups, far better than if-elif chains for 26 cases, and it's easily extensible, say by adding numbers ("0": "-----") or symbols. The codes follow international standards, with dots and dashes only, no extras like prosigns. By sticking to uppercase keys, it sets up for input normalization, ensuring consistency. This structure is reusable, perhaps in a reverse decoder function where you'd map codes back to letters.

2. Generation Function: Input Processing and Formatting

The generate_simple_morse function takes text and an optional placeholder, returning the encoded string:

def generate_simple_morse(text: str, unknown_placeholder: str = "?") -> str:
    """
    Translate a text string into Morse code using A-Z dictionary mapping.
    Converts characters to uppercase before processing for consistent lookup.
    Replaces unsupported characters with a configurable placeholder symbol.
    Splits the input into words, then encodes each letter individually.
    Joins encoded letters with single spaces and words with double spaces.
    """
Enter fullscreen mode Exit fullscreen mode

Start with normalization:

# Convert to uppercase to match mapping keys
text = text.upper()
words = text.split()  # split into words by whitespace
morse_words = []
Enter fullscreen mode Exit fullscreen mode

This upper() makes it case-insensitive, so "Hello" becomes "HELLO" for lookups. split() handles multiple spaces or tabs as one, treating the input as space-separated words.

Then, encode per word:

for word in words:
    morse_letters = []
    for ch in word:
        morse_letters.append(MORSE_MAPPING.get(ch, unknown_placeholder))
    # join letters with single space
    morse_words.append(" ".join(morse_letters))
Enter fullscreen mode Exit fullscreen mode

For each char, get(ch, "?") fetches the code or defaults to "?", gracefully ignoring punctuation or numbers. The inner list collects codes, joined by single spaces for letter separation, per Morse conventions.

Finally:

# join words with two spaces
return "  ".join(morse_words)
Enter fullscreen mode Exit fullscreen mode

Double spaces distinguish words. For "SOS", it yields "... --- ...", readable and standard. The placeholder param allows customization, like "" to skip unknowns. This nested loop is clear, O(n) for text length, and avoids recursion for simplicity. Choices like split/join favor string methods over manual indexing, boosting readability without sacrificing performance for short inputs.

3. Interactive Main: User Loop with Exit

The script runs interactively under __name__ == "__main__", for standalone execution:

print("\n📡 Simple Morse Code Generator (A-Z) 🚀\n")
while True:
    user_input = input("Enter a sentence (or type 'q' to quit): ").strip()
    if user_input.lower() == "q":
        print("\n👋 Goodbye!\n")
        break
    morse = generate_simple_morse(user_input)
    print(f"\nMorse Code: {morse}\n")
Enter fullscreen mode Exit fullscreen mode

strip() cleans whitespace from input. The lower() == "q" exit is case-insensitive. This loop enables multiple conversions without restarts, user-friendly for testing phrases. Emojis add visual flair, but could be toggled. It handles empty inputs by producing empty Morse, or unknowns with "?", showing thoughtful edge handling. For production, add try-except around input for robustness, but here it keeps focus on core logic.


🎯 Summary and Reflections

This Morse code generator transforms text encoding into an interactive tool, leveraging dicts for quick mappings and loops for structured output. It reinforced key principles for me:

  • Mapping efficiency: Dicts excel for static lookups, scalable to fuller char sets without code bloat.
  • Input sanitation: Upper and split ensure reliability, common in text processors to avoid surprises.
  • Error grace: Placeholders over errors make it forgiving, better for casual use.
  • Modularity: Separate function from main allows unit testing, like asserting "A" -> ".-".
  • Interactivity: While loop with break creates a simple CLI, extensible to commands like "decode".

What stood out was its balance, intermediate yet accessible, teaching real patterns like config params (placeholder) and typing for hints. Limitations? No numbers/symbols, but adding to dict is trivial. Surprising how Morse's variable lengths (E: ".", J: ".---") highlight why fixed encodings evolved. For enhancements, add audio output via libraries like pydub, or a decoder reversing the dict.

Advanced Alternatives: Use regex for input cleaning, or comprehensions: " ".join(" ".join(MORSE_MAPPING.get(c, "?") for c in word) for word in text.upper().split()). For speed on long texts, precompute, but unnecessary here. How do you handle encodings? Comment your tips!


🚀 Next Steps and Resources

Day 35 bridged data structures with user interaction, setting up for more encoding/decoding adventures. In #80DaysOfChallenges? Added numbers? Share your extension!

Top comments (0)