/** * 文字列キーから余分な記号を除去する汎用関数 * @param key - 正規化する文字列 * @param options - 正規化オプション * @returns 正規化された文字列 */ export function normalizeKey( key: string, options: { removeSymbols?: boolean; unicodeNormalize?: boolean | 'NFC' | 'NFD' | 'NFKC' | 'NFKD'; trim?: boolean; } = {}, ): string { if (!key || typeof key !== 'string') return key; const { removeSymbols = true, unicodeNormalize = false, trim = true } = options; let result = key; // 記号除去 if (removeSymbols) { result = result .replace(/^["']?\[['"]?/, '') // 先頭の"[' や [' や ["などを削除 .replace(/['"]?\]?["']?$/, '') // 末尾の'] や "]や '"などを削除 .replace(/^['"]/, '') // 先頭の' または "を削除 .replace(/['"]$/, ''); // 末尾の' または "を削除 } // 空白除去 if (trim) { result = result.trim(); } // Unicode正規化 if (unicodeNormalize) { const normalizationForm = typeof unicodeNormalize === 'string' ? unicodeNormalize : 'NFKC'; result = result.normalize(normalizationForm); } return result; } /** * オブジェクトのキーを正規化する汎用関数 * @param data - 正規化対象のデータ * @param keyNormalizer - キー正規化関数(デフォルト: normalizeKey) * @returns キーが正規化されたデータ */ export function normalizeObjectKeys(data: unknown, keyNormalizer: (key: string) => string = normalizeKey): unknown { if (!data || typeof data !== 'object') { return data; } // 配列の場合は各要素を再帰的に処理 if (Array.isArray(data)) { return data.map((item) => normalizeObjectKeys(item, keyNormalizer)); } // オブジェクトの場合はキーを正規化 const normalized: Record = {}; for (const [key, value] of Object.entries(data)) { const cleanKey = keyNormalizer(key); // 値も再帰的に処理 normalized[cleanKey] = normalizeObjectKeys(value, keyNormalizer); } return normalized; } /** * @deprecated normalizeObjectKeysを使用してください */ export function normalizeStrategyKeys(strategyData: unknown): unknown { return normalizeObjectKeys(strategyData); } /** * 配列の特定インデックスの要素を正規化する汎用関数 * @param array - 対象の配列 * @param indices - 正規化するインデックスの配列 * @param normalizer - 正規化関数(デフォルト: normalizeObjectKeys) * @returns 指定インデックスが正規化された配列 */ export function normalizeArrayAtIndices( array: unknown[], indices: number[], normalizer: (data: unknown) => unknown = normalizeObjectKeys, ): unknown[] { if (!Array.isArray(array)) { return array as unknown[]; } const normalized = [...array]; indices.forEach((index) => { if (index >= 0 && index < array.length) { normalized[index] = normalizer(array[index]); } }); return normalized; } /** * @deprecated normalizeArrayAtIndicesを使用してください */ export function normalizeThemeByMomentResponse(response: unknown): unknown { if (!Array.isArray(response) || response.length < 3) { return response; } // response[2]がstrategy_x_moment return normalizeArrayAtIndices(response, [2]); } /** * 正規化されたキーでオブジェクトから値を検索する汎用関数 * @param data - 検索対象のオブジェクト * @param originalKey - 検索するキー * @param normalizeOptions - 正規化オプション * @returns マッチした値、見つからない場合はundefined */ export function findWithNormalizedKey( data: Record | undefined, originalKey: string, normalizeOptions: Parameters[1] = { removeSymbols: true, unicodeNormalize: 'NFKC', trim: true, }, ): T | undefined { if (!data) { return undefined; } // まず元のキーで検索 if (data[originalKey]) { return data[originalKey]; } // 正規化したキーで検索 const normalizedKey = normalizeKey(originalKey, normalizeOptions); for (const [key, value] of Object.entries(data)) { const normalizedDataKey = normalizeKey(key, normalizeOptions); if (normalizedDataKey === normalizedKey) { return value; } } return undefined; }