回答編集履歴
7
typo修正
answer
CHANGED
@@ -77,7 +77,7 @@
|
|
77
77
|
> ```
|
78
78
|
|
79
79
|
`"World"` と `"JavaScript"` の文字列長は異なるので、**可変長**の置換を期待しています。
|
80
|
-
何が問題かというと、1回目の置換
|
80
|
+
何が問題かというと、1回目の置換後に2回目の置換処理を実行する場合、**文字列長が変わるとインデックス値もずれる**のです。
|
81
81
|
これは2つの解決法があります。
|
82
82
|
|
83
83
|
- 置換前後の文字列長の差分をとって、インデックス値を補正する
|
6
複数の範囲指定に対応する
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フラグを削除、^ を追加
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}/
|
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}/
|
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
キャッシュ付き関数
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
; 付け忘れ
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
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 正規表現の修正
answer
CHANGED
@@ -21,7 +21,7 @@
|
|
21
21
|
/**
|
22
22
|
* ES2018コード
|
23
23
|
*/
|
24
|
-
'Hello, World!'.replace(/(?<=
|
24
|
+
'Hello, World!'.replace(/(?<=.{7}).{5}/gs, 'JavaScript'); // "Hello, JavaScript!"
|
25
25
|
```
|
26
26
|
|
27
27
|
### String.prototype.slice
|