StringExamples

Examples

Real-world usage examples for @visulima/string functions.

Examples

Practical, real-world examples demonstrating how to use @visulima/string in common scenarios.

API Key Naming Conventions

Convert between naming conventions when working with APIs that use different styles.

import { camelCase, snakeCase, kebabCase } from "@visulima/string/case";

// Convert API response keys from snake_case to camelCase
const apiResponse = {
    user_name: "John",
    email_address: "john@example.com",
    phone_number: "555-1234",
};

const camelCaseKeys = Object.fromEntries(
    Object.entries(apiResponse).map(([key, value]) => [
        camelCase(key),
        value,
    ])
);
// { userName: 'John', emailAddress: 'john@example.com', phoneNumber: '555-1234' }

// Convert camelCase to snake_case for API requests
const requestBody = {
    userName: "Jane",
    emailAddress: "jane@example.com",
};

const snakeCaseKeys = Object.fromEntries(
    Object.entries(requestBody).map(([key, value]) => [
        snakeCase(key),
        value,
    ])
);
// { user_name: 'Jane', email_address: 'jane@example.com' }

CSS Class Name Generation

Generate CSS class names from component props.

import { kebabCase } from "@visulima/string/case";

function generateClassName(
    component: string,
    modifiers: Record<string, boolean>
): string {
    const base = kebabCase(component);
    const activeModifiers = Object.entries(modifiers)
        .filter(([, active]) => active)
        .map(([name]) => `${base}--${kebabCase(name)}`);

    return [base, ...activeModifiers].join(" ");
}

generateClassName("UserProfile", {
    isActive: true,
    hasAvatar: true,
    isDisabled: false,
});
// 'user-profile user-profile--is-active user-profile--has-avatar'

Environment Variable Mapping

Map configuration objects to environment variable names.

import { constantCase } from "@visulima/string/case";

function configToEnvVars(
    config: Record<string, string>,
    prefix: string = "APP"
): Record<string, string> {
    return Object.fromEntries(
        Object.entries(config).map(([key, value]) => [
            `${prefix}_${constantCase(key)}`,
            value,
        ])
    );
}

configToEnvVars({
    databaseUrl: "postgres://localhost",
    redisHost: "127.0.0.1",
    apiSecret: "my-secret",
});
// {
//   APP_DATABASE_URL: 'postgres://localhost',
//   APP_REDIS_HOST: '127.0.0.1',
//   APP_API_SECRET: 'my-secret',
// }

Blog Post URL Generation

Generate SEO-friendly URLs from article titles.

import { slugify } from "@visulima/string/slugify";

function generatePostUrl(title: string, date: Date): string {
    const slug = slugify(title);
    const year = date.getFullYear();
    const month = String(date.getMonth() + 1).padStart(2, "0");

    return `/blog/${year}/${month}/${slug}`;
}

generatePostUrl("My First Blog Post!", new Date("2024-03-15"));
// '/blog/2024/03/my-first-blog-post'

generatePostUrl("Internationalization: 国际化 and i18n", new Date("2024-06-01"));
// '/blog/2024/06/internationalization-guo-ji-hua-and-i18n'

// With custom options for a filename
function generateFilename(title: string): string {
    return slugify(title, {
        separator: "_",
        allowedChars: "a-zA-Z0-9_.",
    });
}

generateFilename("My Document (Final) v2.pdf");
// 'my_document_final_v2.pdf'

CLI Command Suggestions

Suggest the correct command when users make typos.

import { closestString } from "@visulima/string/closest-string";
import { wordSimilaritySort } from "@visulima/string/word-similarity-sort";

const commands = [
    "init", "build", "dev", "start", "test",
    "lint", "format", "deploy", "publish",
];

function suggestCommand(input: string): string {
    const match = closestString(input, commands);

    if (!match) {
        return `Unknown command: ${input}. Run --help to see available commands.`;
    }

    return `Did you mean: ${match}?`;
}

suggestCommand("biuld");    // 'Did you mean: build?'
suggestCommand("tset");     // 'Did you mean: test?'
suggestCommand("publsh");   // 'Did you mean: publish?'

// Show multiple suggestions
function suggestCommands(input: string, count: number = 3): string[] {
    return wordSimilaritySort(input, commands).slice(0, count);
}

