teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

7

typo修正

2019/11/02 16:42

投稿

think49
think49

スコア18194

answer CHANGED
@@ -77,7 +77,7 @@
77
77
  > ```
78
78
 
79
79
  `"World"` と `"JavaScript"` の文字列長は異なるので、**可変長**の置換を期待しています。
80
- 何が問題かというと、1回目の置換に2回目の置換処理を実行する場合、**文字列長が変わるとインデックス値もずれる**のです。
80
+ 何が問題かというと、1回目の置換に2回目の置換処理を実行する場合、**文字列長が変わるとインデックス値もずれる**のです。
81
81
  これは2つの解決法があります。
82
82
 
83
83
  - 置換前後の文字列長の差分をとって、インデックス値を補正する

6

複数の範囲指定に対応する

2019/11/02 16:42

投稿

think49
think49

スコア18194

answer CHANGED
@@ -52,4 +52,60 @@
52
52
  replaceData('Hello, World!', 7, 5, 'JavaScript');
53
53
  ```
54
54
 
55
+ ### 複数の範囲指定に対応する
56
+
57
+ > 置き換えたい文字列が全体に複数ある場合に対応するため、また、全体の文字列もある程度規則性があり、置換の開始位置はそうそう変わらないので「文字列の◯番目から△番目を置換する」という方法を取りたかったのです。
58
+
59
+ 置換する範囲が複数ある場合、効率化の為には複数をまとめて**一括置換**する必要があり、回答の方向性が大きく変わります。
60
+ 今までに出てきた回答は全て**一つの範囲を置換するコード**であり、要件を満たしていません。
61
+
62
+ - [replace-by-index.js: 指定したインデックス範囲の文字列を置換](https://gist.github.com/think49/3f6e788c02d4983b82173f7ae4c9df9d)
63
+
64
+ ```JavaScript
65
+ replaceByIndex('ABC', [0, 1, 'AAA'], [1, 1, 'BBB'], [2, 1, 'CCC']); // "AAABBBCCC"
66
+ ```
67
+
68
+ 課題となるのは、置換元/置換先の文字列長が同値かどうか、です。
69
+
70
+ > ```JavaScript
71
+ > var str = "Hello,World!";
72
+ > console.log(str);
73
+ > var str_1 = str.substr(0, 6) //"Hello"を切り出し
74
+ > var str_2 = str.substr(11, 1); //"!"を切り出し
75
+ > str = str_1 + "Japan" + str_2;
76
+ > console.log(str); //“Hello,Japan!”と出力
77
+ > ```
78
+
79
+ `"World"` と `"JavaScript"` の文字列長は異なるので、**可変長**の置換を期待しています。
80
+ 何が問題かというと、1回目の置換跡に2回目の置換処理を実行する場合、**文字列長が変わるとインデックス値もずれる**のです。
81
+ これは2つの解決法があります。
82
+
83
+ - 置換前後の文字列長の差分をとって、インデックス値を補正する
84
+ - 常に「置換前の文字列」から文字列を切り出す
85
+
86
+ replace-by-index.js では後者を採用しました。
87
+
88
+ ---
89
+
90
+ なお、既に置換されている範囲を何度も検索してしまう非効率性は `String#slice` を使用する関係上、無視しています。
91
+ 置換元文字列を slice で切り出せば対応可能ですが、インデックス値も補正しなければならず、諸々の補正処理を全て実行すると、パフォーマンスが落ちる可能性がありました(実験してはいないので、興味があれば検証してみて下さい)。
92
+
93
+ `String#replace` で正規表現指定する方法を採用すれば、1回ずつしか消費されない為、この問題に対応できます。
94
+ 正規表現を動的生成し、一度に置換すれば要件を達成できるでしょう。
95
+ `[\s\S]` は効率が良いとはいえない為、`s` フラグを実装しているブラウザのみで問題がなければ、ですが。
96
+
97
+ ### 実行速度の確認
98
+
99
+ 「理論値」と「実測値」が異なる事は往々にしてあります。
100
+ 今回、回答するにあたって実測値を基にしてはいない為、是非、実測して確認してみてください。
101
+
102
+ - [実行の測定とカウント  \|  Tools for Web Developers  \|  Google Developers](https://developers.google.com/web/tools/chrome-devtools/console/track-executions?hl=ja)
103
+
104
+ ### 質問の仕方について
105
+
106
+ 追加条件でコードの難易度が格段に上がりました。
107
+ 回答の練り直しも余儀なくされましたので、今後は質問文に全ての条件を書くようにしてください。
108
+ また、補足要求を受けて追加情報は質問を [編集] して、質問文に追記してください。
109
+ 全てのコメントを読んで質問の全容を読み取るのはそれなりに労力がかかります。
110
+
55
111
  Re: Y.NINOMIYA さん

5

不要なgフラグを削除、^ を追加

2019/11/02 16:29

投稿

think49
think49

スコア18194

answer CHANGED
@@ -10,18 +10,18 @@
10
10
 
11
11
  ### String.prototype.replace
12
12
 
13
- 一応、正規表現を駆使すれば、ひとつのメソッドで実現は可能です。
13
+ 一応、正規表現を駆使すれば、つのメソッドで実現は可能です。
14
14
 
15
15
  ```JavaScript
16
16
  /**
17
17
  * レガシーコード
18
18
  */
19
- 'Hello, World!'.replace(/([\s\S]{7})[\s\S]{5}/g, '$1JavaScript'); // "Hello, JavaScript!"
19
+ 'Hello, World!'.replace(/^([\s\S]{7})[\s\S]{5}/, '$1JavaScript'); // "Hello, JavaScript!"
20
20
 
21
21
  /**
22
22
  * ES2018コード
23
23
  */
24
- 'Hello, World!'.replace(/(?<=.{7}).{5}/gs, 'JavaScript'); // "Hello, JavaScript!"
24
+ 'Hello, World!'.replace(/(?<=^.{7}).{5}/s, 'JavaScript'); // "Hello, JavaScript!"
25
25
  ```
26
26
 
27
27
  ### String.prototype.slice
@@ -42,7 +42,7 @@
42
42
  text.data; // "Hello, JavaScript!"
43
43
  ```
44
44
 
45
- キャッシュ付き関数
45
+ ユーザ定義関数(テストノードをキャッシュ)
46
46
 
47
47
  ```JavaScript
48
48
  const replaceData = (text =>

4

キャッシュ付き関数

2019/11/01 10:10

投稿

think49
think49

スコア18194

answer CHANGED
@@ -40,7 +40,16 @@
40
40
  const text = new Text('Hello, World!');
41
41
  text.replaceData(7, 5, 'JavaScript');
42
42
  text.data; // "Hello, JavaScript!"
43
+ ```
43
44
 
45
+ キャッシュ付き関数
46
+
47
+ ```JavaScript
48
+ const replaceData = (text =>
49
+ (string, offset, count, data) => (text.data = string, text.replaceData(offset, count, data), text.data)
50
+ )(new Text);
51
+
52
+ replaceData('Hello, World!', 7, 5, 'JavaScript');
44
53
  ```
45
54
 
46
55
  Re: Y.NINOMIYA さん

3

; 付け忘れ

2019/10/31 13:21

投稿

think49
think49

スコア18194

answer CHANGED
@@ -38,8 +38,9 @@
38
38
 
39
39
  ```JavaScript
40
40
  const text = new Text('Hello, World!');
41
- text.replaceData(7, 5, 'JavaScript')
41
+ text.replaceData(7, 5, 'JavaScript');
42
42
  text.data; // "Hello, JavaScript!"
43
+
43
44
  ```
44
45
 
45
46
  Re: Y.NINOMIYA さん

2

CharacterData.prototype.replaceData

2019/10/31 12:53

投稿

think49
think49

スコア18194

answer CHANGED
@@ -32,4 +32,14 @@
32
32
  string.slice(0,7) + 'JavaScript' + string.slice(12); // "Hello, JavaScript!"
33
33
  ```
34
34
 
35
+ ### CharacterData.prototype.replaceData
36
+
37
+ DOM APIに目的の関数があるようですね。
38
+
39
+ ```JavaScript
40
+ const text = new Text('Hello, World!');
41
+ text.replaceData(7, 5, 'JavaScript')
42
+ text.data; // "Hello, JavaScript!"
43
+ ```
44
+
35
45
  Re: Y.NINOMIYA さん

1

ES2019 正規表現の修正

2019/10/31 12:52

投稿

think49
think49

スコア18194

answer CHANGED
@@ -21,7 +21,7 @@
21
21
  /**
22
22
  * ES2018コード
23
23
  */
24
- 'Hello, World!'.replace(/(?<=[\s\S]{7}).{5}/gs, 'JavaScript'); // "Hello, JavaScript!"
24
+ 'Hello, World!'.replace(/(?<=.{7}).{5}/gs, 'JavaScript'); // "Hello, JavaScript!"
25
25
  ```
26
26
 
27
27
  ### String.prototype.slice