Recognize name out of user input - javascript

I want to make a basic AI chat in Javascript.
1) If a user says 'Hi, my name is Thore' I want to check what the closest match is with some predefined values.
My array looks like this:
const nameSentences = [`my name is`, `i'm`, `they call me`];
How can I check what the closest match is? In this example it should be the first value of my array.
2) The second part is how I can get the name out of the user input. Is it possible to predefine a place where the variable should stand?
Something like this
const nameSentences = [`my name is ${varName}`, `i'm ${varName}`, `they call me ${varName}`];
And afterwards substring the matching sentence with the user input to save the name the variable?

You can save the different ways you would like to accept a name as Regular Expressions, with the capture for the name in the regular expression. You can get as robust as you'd like with it, but here is a starting point.
Once you find a match, you can stop iterating over the possible variations, you can take the match and output the name.
const nameSentences = [
/i'm (\w+)/i,
/my name is (\w+)/i,
/they call me (\w+)/i
];
function learnName(input) {
let match;
for (let i = 0; i < nameSentences.length; i++) {
match = input.match(nameSentences[i]);
if (match) break;
}
if (match) {
return `Hello, ${match[1]}. It's nice to meet you.`;
} else {
return `I didn't catch that, would you mind telling me your name again?`;
}
}
console.log(learnName('Hi, my name is Thore.'));
console.log(learnName('They call me Bob.'));
console.log(learnName(`I'm Joe.`));
console.log(learnName(`Gibberish`));

Related

.toLowerCase() / .toUpperCase() not working