suggestCommands("sta");
// ['start', 'init', 'test']

Terminal Table Formatting

Build aligned columns in terminal output using string width.

import { getStringWidth } from "@visulima/string/get-string-width";

function formatTable(
    headers: string[],
    rows: string[][],
    padding: number = 2
): string {
    // Calculate column widths
    const colWidths = headers.map((header, colIndex) => {
        const cellWidths = rows.map(
            (row) => getStringWidth(row[colIndex] ?? "")
        );
        return Math.max(getStringWidth(header), ...cellWidths);
    });

    // Format a row
    const formatRow = (cells: string[]): string =>
        cells
            .map((cell, i) => {
                const width = getStringWidth(cell);
                const pad = " ".repeat(
                    (colWidths[i] ?? 0) - width + padding
                );
                return cell + pad;
            })
            .join("");

    // Build table
    const headerLine = formatRow(headers);
    const separator = colWidths
        .map((w) => "-".repeat(w + padding))
        .join("");
    const bodyLines = rows.map((row) => formatRow(row));

    return [headerLine, separator, ...bodyLines].join("\n");
}

const table = formatTable(
    ["Name", "Country", "City"],
    [
        ["John", "USA", "New York"],
        ["Tanaka", "Japan", "Tokyo"],
        ["Ahmed", "Egypt", "Cairo"],
    ]
);

console.log(table);
// Name      Country   City
// --------------------------------
// John      USA       New York
// Tanaka    Japan     Tokyo
// Ahmed     Egypt     Cairo

Search Autocomplete

Build a fuzzy search with ranked results.

import { wordSimilaritySort } from "@visulima/string/word-similarity-sort";
import { distance } from "@visulima/string";

const products = [
    "iPhone 15 Pro",
    "iPhone 15 Pro Max",
    "Samsung Galaxy S24",
    "Samsung Galaxy S24 Ultra",
    "Google Pixel 8",
    "Google Pixel 8 Pro",
    "OnePlus 12",
];

function searchProducts(
    query: string,
    maxResults: number = 5
): string[] {
    if (query.length < 2) return [];

    // Sort by similarity, then filter out poor matches
    const sorted = wordSimilaritySort(query, products, {
        caseSensitive: false,
    });

    // Only return results with reasonable distance
    return sorted
        .filter((product) => {
            const d = distance(
                query.toLowerCase(),
                product.toLowerCase()
            );
            // Allow distance up to half the query length
            return d <= Math.max(query.length * 0.6, 3);
        })
        .slice(0, maxResults);
}

searchProducts("iphone");
// ['iPhone 15 Pro', 'iPhone 15 Pro Max']

searchProducts("pixel");
// ['Google Pixel 8', 'Google Pixel 8 Pro']

Truncating Terminal Output

Truncate strings to fit terminal width while preserving Unicode and ANSI codes.

import {
    getStringTruncatedWidth,
} from "@visulima/string/get-string-truncated-width";
import { getStringWidth } from "@visulima/string/get-string-width";

function truncateForTerminal(
    text: string,
    maxWidth: number,
    ellipsis: string = "..."
): string {
    const width = getStringWidth(text);

    if (width <= maxWidth) {
        return text;
    }

    const result = getStringTruncatedWidth(text, {
        limit: maxWidth,
        ellipsis,
    });

    return text.slice(0, result.index) + (result.ellipsed ? ellipsis : "");
}

truncateForTerminal("Hello, World!", 10);
// 'Hello, ...'

truncateForTerminal("你好世界 - Hello World", 12);
// '你好世界 ...'

// Preserve ANSI colors
truncateForTerminal("\x1b[31mError: Something went wrong\x1b[0m", 15);
// Truncates the visible text, not the escape codes

Detecting Text Direction

Handle bidirectional text in your application.

import { direction } from "@visulima/string";

function wrapWithDirection(text: string): string {
    const dir = direction(text);

    if (dir === "rtl") {
        return `<span dir="rtl">${text}</span>`;
    }

    return text;
}

wrapWithDirection("Hello");     // 'Hello'
wrapWithDirection("مرحبا");     // '<span dir="rtl">مرحبا</span>'

