Compare two sentences and identify wrong words in javascript - javascript

I want to compare two sentences. Here is the example:
Correct: Experts believe industrial development will help the economy.
Given: Xperts does believe that development won't economy.
Expected Output:
Experts Xperts does believe industrial that development will won't help the economy.
I have tried to compare string by splitting the both words and checking them.
let given= "Xperts does believe that development won't economy.";
let correct= "Experts believe industrial development will help the economy.";
function checkSentence(given,correct){
let final='';
for(i=0; i<given.length; i++){
if(given[i].trim()==correct[i].trim()){
final += given[i]+' ';
}else{
final += "<i>given[i]</i> <b>correct[i]</b>";
}
}
return final;
}

I would solve the problem recursively with the following steps:
Try to find the closest matching word in each sentence
If match found: Split each sentence at the matching word and run the same function on each side
If no match found: Both sentences have nothing in common and you can return each sentence formatted in the way you want
Note: I start and try to find the longest matching word because those are most indicative of sentence structure rather than searching for 'it' and 'and'
const correct= "Experts believe industrial development will help the economy.";
const given= "Xperts does believe development that won't economy.";
const correctArray = correct.split(" ")
const givenArray = given.split(" ")
// Returns [correctIndex, givenIndex] if match found or [-1, -1] if no match found
function findIndexOfLongestMatchingWord(correctArray, givenArray) {
// Create an array of word length and its index for the correct word array
const correctWordLengthIndexArray = correctArray.map((word, index) => [word.length, index]).sort(([length1, index1], [length2, index2]) => length2 - length1)
for(let matchingIndex = 0; matchingIndex < correctArray.length; matchingIndex++) {
const correctArrayIndex = correctWordLengthIndexArray[matchingIndex][1]
const correctArrayWord = correctArray[correctArrayIndex]
const foundIndexOfGivenWord = givenArray.findIndex(givenWord => givenWord === correctArrayWord)
if(foundIndexOfGivenWord != -1) return [correctArrayIndex, foundIndexOfGivenWord];
}
return [-1,-1]
}
function formatCorrectArray(correctArray) {
return correctArray.length == 0 ? "" : `<b>${correctArray.join(" ")}</b>`
}
function formatGivenArray(givenArray) {
return givenArray.length == 0 ? "" : `<i>${givenArray.join(" ")}</i>`
}
function findDifferenceRecursively(correctArray, givenArray) {
// If either array empty there is nothing to compare, return each one formatted
if(correctArray.length == 0 || givenArray.length == 0) {
return formatCorrectArray(correctArray) + formatGivenArray(givenArray)
}
const [correctIndex, givenIndex] = findIndexOfLongestMatchingWord(correctArray, givenArray);
if (correctIndex != -1) {
// Split each string at their index and run find difference on each side of the indexes;
const leftCorrect = correctArray.slice(0, correctIndex)
const rightCorrect = correctArray.slice(correctIndex + 1)
const leftGiven = givenArray.slice(0, givenIndex)
const rightGiven = givenArray.slice(givenIndex + 1)
// Run function on words on each side
return findDifferenceRecursively(leftCorrect, leftGiven) + ` ${correctArray[correctIndex]} ` + findDifferenceRecursively(rightCorrect, rightGiven)
} else {
return formatCorrectArray(correctArray) + formatGivenArray(givenArray)
}
}
const result = findDifferenceRecursively(correctArray, givenArray)
// Returns "<b>Experts</b><i>Xperts does</i> believe <b>industrial</b> development <b>will help the</b><i>that won't</i> economy. "
console.log(result)

Related

removing the second matched word from a string?

