I have a string containing multiple sentences. I also have the current cursor/caret position.
I need to be able to extract the current sentence at the given cursor position.
For example, take this string:
This is the first sentence. And this is the second! Finally, this is the third sentence
If the current cursor position is 33 then the cursor is in the second sentence.
In which case, the result returned should be:
And this is the second!
I only need to use the standard sentence definers of .?!
Any help with this would be greatly appreciated.
Although I am expecting regex to be required, if there is a faster alternative using native methods I would be interested in that also.
Here is a way to achieve what you need: use String#split with /[?!.]/g to get an array of sentences and then iterate over the array to sum up the lengths of the sentences found, and if the index is smaller than the count, return the sentence.
function getSentenceByPos(idx, str) {
pos = 0;
array = str.split(/[?!.]/g);
for (var i=0; i<array.length; i++) {
pos += array[i].length + 1;
if (pos >= idx) {
return array[i];
}
}
}// 26 still 1 then `.`. 51 then `!` - 53 is 3rd sentence!
document.write(getSentenceByPos(53, "This is the first sentence. And this is the second! Finally, this is the third sentence"));
I wanted to add an answer that doesn't use regular expression to split up the
string because doing so is quite inefficient and would likely be very slow on
larger chunks of text.
The most efficient way to do it would probably be to use a couple of loops to search, requiring only 2 passes to find the ends of the sentence.
var sentenceFromPos = function (s, pos) {
var len = s.length,
start,
end,
char;
start = pos;
end = pos;
while (start >= 0) {
char = s.charAt(start);
if (char === '.' || char === '?' || char === '!') {
break;
}
start -= 1;
}
while (end < len) {
char = s.charAt(end);
if (char === '.' || char === '?' || char === '!') {
break;
}
end += 1;
}
return s.substring(start + 1, end + 1).trim();
};
var phrase = 'This is the first sentence. And this is the second! Finally, this is the third sentence';
console.log(sentenceFromPos(phrase, 10));
console.log(sentenceFromPos(phrase, 33));
console.log(sentenceFromPos(phrase, 53));
This function will respect cursors over the limits of the phrases (like ! or .)
function getPhrase(string, cursor) {
phrases = string.match(/.*?(!|\.|$)/g)
basecursor = 0;
phrase = phrases[0]
for(ii=0; ii<phrases.length-1; ii++) {
if (basecursor+phrases[ii].length<cursor) {
phrase = phrases[ii+1]
basecursor += phrases[ii].length
}
}
return(phrase)
}
string = "This is the first sentence. And this is the second! Finally, this is the third sentence"
cursor = 0
phrase = getPhrase(string, cursor)
document.write(phrase)
Related
Now I have solved this algorithmic challenge myself but I would like someone to explain the answer below line by line please as I took it from someone else. I do NOT understand it at all and how the answers come to be, even after using pythonTutor.
Challenge: Write a function called findLongestSubstring, which accepts a string and returns the length of the longest substring with all distinct characters.
Edit: I only do NOT understand the ABOVE CODE.
function findLongestSubstring(str) {
let longest = 0;
let seen = {};
let start = 0;
for (let i = 0; i < str.length; i++) {
let char = str[i];
if (seen[char]) {
start = Math.max(start, seen[char]);
}
// index - beginning of substring + 1 (to include current in count)
longest = Math.max(longest, i - start + 1);
// store the index of the next char so as to not double count
seen[char] = i + 1;
}
return longest;
}
// findLongestSubstring("thisisawesome"); // 6
// findLongestSubstring("thecatinthehat"); // 7
My solution:
function findLongestSubstring(str){
if (str.length === 0) return 0;
// track longest length
let longestLength;
// get first subArr
let subStrArr = str.split("").slice(0,1);
// get first subArrLength
let subStrLength = subStrArr.length;
longestLength = subStrLength;
// variable for checking every character
let j = 0;
// for loop
for ( let i = 1; i < str.length; i++ ) {
// if current element don't exist in subArr
if (!subStrArr.includes(str[i])) {
subStrArr = str.split("").slice(j,i+1);
subStrLength = subStrArr.length;
}
// does exist
else {
j++;
i = j;
subStrArr = str.split("").slice(i,i+1);
subStrLength = subStrArr.length;
}
if (subStrLength > longestLength) longestLength = subStrLength;
}
return longestLength;
}
findLongestSubstring("rithmschool"); // 7
A substring with unique characters doesn't obviously have duplicate letters. So as we move forward in our string str, we keep track of the greatest index of all the characters that we have already met in seen. The greatest index of each letter is obviously the one we have seen last since we move from left to right.
Now, in your iteration, when you reach a letter that you find in seen, you need to set start to that character's index tracked by seen to avoid including both letters in your substring. We take the max because start of substring may already be higher because of another double letter that we met earlier:
if (seen[char]) {
start = Math.max(start, seen[char]);
}
Then we just look how long our current substring is and retain it if it's longer than our longest seen.
longest = Math.max(longest, i - start + 1);
And at last we save the index of the character in seen
seen[char] = i + 1;
The order of these operations is important. If we updated seen with the index of the char first then we couldn't check seen for the character and set start based on it.
Is it possible to Find specific character from string and change its position to previous character
for example: Let us say there is say a string: Kù Iù Mù
I want output like : ùK ùI ùM
Yes of course! You can go over the string in loop char by char, look at the next position and if it is the char U want, switch the position!
function switchChar(text, charToFind) {
//temp variable for building result
var result = "";
//loop over original string
for (var i = 0; i < text.length; i++) {
//chack not to jump out of array
if (i + 1 < text.length) {
//if next char is the char im looking for
if (text[i + 1] == charToFind) {
//write next char first
result += charToFind;
//then write original char
result += text[i];
//iterate i once more to jump over next char (I appended two chars in this single cycle)
i++;
//if it is not the char Im looking for
} else {
//write it down
result += text[i];
}
//I am at the end
} else {
//write the char
result += text[i];
}
}
return result;
}
console.log(switchChar('Kù Iù Mù', 'ù'))
Write a function that removes the last vowel in each word in a sentence.
Examples:
removeLastVowel("Those who dare to fail miserably can achieve greatly.")
➞ "Thos wh dar t fal miserbly cn achiev gretly."
removeLastVowel("Love is a serious mental disease.")
➞ "Lov s serios mentl diseas"
removeLastVowel("Get busy living or get busy dying.")
➞ "Gt bsy livng r gt bsy dyng"
What I am doing is
function sumDigProd(arr) {
let ans = arr.split(" ");
let array = [];
for (i = 0; i < ans.length; i++) {
for (j = 0; j < ans[i].length; j++) {
var vowelAtLast;
if (
ans[i][j].toLowerCase() == "a" ||
ans[i][j].toLowerCase() == "e" ||
ans[i][j].toLowerCase() == "i" ||
ans[i][j].toLowerCase() == "o" ||
ans[i][j].toLowerCase() == "u"
) {
vowelAtLast = ans[i][j];
}
}
var idex = ans[i].lastIndexOf(vowelAtLast);
console.log(idex,ans[i],ans[i][idex]);
ans[i].replace(ans[i][idex],'')
array.push(ans[i])
console.log(ans)
}
console.log(ans)
return array.join(" ");
}
console.log(sumDigProd("youuo goalo people."));
Here, console.log(idex,ans[i],ans[i][idex]); gives me the correct output for example:
4 "youuo" "o"
But now when I try to do:
ans[i].replace(ans[i][idex],'')
console.log(ans)
I get
["youuo", "goalo", "people."]
again instead i should get
["youu", "goalo", "people."]
after the first time loop runs... and then at last I get the output as
["youu", "goal", "peopl."]
as per the problem but I get
["youuo", "goalo", "people."]
why are no changes made by the replace method here?
A problem is that
ans[i].replace(ans[i][idex], '')
will only replace the first occurrence of whatever character ans[i][idex] is. Eg
aza
would result in
za
Another issue is that you must use the return value of .replace, else it'll go unused and be irrelevant; you'd want something like
ans[i] = ans[i].replace(ans[i][idex], '')
instead, so that the item at that index in the array is properly reassigned.
But it would probably be easier to use a regular expression: match a vowel, followed by capturing zero or more non-vowels in a capture group, with lookahead matching a space or the end of the string. Then replace with the first capture group, thereby removing the last vowel:
const sumDigProd = str => str.replace(
/[aeiou]([^aeiou]*?)(?= |$)/gi,
'$1'
);
console.log(sumDigProd("youuo goalo people."));
[aeiou]([^aeiou]*?)(?= |$) means:
[aeiou] - Any vowel
([^aeiou]*?) - Match and capture:
[^aeiou]*? Any non-vowel, lazily (capture group), up until
(?= |$) - lookahead matches a space or the end of the string
Then
'$1' - Replace with the capture group
To change your original code, identify the last index of a vowel by iterating from the final index of the string and going backwards. When one is found, reassign the string at ans[i] and .sliceing the portions behind and in front of the found vowel:
function sumDigProd(arr) {
let ans = arr.split(" ");
for (i = 0; i < ans.length; i++) {
for (j = ans[i].length - 1; j >= 0; j--) {
if (
ans[i][j].toLowerCase() == "a" ||
ans[i][j].toLowerCase() == "e" ||
ans[i][j].toLowerCase() == "i" ||
ans[i][j].toLowerCase() == "o" ||
ans[i][j].toLowerCase() == "u"
) {
ans[i] = ans[i].slice(0, j) + ans[i].slice(j + 1);
// go to next word
break;
}
}
}
return ans.join(' ');
}
console.log(sumDigProd("youuo goalo people."));
I'm beginner in JS. I've tried to understand Caesar Cipher ROT13, but it was too complicated for me. So I've tried to write my own code. Here it is below:
function encrip() {
var alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
var str = "Ni Hao";
var string = str.toUpperCase();
for (var i = 0; i < string.length; i++) {
for (var k = 0; k < alphabet.length; k++) {
if(string.charAt(i) == alphabet[k]) {
/* console.log(string.charAt(i) + ' ' + alphabet.indexOf(alphabet[k])); */
}
}
}
}
encrip();
But I am stuck. How to do:
1. Get value from var str and then access to var alphabet , after change each letter from var str value to next 3 from alphabet (var str each element's current position would be changed) For example: Input: Ni Hao ==> output: QL KDR
2. Create universal code, I mean, not only for changing position by 3, but when I give value '5', each element would be changed by next 5 positions from alphabet. So output can be changed when I change its' value
I hope I explained everything clearly. Thanks everyone in advance for help!!
you can use the following function to encrypt english words, the 1st parameter is the string to encrypt and the 2nd for shifting
function encryp(str,pos){
var alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var strUC=str.toUpperCase();
var enc="";
for(var i=0;i<strUC.length;i++){
if(strUC.charAt(i)!=" "){
enc+=alpha.charAt((alpha.indexOf(strUC.charAt(i))+pos)%26)
}
else{
enc+=" "
}
// in your case pos=3
}
return enc;
}
console.log(encryp("NiHao",3));
You don't need two for loops to do this. Iterate over the input string and find the index of each character in the alphabet array, if found add the shift to it to get the encrypted character.
To handle overflow use the modulus operator to cycle through the array.
Also I assume that you are not going use any special symbols to do the encryption.
function encrip(string, shift) {
var alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
string = string.toUpperCase();
let arr = [];
for (var i = 0; i < string.length; i++) {
let char = alphabet.indexOf(string[i]) !== -1 ? alphabet[(alphabet.indexOf(string[i]) %26) + shift] : " ";
arr.push(char);
}
let encryp = arr.join("");
console.log(encryp);
return encryp;
}
encrip("Ni Hao", 3);
First of all, instead of your inner for loop scanning the whole alphabet array, you can use the built-in function indexOf:
alphabet.indexOf('K') // returns 10
Secondly, you'll want to build up your enciphered string in a separate variable. For each letter, get the index of that letter in the alphabet, add your cipher offset parameter to that index and add the resulting letter from the alphabet to your new string. An important step is that when you add to the index of the letter, you want to make sure the resulting index is within range for the alphabet array. You can do that using the % (modulo) operator, which will wrap high values back round to the start of the array. In full:
function encipher(input, offset) {
var alphabet = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
var str = input.toUpperCase();
var result = '';
for (var i = 0; i < str.length; i++) {
letterIndex = alphabet.indexOf(str.charAt(i));
if (letterIndex === -1) {
result += str[i]; // if the letter isn't found in the alphabet, add it to the result unchanged
continue;
}
cipheredIndex = (letterIndex + offset) % alphabet.length; // wrap index to length of alphabet
result += alphabet[cipheredIndex];
}
console.log(result);
}
encipher('Ni Hao', 5); // output: 'SN MFT'
$('.creditCardText').keyup(function() {
var foo = $(this).val().split("-").join(""); // remove hyphens
if (foo.length > 0) {
foo = foo.match(new RegExp('.{1,4}', 'g')).join("-");
}
$(this).val(foo);
});
I found this tutorial on putting dash after every 4 character from here my question is what if the character interval is not constant like in this example it is only after every 4 what if the interval is 3 characters "-" 2 characters "-" 4 characters "-" 3 characters "-" so it would appear like this 123-12-1234-123-123.
In this case, it is more convenient to just write normal code to solve the problem:
function format(input, format, sep) {
var output = "";
var idx = 0;
for (var i = 0; i < format.length && idx < input.length; i++) {
output += input.substr(idx, format[i]);
if (idx + format[i] < input.length) output += sep;
idx += format[i];
}
output += input.substr(idx);
return output;
}
Sample usage:
function format(input, format, sep) {
var output = "";
var idx = 0;
for (var i = 0; i < format.length && idx < input.length; i++) {
output += input.substr(idx, format[i]);
if (idx + format[i] < input.length) output += sep;
idx += format[i];
}
output += input.substr(idx);
return output;
}
$('.creditCardText').keyup(function() {
var foo = $(this).val().replace(/-/g, ""); // remove hyphens
// You may want to remove all non-digits here
// var foo = $(this).val().replace(/\D/g, "");
if (foo.length > 0) {
foo = format(foo, [3, 2, 4, 3, 3], "-");
}
$(this).val(foo);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<input class="creditCardText" />
While it is possible to do partial matching and capturing with regex, the replacement has to be done with a replacement function. In the replacment function, we need to determine how many capturing group actually captures some text. Since there is no clean solution with regex, I write a more general function as shown above.
You can split it using a regular expression. In this case, I'm using a expression to check for non-spaces with interval 3-2-4-3.
The RegExp.exec will return with a "match" array, with the first element containing the actual string. After removing the first element of the match, you can then join them up with dashes.
var mystring = "123121234123"
var myRegexp = /^([^\s]{3})([^\s]{2})([^\s]{4})([^\s]{3})$/g
var match = myRegexp.exec(mystring);
if (match)
{
match.shift();
mystring = match.join("-")
console.log(mystring)
}
Per further comments, the op clarified they need a fixed interval for when to insert dashes. In that case, there are several ways to implement it; I think regular expression would probably be the worst, in other words, overkill and overly complication solution.
Some simpler options would be to create a new character array, and in a loop append character by character, adding a dash too every time you get to the index you want. This would probably be the easiest to write and grok after the fact, but a little more verbose.
Or you could convert to a character array and use an 'insert into array at index'-type function like splice() (see Insert Item into Array at a Specific Index or Inserting string at position x of another string for some examples).
Pass the input value and the indexes to append the separator, first, it will remove the existing separators then just append separators on positions indexes.
export function addSeparators(
input: string,
positions: number[],
separator: string
): string {
const inputValue = input.replace(/-/g, '').split(''); // remove existing separators and split characters into array
for (let i = 0; i < inputValue.length; i++) {
if (positions.includes(i)) inputValue.splice(i, 0, separator);
}
return inputValue.join('');
}