Testing Colored CLI Output

Test that your CLI produces correctly colored output.

import { expect, describe, it } from "vitest";
import { toEqualAnsi } from "@visulima/string/test/vitest";
import {
    formatAnsiString,
    compareAnsiStrings,
} from "@visulima/string/test/utils";

expect.extend({ toEqualAnsi });

describe("CLI formatter", () => {
    it("should format errors in red", () => {
        const output = formatError("File not found");

        // Exact ANSI comparison
        expect(output).toEqualAnsi("\x1b[31mError: File not found\x1b[39m");
    });

    it("should format warnings with yellow text", () => {
        const output = formatWarning("Deprecated API");
        const formatted = formatAnsiString(output);

        // Test visible content
        expect(formatted.stripped).toBe("Warning: Deprecated API");

        // Verify ANSI codes are present
        expect(formatted.lengthDifference).toBeGreaterThan(0);
    });

    it("should compare two formatted strings", () => {
        const v1Output = formatV1("message");
        const v2Output = formatV2("message");

        const comparison = compareAnsiStrings(v1Output, v2Output);

        // They should display the same text
        expect(comparison.strippedEqual).toBe(true);
    });
});

Case Identification for Input Validation

Validate that user input follows expected naming conventions.

import { identifyCase } from "@visulima/string/case";

function validateVariableName(name: string): {
    valid: boolean;
    caseStyle: string;
    suggestion?: string;
} {
    const caseStyle = identifyCase(name);

    if (caseStyle === "camel") {
        return { valid: true, caseStyle };
    }

    return {
        valid: false,
        caseStyle,
        suggestion: `Expected camelCase but got ${caseStyle}`,
    };
}

validateVariableName("myVariable");
// { valid: true, caseStyle: 'camel' }

validateVariableName("my_variable");
// { valid: false, caseStyle: 'snake', suggestion: 'Expected camelCase but got snake' }

validateVariableName("MyVariable");
// { valid: false, caseStyle: 'pascal', suggestion: 'Expected camelCase but got pascal' }

Multi-Language Content Processing

Process content in multiple languages using locale-aware splitting.

import { splitByCase, camelCase } from "@visulima/string/case";

// Split mixed-script content
const japaneseMixed = "ひらがなカタカナABC";
splitByCase(japaneseMixed, { locale: "ja" });
// ['ひらがな', 'カタカナ', 'ABC']

// Case conversion with locale support
camelCase("Straße nach Berlin", { locale: "de" });
// Handles German eszett correctly

// Korean mixed text
splitByCase("한글Text", { locale: "ko" });
// ['한글', 'Text']

Building a Spell Checker

Use string similarity to suggest corrections for misspelled words.

import { closestString } from "@visulima/string/closest-string";
import { closestN } from "@visulima/string";

const dictionary = [
    "hello", "world", "computer", "program",
    "algorithm", "function", "variable", "constant",
    "interface", "abstract", "implement", "typescript",
];

function spellCheck(word: string): {
    correct: boolean;
    suggestions: (string | undefined)[];
} {
    const lowerWord = word.toLowerCase();

    if (dictionary.includes(lowerWord)) {
        return { correct: true, suggestions: [] };
    }

    return {
        correct: false,
        suggestions: closestN(lowerWord, dictionary, 3),
    };
}

spellCheck("algorythm");
// { correct: false, suggestions: ['algorithm', ...] }

spellCheck("typscript");
// { correct: false, suggestions: ['typescript', ...] }

spellCheck("function");
// { correct: true, suggestions: [] }

Next Steps

Support

Contribute to our work and keep us going

Community is the heart of open source. The success of our packages wouldn't be possible without the incredible contributions of users, testers, and developers who collaborate with us every day.Want to get involved? Here are some tips on how you can make a meaningful impact on our open source projects.

Ready to help us out?

Be sure to check out the package's contribution guidelines first. They'll walk you through the process on how to properly submit an issue or pull request to our repositories.

Submit a pull request

Found something to improve? Fork the repo, make your changes, and open a PR. We review every contribution and provide feedback to help you get merged.

Good first issues

Simple issues suited for people new to open source development, and often a good place to start working on a package.
View good first issues