I got a string
For example:
This is for trails and I want to learn Js and Coding and Development
The above mentioned line as a string
function trail(sen){
var cat = "and"
var fin = sen.indexOf(cat);
if(fin > 0){
var last = sen.substring(0, fin)
}
else{
var last = sen;
}
return last;
}
console.log(
trail("This is for trails and I want to learn Js and Coding and Development ")
);
I am trying to find the index of the second "and" in a string rather than the first one.
and get the string part from index 0 to that second "and"
Could you please provide the better approach ?
You can use split together with join to achieve this, like so:
const myStr = 'This is for trails and I want to learn Js and Coding and Development'
const subStr = 'and'
const splitted = getSplitted(myStr, subStr, 2) // Splits before the "N th" ocurrence of subStr
console.log(splitted)
function getSplitted(str, subStr, idx) {
return str.split(subStr, idx).join(subStr);
}
You can first find the second occurrence and then remove it via simple slice.
This method also supports regular expressions as pattern.
/**
* Find the n-th occurrence of given pattern in a string.
* #param { string } str The string to be examined.
* #param { string | RegExp } pattern The pattern to be matched.
* #param { number } n Starting index.
* #return { [number, string | RegExpExecArray] } The index & the match result. `[-1, null]` if pattern occurs less than n times.
*/
function findNth(str, pattern, n = 1) {
// The total processed index & and the last match
let index = 0, result;
for(; n--; ) {
// Index of the next match relative to the end of the last one
let offset = -1;
if(pattern instanceof RegExp) {
const match = pattern.exec(str);
if(match !== null) {
offset = match.index;
result = match[0];
}
}
else { // string case
offset = str.indexOf(pattern);
result = pattern;
}
// If none is matched
if(offset === -1)
return [-1, null];
// Seek over the match result
offset += result.length;
str = str.slice(offset);
index += offset;
}
// Gotta go back to the start of the last match
index -= result.length;
return [index, result];
}
/** Remove the n-th occurrence of given pattern out of a string. */
function removeNth(str, pattern, n = 1) {
const result = findNth(str, pattern, n);
if(result[0] === -1)
return str;
return str.slice(0, result[0]) + str.slice(result[0] + result[1].length);
}
{
const str = 'This is for trails and I want to learn Js and Coding and Development';
console.log(removeNth(str, 'and', 2));
console.log(removeNth(str, /\s*and/, 2));
}
Use split
sen.split(cat, 2) // This line will divide the syntax into an array of two elements till second "and" occurrence
// ['This is for trails ', ' I want to learn Js ']
Then you need to join them to add the first and
sen.split(cat, 2).join(cat)
And to get the length
sen.split(cat, 2).join(cat).length
let str = "This is for trails and I want to learn Js and Coding and Development".split("and", 2).join("");
console.log(str);

JavaScript: How can I split a decimal number into 2 part according to where the first zero occurs after a non-zero integer

Sorry if the title sounds confusing. Basically what I am trying to do is to split a decimal number like this 0.1000 into two part - 1. 0.1 and 000 so I can render them differently with different styles.
Check out this screenshot
All the numbers are represented in strings. The tricky part is that we cannot split the number using number.split('0') since we only want to split at the first zero that appears after a non-zero integer.
Not sure how I can do this.
If I did not misunderstand what you are trying to achieve, you can do it with a regex that only matches unlimited zeros that are at the end of the given string like follows:
function markNumber(num) {
return num.replace( /(0{1,})$/g, '<span>$1</span>')
}
const number = 1.2345670089
let renderStyle1 = ''
let renderStyle2 = ''
const string = String(number) + '.'
const parts = string.split('.')
const decimals = parts[1]
const decimalsArray = Array.from(decimals);
// From MDN: The findIndex() method returns the index of the first element in the array that satisfies the provided testing function. Otherwise -1 is returned.
const firstIndexOfZero = decimalsArray.findIndex(x => x === '0');
// From MDN: The slice() method returns a shallow copy of a portion of an array into a new array object selected from start to end (end not included) where start and end represent the index of items in that array. The original array will not be modified.
if(firstIndexOfZero === -1){
renderStyle1 = parts[0] + parts[1]
} else {
renderStyle1 = parts[0] + decimalsArray.slice(0, firstIndexOfZero).join('') // using .join method to convert array to string without commas
renderStyle2 = decimalsArray.slice(firstIndexOfZero, decimalsArray.length).join('') // using .join method to convert array to string without commas
}
console.log(renderStyle1) // "1234567"
console.log(renderStyle2) // "0089"
Messy, and, probably, can be improved, but this should work:
let re = /(\d*\.[1-9]*?)(0.*)/;
["1000", "1.01", "1.10", "1.000", "1.34043"].map((str) =>
str.split(re).filter((entry) => entry !== "")
);
Here's my regex function
const number = ['0.1000', '2.534300', '1.2000', '1.004334000'];
function split_float(num) {
const reg = /^(\d*\.\d*[^0])(0*)$/g;
const [, ...matches] = [...num.matchAll(reg)][0];
return matches;
}
console.log(number.map(split_float));
here is my answer. It uses split and substring to achieve what you want. Tried it in w3school's tryit editor. Handles all of your data in screenshot pretty well:
function myFunction() {
var str = "0.01200";
var partone = str.split(".")[0];
var temp = str.split(".")[1];
for (var i=0; i<temp.length; i++){
if (temp[i] != 0 && temp[i+1] == 0){
break;
}
}
var parttwo = temp.substring(i+1);
partone = partone + "." + temp.substring(0, i+1);
var res = "partOne = " + partone + " and partTwo = " + parttwo;
document.getElementById("demo").innerHTML = res;
}
Here is the screenshot:

if Input: 8-e Expected Output: 2|4|6|8

Question 2: The input consist of a string, "o" represents odd number, "e" represents even number to be printed
Example 1.
Input: 8-e
Expected Output: 2|4|6|8
Example 2.
Input: 6-o
Expected Output: 1|3|5
Example 3.
Input: 1-o
Expected Output: 1
if have tried with for loop, but I'am a beginner so I'am confused with(-e)
const evenOdd = (number) => {
let evenvalue = [];
let oddValue=[];
for(let i =0; i<=number; i++){
if(number%i==0)
evenvalue.push(i);
console.log(evenvalue);
}if(number%i!=0){
oddValue.push(i);
console.log(oddValue);
}
};
evenOdd(9);
You could take a while statement and get a start value of one plus an offset of one if the wanted type is even. Then iterate and add the value to the result set until the value is greater than the maximum value.
function fn(request) {
var [max, type] = request.split('-'),
i = 1 + (type === 'e'),
result = [];
while (i <= max) {
result.push(i);
i += 2;
}
return result;
}
console.log(...fn('8-e'));
console.log(...fn('6-o'));
console.log(...fn('1-o'));
You will need to extract the letter and the number from you string first. One easy way to do that :
const evenOdd = (s) => {
let odd = s.length-1 ==='o';
let number = Number(s.substring(0, s.length-2));
let evenvalue = [];
...
if(odd){...} else {...}
};
You could also use split() or if the pattern was more complicated, a Regex.
You can split on - and add based on type add values upto the number
Split the given input by -, first value represents max number and second represents it's type
Check the type if it is even add the even values start from 2 and upto to the max number else start from 1, and join them with | in the end
let func = (input) => {
let [num, type] = input.split('-')
let arr = []
let i = 1 + (type === 'e')
while (i <= num) {
arr.push(i)
i += 2
}
return arr.join('|')
}
console.log(func('8-e'))
console.log(func('1-o'))
console.log(func('6-o'))
Basically, don't supply a number to the function, supply a string and then parse the string. That is, don't try and give the function 9-e, give it '9-e'.
Get the parts of the input by splitting on -.
Turn the number into a number.
Give 0 for even, 1 for odd (x % 2 is 0 for even number, 1 for odd).
Build the results.
function listNumbers(constraint)
{
const parts = constraint.split('-');
const number = Number(parts[0]);
const numberType = parts[1] === 'e' ? 0:1;
let result = [];
for(let i = 1; i <= number; i++)
{
if(i%2 === numberType)
{
result.push(i);
}
}
return result;
}
console.log(listNumbers('8-e'));
Or if you want make the code look clever:
function listNumbers(constraint)
{
const parts = constraint.split('-');
const number = Number(parts[0]);
const numberType = parts[1] === 'e' ? 0:1;
return Array.from(Array(number), (x,i) => i + 1 ).filter(x => x%2 == numberType);
}
console.log(listNumbers('8-e'));

JavaScript Convert Names to Uppercase, Except Mc/Mac/etc

