JavaScript Strings

JavaScript strings are a fundamental data type used to represent text. Strings in JavaScript are immutable, meaning once a string is created, it cannot be changed. Instead, any operation that seems to modify a string actually creates a new string.

Concept and Use Cases

Definition: A string in JavaScript is a sequence of characters used to represent text. It can be created using single quotes ('...'), double quotes ("..."), or backticks (`...`).

Common Use Cases:

  • Displaying text on a webpage.
  • Handling user input.
  • Storing and manipulating text data.
  • Implementing search functionalities.

When to Use

  • When you need to handle or display text.
  • When working with user input or form data.
  • When performing text manipulation, such as concatenation, slicing, or pattern matching.

Time and Space Complexity

Time Complexity:

  • Access: O(1) (indexing into a string)
  • Searching: O(n) (for methods like indexOf, includes)
  • Slicing: O(n) (copying the sliced part)
  • Concatenation: O(n) (creating a new string)

Space Complexity:

  • Depends on the size of the string and the operation being performed.

String Operations and Methods

Creating Strings

Example:

let singleQuote = 'Hello, World!';
let doubleQuote = "Hello, World!";
let backtick = `Hello, World!`;

String Length

Example:

let str = 'Hello, World!';
console.log(str.length);  // Output: 13

Accessing Characters

Example:

let str = 'Hello, World!';
console.log(str[0]);  // Output: 'H'
console.log(str.charAt(0));  // Output: 'H'

Concatenation

Example:

let str1 = 'Hello';
let str2 = 'World';
let result = str1 + ', ' + str2 + '!';  // Using + operator
let result2 = `${str1}, ${str2}!`;  // Using template literals
console.log(result);  // Output: 'Hello, World!'
console.log(result2);  // Output: 'Hello, World!'

Slicing and Substring

Example:

let str = 'Hello, World!';
console.log(str.slice(0, 5));  // Output: 'Hello'
console.log(str.substring(0, 5));  // Output: 'Hello'
console.log(str.substr(0, 5));  // Output: 'Hello'

Searching

Example:

let str = 'Hello, World!';
console.log(str.indexOf('World'));  // Output: 7
console.log(str.includes('World'));  // Output: true
console.log(str.startsWith('Hello'));  // Output: true
console.log(str.endsWith('!'));  // Output: true

Replacing

Example:

let str = 'Hello, World!';
let newStr = str.replace('World', 'JavaScript');
console.log(newStr);  // Output: 'Hello, JavaScript!'

Trimming

Example:

let str = '   Hello, World!   ';
console.log(str.trim());  // Output: 'Hello, World!'
console.log(str.trimStart());  // Output: 'Hello, World!   '
console.log(str.trimEnd());  // Output: '   Hello, World!'

Case Conversion

Example:

let str = 'Hello, World!';
console.log(str.toUpperCase());  // Output: 'HELLO, WORLD!'
console.log(str.toLowerCase());  // Output: 'hello, world!'

Splitting

Example:

let str = 'Hello, World!';
let words = str.split(' ');
console.log(words);  // Output: ['Hello,', 'World!']

Joining

Example:

let words = ['Hello,', 'World!'];
let str = words.join(' ');
console.log(str);  // Output: 'Hello, World!'

Practical Tips and Tricks

  • Avoiding Repeated Concatenation in Loops: Using + for concatenation in loops can be inefficient. Instead, use Array.join or template literals.

    Example:

    let result = '';
    for (let i = 0; i < 1000; i++) {
        result += 'a';  // Inefficient
    }
    
    let resultArray = new Array(1000).fill('a');
    result = resultArray.join('');  // More efficient
    
  • Template Literals for Multiline Strings: Use backticks for easy multiline string creation.

    Example:

    let multiLine = `
    This is a string
    that spans multiple
    lines.
    `;
    console.log(multiLine);
    
  • Regular Expressions for Pattern Matching: Use regex for complex search and replace operations.

    Example:

    let str = 'Hello, World!';
    let regex = /world/i;  // Case-insensitive search
    console.log(str.match(regex));  // Output: ['World']
    