I'm trying to get better at javascript through codewars.com katas, and I came across an exercice in which things like element[i]=element[i].toLowerCase() doesn't change anything at all.
I would like to have some help with my code, here is the exercice's instructions followed by my code:
(Please note that I'm not very experienced with JS so the code may not be perfect at all)
A string is considered to be in title case if each word in the string
is either:
(a) capitalised (that is, only the first letter of the word
is in upper case) or
(b) considered to be an exception and put entirely into lower case unless it is the first word, which is always capitalised.
Write a function that will convert a string into title case, given an optional list of exceptions (minor words). The list of minor words will be given as a string with each word separated by a space.
Your function should ignore the case of the minor words string -- it should behave in the same way even if the case of the minor word string is changed.
Arguments:
First argument (required): the original string to be converted.
Second argument (optional): space-delimited list of minor words that must always be lowercase except for the first word in the string. The JavaScript/CoffeeScript tests will pass undefined when this argument is unused.
function titleCase(title, minorWords) {
if(title.length==0){return ""}
var titlesplit = title.split(" ")
if(minorWords){
minorWords=minorWords.split(" ")
}
var solutionstring = ""
titlesplit.forEach(element => myfunction(element,minorWords))
solutionstring[0] = solutionstring[0].toUpperCase()
return solutionstring
function myfunction(element,minorWords){
var elementlength= element.length
var i=0
if(minorWords && minorWords.includes(element)){
for(i;i<elementlength;i++){
element[i]=element[i].toLowerCase()
}
}else {
for(i;i<elementlength;i++){
if(i==0){element[i]=element[i].toUpperCase()}
else{element[i]=element[i].toLowerCase()}
}
}
if(solutionstring.length==0){solutionstring=solutionstring+element}else{solutionstring=solutionstring+" "+element}
return
}
}
As pointed out in comments, Strings are immutable in JavaScript.
Additionally, for searching use Maps instead of includes.
Likewise you can see what Set in JavaScript is and easily use Set here.
Added comments for you better understanding.
function titleCase(title, minorWords) {
// Use === for comparison
// Prefer using curly braces even for single statements
if (title.length === 0) {
return "";
}
var titlesplit = title.split(" ");
// Maps/Objects give O(1) search compared to arrays O(n)
// Key,value pairs - similar to dictionary
var minorWordsMap = {};
minorWords.split(" ").forEach(i => minorWordsMap[i.toLowerCase()] = true);
var finalWords = titlesplit.map((element, index) => convertCase(element, index));
finalWords[0] = toPascalCase(finalWords[0]);
return finalWords.join(" ");
function toPascalCase(s) {
s = s.split("");
s[0] = s[0].toUpperCase();
return s.join("");
}
function convertCase(element, index) {
const lElement = element.toLowerCase();
// If element is part of exception words, ignore
if(index !== 0 && minorWordsMap[lElement]) {
return element;
}
// If first element or not in exception list, send Title case
return toPascalCase(lElement);
}
}

Looking for the easiest way to extract an unknown substring from within a string. (terms separated by slashes)

The initial string:
initString = '/digital/collection/music/bunch/of/other/stuff'
What I want: music
Specifically, I want any term (will never include slashes) that would come between collection/ and /bunch
How I'm going about it:
if(initString.includes('/digital/collection/')){
let slicedString = initString.slice(19); //results in 'music/bunch/of/other/stuff'
let indexOfSlash = slicedString.indexOf('/'); //results, in this case, to 5
let desiredString = slicedString.slice(0, indexOfSlash); //results in 'music'
}
Question:
How the heck do I accomplish this in javascript in a more elegant way?
I looked for something like an endIndexOf() that would replace my hardcoded .slice(19)
lastIndexOf() isn't what I'm looking for, because I want the index at the end of the first instance of my substring /digital/collection/
I'm looking to keep the number of lines down, and I couldn't find anything like a .getStringBetween('beginCutoff, endCutoff')
Thank you in advance!
your title says "index" but your example shows you wanting to return a string. If, in fact, you are wanting to return the string, try this:
if(initString.includes('/digital/collection/')) {
var components = initString.split('/');
return components[3];
}
If the path is always the same, and the field you want is the after the third /, then you can use split.
var initString = '/digital/collection/music/bunch/of/other/stuff';
var collection = initString.split("/")[2]; // third index
In the real world, you will want to check if the index exists first before using it.
var collections = initString.split("/");
var collection = "";
if (collections.length > 2) {
collection = collections[2];
}
You can use const desiredString = initString.slice(19, 24); if its always music you are looking for.
If you need to find the next path param that comes after '/digital/collection/' regardless where '/digital/collection/' lies in the path
first use split to get an path array
then use find to return the element whose 2 prior elements are digital and collection respectively
const initString = '/digital/collection/music/bunch/of/other/stuff'
const pathArray = initString.split('/')
const path = pathArray.length >= 3
? pathArray.find((elm, index)=> pathArray[index-2] === 'digital' && pathArray[index-1] === 'collection')
: 'path is too short'
console.log(path)
Think about this logically: the "end index" is just the "start index" plus the length of the substring, right? So... do that :)
const sub = '/digital/collection/';
const startIndex = initString.indexOf(sub);
if (startIndex >= 0) {
let desiredString = initString.substring(startIndex + sub.length);
}
That'll give you from the end of the substring to the end of the full string; you can always split at / and take index 0 to get just the first directory name form what remains.
You can also use regular expression for the purpose.
const initString = '/digital/collection/music/bunch/of/other/stuff';
const result = initString.match(/\/digital\/collection\/([a-zA-Z]+)\//)[1];
console.log(result);
The console output is:
music
If you know the initial string, and you have the part before the string you seek, then the following snippet returns you the string you seek. You need not calculate indices, or anything like that.
// getting the last index of searchString
// we should get: music
const initString = '/digital/collection/music/bunch/of/other/stuff'
const firstPart = '/digital/collection/'
const lastIndexOf = (s1, s2) => {
return s1.replace(s2, '').split('/')[0]
}
console.log(lastIndexOf(initString, firstPart))

how to ensure that my string have two word in typescript

How do I ensure that my string have two words in it using typescript.
The reason I need this so that I would call server only if name is in "first last" name format.
The answer really isn't TypeScript dependent. It's basic JavaScript.
You can use a regular expression to perform a test on the string:
function testString(input){
// Return whether or not there are letters (any amount and any case)
// followed by one space and then more letters (any amount and any case).
// Of course, if you wanted to get more specific about case or character
// counts, adjusting the regular expression would be simple.
return /^[A-Za-z]+ [A-Za-z]+$/.test(input);
}
console.log(testString("Scott"));
console.log(testString("Scott Marcus"));
console.log(testString("Scott\nMarcus"));
Things like this can't be typed with TypeScript, but you can make use of type guards to explicitly indicate that a param value needs to pass a specific test first to be a valid input to a function.
Here's what a "strongly typed" version of Scott's answer could be:
my-custom-types.d.ts
// A string supertype that represents a string that passed our RegEx test
declare type TwoWords = Branded<string, 'two-words'>
// Used to declare an unique primitive type, "nominal types" aren't supported yet
// #see https://github.com/Microsoft/TypeScript/issues/202
declare type Branded<T, U> = T & { '__brand': U }
// FullName in a "first last" name format
declare type FullName = TwoWords
my-test.ts
// This a type guard that casts strings with two words to TwoWords
function isTwoWords(input: string): input is TwoWords {
return /^[A-Za-z]+ [A-Za-z]+$/.test(input)
}
// This is the function that should only receive strings with two words
function showName(name: FullName) {
let [first, last] = name.split(' ') // Can be splited with safety
console.log(`${last}, ${first}`)
}
let name1 = 'John'
let name2 = 'John Doe'
let name3 = 'John Doe Junior'
// Error, you can't assume this string has two words before testing it
showName(name2)
for (let name of [name1, name2, name3]) {
if (isTwoWords(name)) {
// No errors, TS knows that only a TwoWords type reach this line
showName(name)
}
}
You can ensure your string has two words in it possibly containing letters with accents, multiple hyphens within, multiple apostrophes within, and separated by a space with RegEx.
const allowedChars = "a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; // https://stackoverflow.com/a/1073545/188246
const isTwoWordNameRegEx = new RegExp(`^[${allowedChars}]+(['\-][${allowedChars}]+)* [${allowedChars}]+(['\-][${allowedChars}]+)*$`, "i");
isTwoWordNameRegEx.test("Sébastien Doe"); // true
isTwoWordNameRegEx.test("John Doe"); // true
isTwoWordNameRegEx.test("John Doe-Williams") // true
isTwoWordNameRegEx.test("Scarlett O'Hara") // true
isTwoWordNameRegEx.test("John Doe-Williams-Jane") // true
isTwoWordNameRegEx.test("John Doe-") // false
isTwoWordNameRegEx.test("John Doe'") // false
isTwoWordNameRegEx.test("John' Doe") // false
isTwoWordNameRegEx.test("John Doe Williams") // false
Now that I've mentioned this... don't do it! It's still making an assumption about how a name might be. Please read Falsehoods Programmers Believe About Names.
If you really want to restrict it to two words, then please consider a very relaxed version:
const isTwoWordNameRegEx = /^\S+ \S+$/;

Look for substring in a string with at most one different character-javascript

I am new in programing and right now I am working on one program. Program need to find the substring in a string and return the index where the chain starts to be the same. I know that for that I can use "indexOf". Is not so easy. I want to find out substrings with at moste one different char.
I was thinking about regular expresion... but not really know how to use it because I need to use regular expresion for every element of the string. Here some code wich propably will clarify what I want to do:
var A= "abbab";
var B= "ba";
var tb=[];
console.log(A.indexOf(B));
for (var i=0;i<B.length; i++){
var D=B.replace(B[i],"[a-z]");
tb.push(A.indexOf(D));
}
console.log(tb);
I know that the substring B and string A are the lowercase letters. Will be nice to get any advice how to make it using regular expresions. Thx
Simple Input:
A B
1) abbab ba
2) hello world
3) banana nan
Expected Output:
1) 1 2
2) No Match!
3) 0 2
While probably theoretically possible, I think it would very complicated to try this kind of search while attempting to incorporate all possible search query options in one long complex regular expression. I think a better approach is to use JavaScript to dynamically create various simpler options and then search with each separately.
The following code sequentially replaces each character in the initial query string with a regular expression wild card (i.e. a period, '.') and then searches the target string with that. For example, if the initial query string is 'nan', it will search with '.an', 'n.n' and 'na.'. It will only add the position of the hit to the list of hits if that position has not already been hit on a previous search. i.e. It ensures that the list of hits contains only unique values, even if multiple query variations found a hit at the same location. (This could be implemented even better with ES6 sets, but I couldn't get the Stack Overflow code snippet tool to cooperate with me while trying to use a set, even with the Babel option checked.) Finally, it sorts the hits in ascending order.
Update: The search algorithm has been updated/corrected. Originally, some hits were missed because the exec search for any query variation would only iterate as per the JavaScript default, i.e. after finding a match, it would start the next search at the next character after the end of the previous match, e.g. it would find 'aa' in 'aaaa' at positions 0 and 2. Now it starts the next search at the next character after the start of the previous match, e.g. it now finds 'aa' in 'aaaa' at positions 0, 1 and 2.
const findAllowingOneMismatch = (target, query) => {
const numLetters = query.length;
const queryVariations = [];
for (let variationNum = 0; variationNum < numLetters; variationNum += 1) {
queryVariations.push(query.slice(0, variationNum) + "." + query.slice(variationNum + 1));
};
let hits = [];
queryVariations.forEach(queryVariation => {
const re = new RegExp(queryVariation, "g");
let myArray;
while ((searchResult = re.exec(target)) !== null) {
re.lastIndex = searchResult.index + 1;
const hit = searchResult.index;
// console.log('found a hit with ' + queryVariation + ' at position ' + hit);
if (hits.indexOf(hit) === -1) {
hits.push(searchResult.index);
}
}
});
hits = hits.sort((a,b)=>(a-b));
console.log('Found "' + query + '" in "' + target + '" at positions:', JSON.stringify(hits));
};
[
['abbab', 'ba'],
['hello', 'world'],
['banana', 'nan'],
['abcde abcxe abxxe xbcde', 'abcd'],
['--xx-xxx--x----x-x-xxx--x--x-x-xx-', '----']
].forEach(pair => {findAllowingOneMismatch(pair[0], pair[1])});

I would like to get the value of a given token with in a string

I am currently working on a project that will allow me to bring in a string that would have a designated token that I will grab, get the designated value and remove the token and push to an array. I have the following condition which I am using split in JavaScript but it is not splitting on the designated ending token.
This is the beginning string
"~~/Document Heading 1~~<div>This is a test <b>JUDO</b> TKD</div>~~end~~<div class="/Document Heading 1">This is a test <b>JUDO</b> TKD</div>"
Current Code Block
var segmentedStyles = [];
var contentToInsert = selectedContent.toString();
var indexValue = selectedContent.toString().search("~~");
if (indexValue <= 0) {
var insertionStyle = contentToInsert.split("~~");
segmentedStyles.push(insertionStyle);
}
The designated token is enclosed by a "~~ .... ~~". In this code Block it is going through the condition but the string it is not splitting correctly. I am currently getting the Following string pushed to my array.
This is my current result
[,/Document Heading 1<div>This is a test <b>JUDO</b> TKD</div>end,
<div class="/Document Heading 1">This is a test <b>JUDO</b> TKD</div>]
My Goal
I would like to split a string that is coming in if a token is present. For example I would like to split a string starting from ~~.....~~ through ~~end~~. The array should hold two values like the following
segmentedStyles = [<div>This is a test <b>JUDO</b> TKD</div>],[<div class="/Document Heading 1">This is a test <b>JUDO</b> TKD</div>]
You could use a regular expression for matching the parts.
var string = '~~/Document Heading 1~~<div>This is a test <b>JUDO</b> TKD</div>~~end~~<div class="/Document Heading 1">This is a test <b>JUDO</b> TKD</div>',
array = string.split('~~').filter(function (_, i) {
return i && !(i % 2); // just get element 2 and 4 or all other even indices
});
console.log(array);
Assuming the string always starts with ~~/ you could use the following regex to get the array you want
~~([^\/].*)~~end~~(.*)
https://regex101.com/r/hJ0vM4/1
I honestly didn't quite understand what you're trying to accomplish haha, but I sort of understood what you're trying to do :)
First, just trying to make it clear some stuff. If you split() your string using /~~/ as the Regular Expression for splitting you'll get all the bits surrounded by "~~" in an array, like you did.
Second, if you change the tokens to ~~START~~ and ~~END~~ (tokens that never change) you can accomplish what you want by simply doing string.split(/~~(START|END)~~/) - Much shorter and quicker ;)
Third is the string always in the format ~~<something>~~THE STUFF YOU WANT~~end~~MORE STUFF YOU WANT? If it is, I'd suggest doing this:
function splitTheTokens(str) {
var result = [];
var parts = str.split(/~~end~~/);
for (var i = 0; i < parts.length; i++) {
if (!parts[i]) { continue; } // Skips blanks
if (parts[i].indexOf("~~") == 0) {
// In case you want to do something with the name thing:
var thisPartName = parts[i].substring(2, parts[i].indexOf("~~", 2));
// What (I think) you actually want
var thisPartValue = parts[i].substring(thisPartName.length + 4);
result.push(thisPartValue);
}
else {
result.push(parts[i]);
}
}
return result;
}
Hope this helps :D

Categories

Resources