Everything I can find by searching is people wanting to convert to sentence/title case from lower/upper/random case. That's the opposite of my problem.
What I have is already correct, I want to convert it to uppercase except for the "c" or "ac" etc. So McDonald becomes McDONALD, MacDonald becomes MacDONALD, etc.
Probably the best way is separating out the lower-case letters that occur between two upper-case letters, either before or after running toUpperCase(), but my brain is fried at the moment so I'm not sure how to go about it.
It's for an After Effects expression, controlling the display so I can have sentence case in one composition and upper case in another, from the same source layer. So I know input will be perfect.
You can try something like this:
const input = "MacDonald";
const matches = input.match(/([A-Z][a-z]*)/g);
const output = matches.length > 1 ?
matches.reduce((acc, match, index) => {
if (index === 0) {
return match;
}
return acc + match.toUpperCase();
}) :
input.toUpperCase();
First we take the input apart by matching it against a simple regular expression. The match method in the example will return ["Mac","Donald"].
Then, if there is only one match, we return it in uppercase.
In case of multiple matches, we construct the result by concatenating uppercase parts except for the first part.
Here's a version for a whole sentence:
const input = "Old MacDonald is a fine man.";
const output = input
.split(/\s/)
.map(word => {
const matches = word.match(/([A-Z][a-z]*)/g);
if (!matches || matches.length === 1) {
return word.toUpperCase();
}
return matches.reduce((acc, match, index) => {
return index === 0 ? match : acc + match.toUpperCase();
});
})
.join(' ');
// output == "OLD MacDONALD IS A FINE MAN."
Sami Hult's answer covers most of the bases, but unfortunately refuses to work in After Effects due to syntax issues and map() and reduce() not being supported, and I wanted to make one small tweak, all-capsing only the last portion rather than all but the first (to account for a possible double prefix).
So based on that code, I came up with this:
function str_uppercase(str) {
str = str.split(/\s/);
var output = [];
for (i = 0; i < str.length; i++) {
var word = str[i];
var matches = word.match(/([A-Z][a-z]*)/g);
if (!matches || matches.length === 1) {
word = word.toUpperCase();
} else {
var x = matches.length - 1;
matches[x] = matches[x].toUpperCase();
word = matches.join('');
}
output.push(word);
}
return output.join(' ');
}
console.log(str_uppercase('Old MacMcDonald Had a farm'));
// => OLD MacMcDONALD HAD A FARM
The code below assumes a string prefix to be one capital letter character followed by one or more small letter characters followed by one capital letter character and always at the beginning of the whole word.
The prefix will be retained as it is and the rest will be capitalized.
const input = [
"McDonald",
"MacDonald",
"Mcdonald",
"mcDonald",
"mcdonald"
];
// Function for converting to special uppercase
const specialUpperCase = function(item) {
// Find prefix (one or more lower case characters between upper case character - at the beginning)
const match = item.match(/^[A-Z][a-z]+[A-Z]/);
if (match) {
// If prefix, capitalize only the remaining
return match[0] + item.substr(match[0].length).toLocaleUpperCase();
}
// If no prefix, capitalize the whole string
return item.toLocaleUpperCase();
};
const output = input.map(specialUpperCase);
console.log(output);
The easiest solution would probably be to keep a list of prefixes and test if the word starts with one of these:
//Prefixes to look for
var prefixToKeep = [
"Mac",
"Mc"
];
//Selective uppercase function
function selectiveUpperCase(text) {
//Find words by wordBoundaries
return text.replace(/\b\w+\b/gim, function (word) {
//Test for prefixes
for (var prefixToKeepIndex = 0; prefixToKeepIndex < prefixToKeep.length; prefixToKeepIndex++) {
var prefix = prefixToKeep[prefixToKeepIndex];
if (word.indexOf(prefix) === 0) {
//prefix matches. Return prefix as is + rest of the word in uppercase
return word.slice(0, prefix.length) + word.slice(prefix.length).toUpperCase();
}
}
//No prefix found, return word as uppercase
return word.toUpperCase();
});
}
//TEST
var text = "Old MacDonald had a farm\nE-I-E-I-O\nAnd on this farm he had a cow\nE-I-E-I-O\nWith a moo-moo here\nAnd a moo-moo there\nHere a moo, there a moo\nEverywhere a moo-moo\nOld MacDonald had a farm\nE-I-E-I-O ";
console.log(selectiveUpperCase(text));
EDIT 1 - Upper-Lower-Upper Test
In response to the comments, this newer version tests for Upper-Lower-Upper cases and uses its findings to determine which parts to uppercase.
//Selective uppercase function
function selectiveUpperCase(text) {
//Find words by wordBoundaries
return text.replace(/\b\w+\b/gim, function (word) {
var reg = /[A-Z]+[a-z]+[A-Z]\w+/gm;
//Test for Upper-Lower-Upper combo
if (reg.test(word) || reg.test(word)) {
//start at index 1
var l = 0;
while (l++ < word.length) {
//move up the word and test for an uppercase letter
if (word[l] === word[l].toUpperCase()) {
break;
}
l++;
//return the first slice (the prefix) as is and uppercase the rest
return word.slice(0, l) + word.slice(l).toUpperCase();
}
}
//No prefix found, return word as uppercase
return word.toUpperCase();
});
}
//TEST
var text = "Old MacDonald had a farm\nE-I-E-I-O\nAnd on this farm he had a cow\nE-I-E-I-O\nWith a moo-moo here\nAnd a moo-moo there\nHere a moo, there a moo\nEverywhere a moo-moo\nOld McDonald had a farm\nE-I-E-I-O ";
console.log(selectiveUpperCase(text));
ES6 version with RegEx, you can try below function replaceStr()
const replaceStr = str => str.replace(/(^[A-Z])([a-z]{1,2})(.+)/,
(_, p1, p2, p3) => p1.toUpperCase() + p2 + p3.toUpperCase());

