TypeScript での文字列検索・操作チートシート — 用途別コード集
TypeScript での文字列検索と操作のための実践的なコード集です。よくある用途別に整理しているため、必要な実装をすぐに見つけられます。
基本的な文字検索
単一の文字・部分文字列を検索
// includes() — 最も推奨される方法
function containsCharacter(text: string, char: string): boolean {
return text.includes(char);
}
// indexOf() — 位置も取得したい場合
function hasCharacter(text: string, char: string): boolean {
return text.indexOf(char) !== -1;
}
const text = "Hello, TypeScript!";
console.log(containsCharacter(text, "Type")); // true
console.log(text.indexOf("Type")); // 7
大文字・小文字を区別しない検索
function containsIgnoreCase(text: string, search: string): boolean {
return text.toLowerCase().includes(search.toLowerCase());
}
console.log(containsIgnoreCase("Hello TypeScript", "typescript")); // true
先頭・末尾の確認
const url = "https://example.com/api/users";
url.startsWith("https://"); // true
url.endsWith("/users"); // true
// 複数候補のいずれかで始まるか
const schemes = ["https://", "http://"];
schemes.some(s => url.startsWith(s)); // true
パターンマッチング(正規表現)
// 基本パターン検索
function matchesPattern(text: string, pattern: string): boolean {
return new RegExp(pattern).test(text);
}
// よく使う判定
const hasDigit = (text: string): boolean => /\d/.test(text);
const hasLetter = (text: string): boolean => /[a-zA-Z]/.test(text);
const hasSpecialChar = (text: string): boolean => /[!@#$%^&*()]/.test(text);
const isEmail = (text: string): boolean => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(text);
const isURL = (text: string): boolean => /^https?:\/\/.+/.test(text);
// 特殊文字を含む文字列の安全な検索(エスケープ処理)
function findSpecialChar(text: string, specialChar: string): boolean {
const escapedChar = specialChar.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return new RegExp(escapedChar).test(text);
}
複数文字の検索
// いずれかの文字列を含むか
function containsAny(text: string, searchTerms: string[]): boolean {
return searchTerms.some(term => text.includes(term));
}
// すべての文字列を含むか
function containsAll(text: string, searchTerms: string[]): boolean {
return searchTerms.every(term => text.includes(term));
}
console.log(containsAny("Hello", ["x", "H", "z"])); // true
console.log(containsAll("Hello World", ["Hello", "World"])); // true
入力バリデーション
パスワード強度チェック
type ValidationResult = { valid: boolean; messages: string[] };
function validatePassword(password: string): ValidationResult {
const checks = [
{ test: (p: string) => p.length >= 8, message: "8文字以上必要です" },
{ test: (p: string) => /[A-Z]/.test(p), message: "大文字が必要です" },
{ test: (p: string) => /[a-z]/.test(p), message: "小文字が必要です" },
{ test: (p: string) => /[0-9]/.test(p), message: "数字が必要です" },
{ test: (p: string) => /[!@#$%^&*]/.test(p), message: "特殊文字が必要です" },
];
const failedChecks = checks
.filter(check => !check.test(password))
.map(check => check.message);
return { valid: failedChecks.length === 0, messages: failedChecks };
}
// 使用例
const result = validatePassword("Pass123!");
console.log(result.valid); // true
console.log(result.messages); // []
XSS 対策の入力サニタイズ
function sanitizeInput(input: string): string {
return input
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// 使用例
const userInput = "<script>alert('XSS')</script>";
console.log(sanitizeInput(userInput));
// <script>alert('XSS')</script>
テキスト分析
文字の出現回数をカウント
// indexOf を使った実装
function countOccurrences(text: string, target: string): number {
let count = 0;
let pos = text.indexOf(target);
while (pos !== -1) {
count++;
pos = text.indexOf(target, pos + 1);
}
return count;
}
// 簡潔な代替実装
function countOccurrencesAlt(text: string, target: string): number {
return text.split(target).length - 1;
}
console.log(countOccurrences("banana", "an")); // 2
文字の全位置を取得
function findAllPositions(text: string, target: string): number[] {
const positions: number[] = [];
let pos = text.indexOf(target);
while (pos !== -1) {
positions.push(pos);
pos = text.indexOf(target, pos + 1);
}
return positions;
}
console.log(findAllPositions("abcabc", "a")); // [0, 3]
文字列の出現頻度マップ
function charFrequency(text: string): Map<string, number> {
return [...text].reduce((map, char) => {
map.set(char, (map.get(char) ?? 0) + 1);
return map;
}, new Map<string, number>());
}
パフォーマンス最適化
早期リターン
function efficientSearch(text: string, terms: string[]): boolean {
for (const term of terms) {
if (text.includes(term)) {
return true; // 一致が見つかった時点で終了
}
}
return false;
}
検索結果のキャッシング
const searchCache = new Map<string, boolean>();
function cachedSearch(text: string, search: string): boolean {
const cacheKey = `${text}:${search}`;
if (searchCache.has(cacheKey)) {
return searchCache.get(cacheKey)!;
}
const result = text.includes(search);
searchCache.set(cacheKey, result);
return result;
}
大きなテキストの処理
function searchLargeText(text: string, search: string): boolean {
const chunkSize = 10000;
for (let i = 0; i < text.length; i += chunkSize) {
// search がチャンクをまたがる可能性を考慮してオーバーラップさせる
const chunk = text.slice(i, i + chunkSize + search.length - 1);
if (chunk.includes(search)) return true;
}
return false;
}
特殊ケースの処理
Unicode 文字・絵文字の正確なカウント
// スプレッド構文でサロゲートペアを正しく扱う
function countUnicodeChars(text: string): number {
return [...text].length;
}
// 絵文字を含む文字列の確認
function containsEmoji(text: string): boolean {
const emojiRegex = /[\u{1F300}-\u{1F6FF}\u{1F900}-\u{1F9FF}]/u;
return emojiRegex.test(text);
}
console.log("👋".length); // 2(UTF-16 コードユニット)
console.log(countUnicodeChars("👋")); // 1(正しい文字数)
文字列置換
// 全ての出現を置換(ES2021 以降)
const modernReplaceAll = (text: string, search: string, replace: string): string =>
text.replaceAll(search, replace);
// 後方互換性が必要な場合
const legacyReplaceAll = (text: string, search: string, replace: string): string =>
text.split(search).join(replace);
実践的なツール関数
URL からクエリパラメータを抽出
function getQueryParam(url: string, param: string): string | null {
return new URL(url).searchParams.get(param);
}
console.log(getQueryParam("https://example.com/?id=123&name=test", "id")); // "123"
スラッグ生成(ブログ記事 URL 等)
function toSlug(text: string): string {
return text
.toLowerCase()
.replace(/[^\w\s-]/g, "") // 英数字・スペース・ハイフン以外を除去
.replace(/\s+/g, "-") // スペースをハイフンに
.replace(/-+/g, "-") // 連続ハイフンを単一に
.replace(/^-|-$/g, ""); // 先頭・末尾のハイフンを除去
}
console.log(toSlug("Hello World! This is TypeScript")); // "hello-world-this-is-typescript"
まとめ
- 単純な検索:
includes()が最も読みやすく推奨 - 位置が必要:
indexOf()/lastIndexOf() - パターンマッチ: 正規表現(
test()/match()) - Unicode・絵文字: スプレッド構文
[...text]でサロゲートペアを正しく扱う - パフォーマンス: 大量データには早期リターン・キャッシング・チャンク処理を組み合わせる