Common Gotchas

  • Immutability: Strings are immutable. Any method that modifies a string returns a new string.

    Example:

    let str = 'Hello';
    str[0] = 'h';  // Does nothing
    console.log(str);  // Output: 'Hello'
    
  • Off-by-One Errors: Remember that string indices are zero-based.

    Example:

    let str = 'Hello';
    console.log(str[5]);  // Output: undefined (index out of bounds)
    
  • Unexpected undefined or NaN: Be cautious when converting strings to numbers or accessing out-of-bounds indices.

    Example:

    let str = '123';
    console.log(parseInt(str));  // Output: 123
    console.log(parseInt('abc'));  // Output: NaN
    
    let str2 = 'Hello';
    console.log(str2[10]);  // Output: undefined (index out of bounds)
    

Advanced Topics

String Interning

String interning is a technique of storing only one copy of each distinct string value, which must be immutable. JavaScript engines automatically intern string literals. This can lead to performance improvements as identical strings can share memory.

Unicode and UTF-16

JavaScript strings are encoded in UTF-16. This has implications for handling Unicode characters, especially those outside the Basic Multilingual Plane (BMP).

Example:

let str = '𝌆';  // U+1D306 TETRAGRAM FOR CENTRE
console.log(str.length);  // Output: 2 (UTF-16 encoding uses two 16-bit code units)
console.log(str.codePointAt(0));  // Output: 119558

String.fromCharCode and String.fromCodePoint

Use String.fromCharCode for BMP characters and String.fromCodePoint for characters outside BMP.

Example:

console.log(String.fromCharCode(97));  // Output: 'a'
console.log(String.fromCodePoint(0x1D306));  // Output: '𝌆'

String Algorithms

Below are some common string algorithms you should be familiar with:

Reversing a String

Reversing a string is a common operation where the order of characters in the string is reversed.

Example:

function reverseString(str) {
    return str.split('').reverse().join('');
}

console.log(reverseString('Hello, World!'));  // Output: '!dlroW ,olleH'

// Reversing with a loop
function reverseStringLoop(str) {
    let reversed = '';
    for (let char of str) {
        reversed = char + reversed;
    }
    return reversed;
}
console.log(reverseStringLoop('JavaScript'));  // Output: 'tpircSavaJ'

Palindrome Check

A palindrome check determines if a string reads the same forward and backward, ignoring non-alphanumeric characters and case differences.

Example:

function isPalindrome(str) {
    let cleaned = str.replace(/[^A-Za-z0-9]/g, '').toLowerCase();
    let reversed = cleaned.split('').reverse().join('');
    return cleaned === reversed;
}

console.log(isPalindrome('A man, a plan, a canal, Panama'));  // Output: true

