Correct substring position after replacement

I have a function that is provided by the user:

function replace_function(string) { return string.replace(/:smile:/g, '⻇') .replace(/(foo|bar|baz)/g, 'text_$1'); } 

and I have an input line as follows:

 var input = 'foo bar :smile: xxxx'; 

and I have a number from 0 to the length of the input string, which I use as a substring to split the string.

I need to find the number (position) that will correspond to the output line after replacement, so that the split is in the same visual place. Separation is just for visualization. I only need a number.

The output string can have the same length, this applies only to the case when the length of the input and output is different (for example, the width function and the input string)

 function replace_function(string) { return string.replace(/:smile:/g, '⻇') .replace(/(foo|bar|baz)/g, 'text_$1'); } var textarea = document.querySelector('textarea'); var pre = document.querySelector('pre'); function split() { var input = textarea.value; var output = replace_function(input); // find position for output var position = textarea.selectionStart; var split = [ output.substring(0, position), output.substring(position) ]; pre.innerHTML = JSON.stringify(split); } textarea.addEventListener('click', split); 
 <textarea>xxx foo xxx bar xxx :smile: xxxx</textarea> <pre></pre> 

when you click in the middle of the word to be replaced, the position / split should be after the output word. If you click before, between, or after a word, the position must be in the same place (the position in each case will be different from the corresponding place)

UPDATE : here is my code that works for: smile: only input, but you need to have the text before: smile: (input = ": smile: asdas" and the position in the middle of the smile and the position off)

it works for foo replaced by text_foo, but not when there is: smile: before foo (input "asd: smile: asd foo").

 var get_position = (function() { function common_string(formatted, normal) { function longer(str) { return found && length(str) > length(found) || !found; } var formatted_len = length(formatted); var normal_len = length(normal); var found; for (var i = normal_len; i > 0; i--) { var test_normal = normal.substring(0, i); var formatted_normal = replace_function(test_normal); for (var j = formatted_len; j > 0; j--) { var test_formatted = formatted.substring(0, j); if (test_formatted === formatted_normal && longer(test_normal)) { found = test_normal; } } } return found || ''; } function index_after_formatting(position, command) { var start = position === 0 ? 0 : position - 1; var command_len = length(command); for (var i = start; i < command_len; ++i) { var substr = command.substring(0, i); var next_substr = command.substring(0, i + 1); var formatted_substr = replace_function(substr); var formatted_next = replace_function(next_substr); var substr_len = length(formatted_substr); var next_len = length(formatted_next); var test_diff = Math.abs(next_len - substr_len); if (test_diff > 1) { console.log('return ' + i); return i; } } } return function get_formatted_position(position, command) { var formatted_position = position; var string = replace_function(command); var len = length(string); var command_len = length(command); if (len !== command_len) { var orig_sub = command.substring(0, position); var orig_len = length(orig_sub); var sub = replace_function(orig_sub); var sub_len = length(sub); var diff = Math.abs(orig_len - sub_len); if (false && orig_len > sub_len) { formatted_position -= diff; } else if (false && orig_len < sub_len) { formatted_position += diff; } else { var index = index_after_formatting(position, command); var to_end = command.substring(0, index + 1); //formatted_position -= length(to_end) - orig_len; formatted_position -= orig_len - sub_len; if (orig_sub && orig_sub !== to_end) { var formatted_to_end = replace_function(to_end); var common = common_string(formatted_to_end, orig_sub); var re = new RegExp('^' + common); var before_end = orig_sub.replace(re, ''); var to_end_rest = to_end.replace(re, ''); var to_end_rest_len = length(replace_function(to_end_rest)); if (before_end && orig_sub !== before_end) { var commnon_len = length(replace_function(common)); formatted_position = position - length(before_end) + to_end_rest_len; } } } if (formatted_position > len) { formatted_position = len; } else if (formatted_position < 0) { formatted_position = 0; } } return formatted_position; }; })(); function length(str) { return str.length; } function replace_function(string) { return string.replace(/:smile:/g, '⻇') .replace(/(foo|bar|baz)/g, 'text_$1'); } var textarea = document.querySelector('textarea'); var pre = document.querySelector('pre'); function split() { var input = textarea.value; var output = replace_function(input); // find position for output var position = get_position(textarea.selectionStart, input); var split = [ output.substring(0, position), output.substring(position) ]; pre.innerHTML = JSON.stringify(split); } textarea.addEventListener('click', split); 
 <textarea>xxxx :smile: xxxx :smile: xxx :smile:</textarea> <pre></pre> 
0
javascript string
source share
1 answer

To do this, you will need to perform a replace operation using the RegExp#exec loop and keep track of how changes affect the position, something along these lines (but this will probably be optimized):

 function trackingReplace(rex, string, replacement, position) { var newString = ""; var match; var index = 0; var repString; var newPosition = position; var start; rex.lastIndex = 0; // Just to be sure while (match = rex.exec(string)) { // Add any of the original string we just skipped start = rex.lastIndex - match[0].length; if (index < start) { newString += string.substring(index, start); } index = rex.lastIndex; // Build the replacement string. This just handles $$ and $n, // you may want to add handling for $`, $', and $&. repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) { if (c0 == "$") return "$"; return match[c0]; }); // Add on the replacement newString += repString; // If the position is affected... if (start < position) { // ... update it: if (rex.lastIndex < position) { // It after the replacement, move it newPosition = Math.max(0, newPosition + repString.length - match[0].length); } else { // It *in* the replacement, put it just after newPosition += repString.length - (position - start); } } } // Add on any trailing text in the string if (index < string.length) { newString += string.substring(index); } // Return the string and the updated position return [newString, newPosition]; } 

Here is a snippet showing us that we are checking that with different positions:

 function trackingReplace(rex, string, replacement, position) { var newString = ""; var match; var index = 0; var repString; var newPosition = position; var start; rex.lastIndex = 0; // Just to be sure while (match = rex.exec(string)) { // Add any of the original string we just skipped start = rex.lastIndex - match[0].length; if (index < start) { newString += string.substring(index, start); } index = rex.lastIndex; // Build the replacement string. This just handles $$ and $n, // you may want to add handling for $`, $', and $&. repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) { if (c0 == "$") return "$"; return match[c0]; }); // Add on the replacement newString += repString; // If the position is affected... if (start < position) { // ... update it: if (rex.lastIndex < position) { // It after the replacement, move it newPosition = Math.max(0, newPosition + repString.length - match[0].length); } else { // It *in* the replacement, put it just after newPosition += repString.length - (position - start); } } } // Add on any trailing text in the string if (index < string.length) { newString += string.substring(index); } // Return the string and the updated position return [newString, newPosition]; } function show(str, pos) { console.log(str.substring(0, pos) + "|" + str.substring(pos)); } function test(rex, str, replacement, pos) { show(str, pos); var result = trackingReplace(rex, str, replacement, pos); show(result[0], result[1]); } for (var n = 3; n < 22; ++n) { if (n > 3) { console.log("----"); } test(/([f])([o])o/g, "test foo result foo x", "...$2...", n); } 
 .as-console-wrapper { max-height: 100% !important; } 

And here your snippet is updated to use it:

 function trackingReplace(rex, string, replacement, position) { var newString = ""; var match; var index = 0; var repString; var newPosition = position; var start; rex.lastIndex = 0; // Just to be sure while (match = rex.exec(string)) { // Add any of the original string we just skipped start = rex.lastIndex - match[0].length; if (index < start) { newString += string.substring(index, start); } index = rex.lastIndex; // Build the replacement string. This just handles $$ and $n, // you may want to add handling for $`, $', and $&. repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) { if (c0 == "$") return "$"; return match[c0]; }); // Add on the replacement newString += repString; // If the position is affected... if (start < position) { // ... update it: if (rex.lastIndex < position) { // It after the replacement, move it newPosition = Math.max(0, newPosition + repString.length - match[0].length); } else { // It *in* the replacement, put it just after newPosition += repString.length - (position - start); } } } // Add on any trailing text in the string if (index < string.length) { newString += string.substring(index); } // Return the string and the updated position return [newString, newPosition]; } function replace_function(string, position) { var result = trackingReplace(/:smile:/g, string, '⻇', position); result = trackingReplace(/(foo|bar|baz)/g, result[0], 'text_$1', result[1]); return result; } var textarea = document.querySelector('textarea'); var pre = document.querySelector('pre'); function split() { var position = textarea.selectionStart; var result = replace_function(textarea.value, position); var string = result[0]; position = result[1]; var split = [ string.substring(0, position), string.substring(position) ]; pre.innerHTML = JSON.stringify(split); } textarea.addEventListener('click', split); 
 <textarea>:smile: foo</textarea> <pre></pre> 
+1
source share

All Articles