File size: 7,476 Bytes
68f7925
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
import { describe, expect, it } from 'vitest';
import { normalizeKey, findWithNormalizedKey } from './normalize-strategy-keys';

describe('normalizeKey', () => {
  describe('記号の除去', () => {
    it('先頭と末尾の引用符を削除する', () => {
      expect(normalizeKey('"テスト"')).toBe('テスト');
      expect(normalizeKey("'テスト'")).toBe('テスト');
    });

    it('配列記法の記号を削除する', () => {
      expect(normalizeKey('[テスト]')).toBe('テスト');
      expect(normalizeKey('["テスト"]')).toBe('テスト');
      expect(normalizeKey("['テスト']")).toBe('テスト');
    });

    it('複合的な記号パターンを削除する(実際の問題ケース)', () => {
      // 実際に問題になったケース
      expect(normalizeKey("['購買前(まだ商品について詳しくないが、商品の情報収集を実施している状態)']"))
        .toBe('購買前(まだ商品について詳しくないが、商品の情報収集を実施している状態)');

      expect(normalizeKey('["事業全体の変革(企業の成長戦略を見直したいと考えた瞬間)"]'))
        .toBe('事業全体の変革(企業の成長戦略を見直したいと考えた瞬間)');

      expect(normalizeKey("'事業全体の変革(企業の成長戦略を見直したいと考えた瞬間)']"))
        .toBe('事業全体の変革(企業の成長戦略を見直したいと考えた瞬間)');
    });

    it('ネストした引用符パターンを処理する', () => {
      // エスケープされた引用符は現在の実装では完全には処理されない
      // これは複雑なケースなので、現実的な使用例に焦点を当てる
      expect(normalizeKey("['テスト']")).toBe('テスト');
      expect(normalizeKey('["テスト"]')).toBe('テスト');
    });

    it('末尾の余分な記号を削除する', () => {
      expect(normalizeKey("テスト']")).toBe('テスト');
      expect(normalizeKey("テスト'")).toBe('テスト');
      expect(normalizeKey('テスト"]')).toBe('テスト');
    });

    it('先頭の余分な記号を削除する', () => {
      expect(normalizeKey("['テスト")).toBe('テスト');
      expect(normalizeKey('["テスト')).toBe('テスト');
      expect(normalizeKey("'[テスト")).toBe('テスト');
    });
  });

  describe('Unicode正規化', () => {
    it('全角・半角を正規化する(NFKC)', () => {
      expect(normalizeKey('ABC123', { unicodeNormalize: 'NFKC' })).toBe('ABC123');
      expect(normalizeKey('①②③', { unicodeNormalize: 'NFKC' })).toBe('123');
    });

    it('濁点・半濁点を正規化する', () => {
      const text = 'が' + 'ぎ' + 'ぐ'; // 結合文字で表現
      const normalized = normalizeKey(text, { unicodeNormalize: 'NFC' });
      expect(normalized).toBe('がぎぐ');
    });

    it('瞬間という文字が正しく処理される', () => {
      expect(normalizeKey('瞬間')).toBe('瞬間');
      expect(normalizeKey("['瞬間']")).toBe('瞬間');
      expect(normalizeKey('["購買を決めた瞬間"]')).toBe('購買を決めた瞬間');
    });
  });

  describe('空白の処理', () => {
    it('前後の空白を削除する', () => {
      expect(normalizeKey('  テスト  ')).toBe('テスト');
      expect(normalizeKey('\tテスト\n')).toBe('テスト');
    });

    it('記号除去後の空白も削除する', () => {
      expect(normalizeKey("['  テスト  ']")).toBe('テスト');
      expect(normalizeKey('" テスト "')).toBe('テスト');
    });
  });

  describe('オプションの制御', () => {
    it('removeSymbols=falseで記号を残す', () => {
      expect(normalizeKey('["テスト"]', { removeSymbols: false })).toBe('["テスト"]');
    });

    it('trim=falseで空白を残す', () => {
      expect(normalizeKey('  テスト  ', { trim: false })).toBe('  テスト  ');
    });

    it('unicodeNormalize=falseで正規化しない', () => {
      expect(normalizeKey('ABC', { unicodeNormalize: false })).toBe('ABC');
    });
  });

  describe('エッジケース', () => {
    it('空文字列を処理する', () => {
      expect(normalizeKey('')).toBe('');
    });

    it('null/undefinedを処理する', () => {
      expect(normalizeKey(null as any)).toBe(null);
      expect(normalizeKey(undefined as any)).toBe(undefined);
    });

    it('記号のみの文字列を処理する', () => {
      expect(normalizeKey('[""]')).toBe('');
      expect(normalizeKey("['']")).toBe('');
      expect(normalizeKey('[]')).toBe('');
    });
  });
});

describe('findWithNormalizedKey', () => {
  describe('基本的な検索', () => {
    const data = {
      'キー1': 'value1',
      'キー2': 'value2',
      "['キー3']": 'value3',
    };

    it('完全一致するキーを見つける', () => {
      expect(findWithNormalizedKey(data, 'キー1')).toBe('value1');
    });

    it('正規化後に一致するキーを見つける', () => {
      expect(findWithNormalizedKey(data, "['キー3']")).toBe('value3');
      expect(findWithNormalizedKey(data, 'キー3')).toBe('value3');
    });

    it('見つからない場合はundefinedを返す', () => {
      expect(findWithNormalizedKey(data, 'キー4')).toBeUndefined();
    });
  });

  describe('実際の問題ケースでの検索', () => {
    const problemData = {
      "['購買前(まだ商品について詳しくないが、商品の情報収集を実施している状態)']": {
        prediction: 0.8
      },
      '["事業全体の変革(企業の成長戦略を見直したいと考えた瞬間)"]': {
        prediction: 0.7
      },
      "積極化戦略": {
        prediction: 0.6
      }
    };

    it('余分な記号があるキーでも値を取得できる', () => {
      const result1 = findWithNormalizedKey(
        problemData,
        '購買前(まだ商品について詳しくないが、商品の情報収集を実施している状態)'
      );
      expect(result1).toEqual({ prediction: 0.8 });

      const result2 = findWithNormalizedKey(
        problemData,
        '事業全体の変革(企業の成長戦略を見直したいと考えた瞬間)'
      );
      expect(result2).toEqual({ prediction: 0.7 });
    });

    it('記号がないキーも正しく処理する', () => {
      const result = findWithNormalizedKey(problemData, '積極化戦略');
      expect(result).toEqual({ prediction: 0.6 });
    });
  });

  describe('カスタムオプション', () => {
    const data = {
      'ABC': 'value1',
      '  キー  ': 'value2',
    };

    it('Unicode正規化オプションを使用できる', () => {
      const result = findWithNormalizedKey(data, 'ABC', {
        unicodeNormalize: 'NFKC'
      });
      expect(result).toBe('value1');
    });

    it('trim無効化オプションを使用できる', () => {
      const result = findWithNormalizedKey(data, 'キー', {
        trim: true  // デフォルトでtrueなので、これでも動作する
      });
      expect(result).toBe('value2');
    });
  });

  describe('エッジケース', () => {
    it('undefinedのデータを処理する', () => {
      expect(findWithNormalizedKey(undefined, 'key')).toBeUndefined();
    });

    it('空のオブジェクトを処理する', () => {
      expect(findWithNormalizedKey({}, 'key')).toBeUndefined();
    });
  });
});