// Palindrome check using two pointers
function isPalindromeTwoPointers(str) {
    let cleaned = str.replace(/[^A-Za-z0-9]/g, '').toLowerCase();
    let left = 0;
    let right = cleaned.length - 1;
    while (left < right) {
        if (cleaned[left] !== cleaned[right]) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}
console.log(isPalindromeTwoPointers('racecar'));  // Output: true

Anagram Check

An anagram check verifies if two strings contain the same characters in the same frequency, ignoring spaces and case differences.

Example:

function areAnagrams(str1, str2) {
    let normalize = str => str.replace(/[^A-Za-z0-9]/g, '').toLowerCase().split('').sort().join('');
    return normalize(str1) === normalize(str2);
}

console.log(areAnagrams('listen', 'silent'));  // Output: true

// Anagram check using character counts
function areAnagramsCharCount(str1, str2) {
    let charCount = str => {
        let count = {};
        str.replace(/[^A-Za-z0-9]/g, '').toLowerCase().split('').forEach(char => {
            count[char] = (count[char] || 0) + 1;
        });
        return count;
    };
    let count1 = charCount(str1);
    let count2 = charCount(str2);
    return JSON.stringify(count1) === JSON.stringify(count2);
}
console.log(areAnagramsCharCount('anagram', 'nagaram'));  // Output: true

Substring Search (Knuth-Morris-Pratt Algorithm)

The Knuth-Morris-Pratt (KMP) algorithm searches for occurrences of a "pattern" string within a "text" string efficiently by preprocessing the pattern to skip unnecessary comparisons.

Example:

function KMPSearch(pattern, text) {
    const buildLPS = (pattern) => {
        let lps = Array(pattern.length).fill(0);
        let len = 0;
        let i = 1;
        while (i < pattern.length) {
            if (pattern[i] === pattern[len]) {
                len++;
                lps[i] = len;
                i++;
            } else {
                if (len !== 0) {
                    len = lps[len - 1];
                } else {
                    lps[i] = 0;
                    i++;
                }
            }
        }
        return lps;
    }

    let lps = buildLPS(pattern);
    let i = 0, j = 0;
    while (i < text.length) {
        if (pattern[j] === text[i]) {
            i++;
            j++;
        }
        if (j === pattern.length) {
            return i - j;
        } else if (i < text.length && pattern[j] !== text[i]) {
            if (j !== 0) {
                j = lps[j - 1];
            } else {
                i++;
            }
        }
    }
    return -1;
}

console.log(KMPSearch('abc', 'abcabc'));  // Output: 0
console.log(KMPSearch('abc', 'aaabcaabc'));  // Output: 3

// Substring search with indexOf
console.log('abcabc'.indexOf('abc'));  // Output: 0
console.log('aaabcaabc'.indexOf('abc'));  // Output: 3

Interview Tips and Tricks

  • Understand String Immutability: Many interview questions will test your understanding of string immutability. Always remember that any "modification" operation on a string returns a new string.

  • Use Built-In Methods Efficiently: Familiarize yourself with string methods like slice, split, join, and replace. These can simplify many problems and lead to more readable code.

  • Edge Cases: Always consider edge cases such as empty strings, single-character strings, and strings with special characters. Prepare to handle these in your solutions.

  • Practice Common Algorithms: Master common string algorithms such as reversing a string, checking for palindromes, and finding substrings. These are frequent in interviews and demonstrate a solid understanding of string manipulation.

Common Mistakes

  • Ignoring Immutability: Forgetting that strings are immutable can lead to inefficient code or bugs.

  • Off-by-One Errors: Be careful with indices, especially in languages where indices are zero-based.

  • Overusing Concatenation in Loops: This can lead to performance issues due to the creation of multiple intermediate strings.

  • Misusing Regular Expressions: Regular expressions are powerful but can be complex and hard to debug. Ensure you fully understand your regex patterns.

By mastering JavaScript strings and understanding their intricacies, you will be well-equipped to handle a variety of interview questions and real-world problems involving text manipulation and processing. Remember to practice regularly and explore advanced string manipulation techniques to deepen your understanding.

Practice Problems

Valid Word AbbreviationDifficulty: Easy

Given an input string and a valid word, determine if the input string is a correct abbreviation for the word by mapping characters to their corresponding full-word equivalents.

Loading...

Group AnagramsDifficulty: Medium

Given an array of strings, group the anagrams together by rearranging the characters in each string to form a unique identifier for grouping purposes.

Loading...

Generate ParenthesesDifficulty: Medium

The goal is to write a program that generates all possible combinations of well-formed parentheses, given a specified number of pairs.

Loading...

Write an algorithm to find the longest substring within a given string that contains no repeating characters.

Loading...

Encode and Decode StringsDifficulty: Medium

Write a program that can both encode and decode a string format using a specific encoding scheme, where the encoded string contains "a" to indicate start of a sequence, followed by the actual string and "e" to end the sequence, while decoding requires reversing this process.

Loading...

Let's continue exploring the next page. Take your time, and proceed when you're ready.

Lesson completed?

Found a bug, typo, or have feedback?

Let me know