Javascript: How can I loop through a word and slice off the characters after the first vowel?

I am trying to make a function that loops through a word, identifies the first vowel found (if any) in the word, and then splits up the word after the vowel.
example input: 'super'
example output: 'su', 'per'
function consAy(word){
if(word[i].indexOf("a" >= 0) || word[i].indexOf("e" >= 0) || word[i].indexOf("i" >= 0) || word[i].indexOf("o" >= 0) || word[i].indexOf("u" >= 0)){
}
One way to do it is to use a regular expression to .match() the pattern you are looking for:
function consAy(word){
var result = word.match(/^([^aeiou]*[aeiou])(.+)$/i)
return result ? result.slice(1) : [word]
}
console.log( consAy('super') )
console.log( consAy('AMAZING') )
console.log( consAy('hi') )
console.log( consAy('why') )
The function I've shown returns an array. If there was a vowel that was not at the end then the array has two elements. If there was only a vowel at the end, or no vowel, then the array has one element that is the same as the input string.
A brief breakdown of the regex /^([^aeiou]*[aeiou])(.+)$/i:
^ // beginning of string
[^aeiou]* // match zero or more non-vowels
[aeiou] // match any vowel
.+ // match one or more of any character
$ // end of string
...where the parentheses are used to create capturing groups for the two parts of the string we want to separate, and the i after the / makes it case insensitive.
The .match() method returns null if there was no match, so that's what the ternary ?: expression is for. You can tweak that part if you want a different return value for the case where there was no match.
EDIT: I was asked for a non-regex solution. Here's one:
function consAy(word){
// loop from first to second-last character - don't bother testing the last
// character, because even if it's a vowel there are no letters after it
for (var i = 0; i < word.length - 1; i++) {
if ('aeiou'.indexOf(word[i].toLowerCase()) != -1) {
return [word.slice(0, i + 1), word.slice(i + 1)]
}
}
return [word]
}
console.log( consAy('super') )
console.log( consAy('AMAZING') )
console.log( consAy('hi') )
console.log( consAy('why') )
This assumes a reasonably modern browser, or Node.
const string = "FGHIJK";
const isVowel = c => c.match(/[AEIOU]/i);
const pos = [...string].findIndex(isVowel);
const truncatedString = `${[...string].slice(0, pos + 1)}`;
truncatedString; // "FGHI"
Edit
As has been pointed out, the above is significantly more hassle than it's worth. Without further ado, a much saner approach.
const string = "FGHIJK";
const vowels = /[aeiou]/i;
const truncateAfter = (string, marker) => {
const pos = string.search(marker);
const inString = pos >= 0;
return string.slice(0, inString ? pos : string.length);
};
const truncated = truncateAfter(string, vowels);
Without using a RegEx of any kind. Ye ole fashioned algorithm.
const truncateAfter = (string, markers) => {
let c = "";
let buffer = "";
for (let i = 0, l = string.length; i < l; i += 1) {
c = string[i];
buffer += c;
if (markers.includes(c)) {
break;
}
}
return buffer;
};
const truncatedString = truncateAfter(
"XYZABC",
["A", "E", "I", "O", "U"],
);
With RegEx golf.
const string = "BCDEFG";
const truncatedString = string.replace(/([aeiou]).*/i, "$1");
With a reduction.
const isVowel = c => /[aeiou]/i.test(c);
const last = str => str[str.length - 1];
const truncatedString = [...string].reduce((buffer, c) =>
isVowel(last(buffer)) ? buffer : buffer + c, "");
Via a dirty filter hack, that takes way too much power O(n**2).
const truncatedString = [...string]
.filter((_, i, arr) => i <= arr.search(/[aeiou]/i));
There are others, but I think that's enough to shake the cobwebs out of my brain, for now.
I always like to take opportunities to write incomprehensible array-based code, so with that in mind...
const regexMatcher = pattern => input => {
return input.match(pattern)
};
const splitAtFirstMatch = matcher => arrayLike => {
return [...arrayLike]
.reduce(([pre, post, matchFound], element) => {
const addPre = matchFound || matcher(element);
return [
matchFound ? pre :[...pre, element],
matchFound ? [...post, element] : post,
addPre
];
}, [[],[], false])
.slice(0, 2)
.map(resultArrays => resultArrays.join(''));
};
console.log(splitAtFirstMatch(regexMatcher(/[aeiou]/))('super'));

Categories