So Im pretty new to javascript and coding in general. Im making a Wordle algorithm just for fun to build my skills in coding. and while making this algorithm i realized that i was going to have to kind of recreate Wordle. so i looked online for wordle recreations in java script, and all the ones I found I saw one flaw that I didn't want to have.
The flaw that I saw was in the IF statement for checking a letter in the wordle answer. Specifically when the letter is in the word but not in the right spot. The recreations that i saw had an IF statement that looked something like this.
IF (answerWord.includes(guessLetter[i])) {
guessLetter[i] = color.(yellow)
}
(this isnt exactly how they wrote it, but its the main idea)
My main focus is on the .includes. This would not work because say our guess word is "agree" and the answer word "ready". the 2 E's in "agree" would be yellow, and we dont want that because their is is only 1 E in "ready. So we only want the first E in "agree" to be yellow and not the second E.
So i decided to figure it out on my own, and i was able to come up with this and it works. At least Im pretty sure it works, base on the the many words i tested it on.
So my question is, since Im making an algorithm im going to be making alot of calculations, is this the most efficient i could write this or can i make it better?
let guessWord = "agree"; // the first word thats inputed
let answerWord = "ready"; // the answer to the wordle
/*
'letterCheck' is an array that tells what condition each letter in 'startLetter' is, based on the answer word
2 = the letter is in the word and in the correct place (GREEN)
1 = the letter is in the word but not in the correct place (YELLOW)
0 = the letter in not in the word (GREY)
*/
var letterCheck = ["0", "0", "0", "0", "0"];
var guessLetter = ["A", "A", "A", "A", "A"]; // the separated letters of 'startword' in a array
var answerLetter = ["A", "A", "A", "A", "A"]; // the separated letters of 'answord' in a array
//adds the start word and the answer world to the arrays
for (var i = 0; i < 5; i++) {
guessLetter[i] = guessWord.substring(i, i + 1);
answerLetter[i] = answerWord.substring(i, i + 1);
}
console.log(guessLetter);
console.log(letterCheck);
console.log(answerLetter);
//this loops goes though every letter one by one
for (var i = 0; i < 5; i++) {
//checks if the letter is in the right spot
if (guessLetter[i] == answerLetter[i]) {
letterCheck[i] = "2";
console.log(guessLetter[i]);
console.log(letterCheck);
} else if (answerWord.includes(guessLetter[i])) {
//checks if there is more than one letter in start word or its the first letter
if (guessWord.split(guessLetter[i]).length - 1 == 1 || i == 0) {
letterCheck[i] = "1";
console.log(guessLetter[i]);
console.log(letterCheck);
//checks if the the amount of same letters in start words is equel to the amount of same letters in answer word
} else if (guessWord.split(guessLetter[i]).length - 1 == answerWord.split(guessLetter[i]).length - 1) {
letterCheck[i] = "1";
console.log(guessLetter[i]);
console.log(letterCheck);
//opposite of above
} else if (guessWord.split(guessLetter[i]).length - 1 != answerWord.split(guessLetter[i]).length - 1) {
letterCheck[i] = "1";
console.log(guessLetter[i]);
console.log(letterCheck);
// checks if any of the letters infront of it are the same as it
for (var j = 0; j < i; j++) {
if (guessLetter[i] == guessLetter[j]) {
letterCheck[i] = "0";
console.log(guessLetter[i]);
console.log(letterCheck);
}
}
}
} else {
letterCheck[i] = "0";
console.log(guessLetter[i]);
console.log(letterCheck);
}
}
console.log(guessLetter);
console.log(letterCheck);
console.log(answerLetter);
( I will remove the console.log's later they just helped my to debug.)
Sorry if my code might be confusing, im not the best at keeping my code clean. if you have any confusion or questions, I'll try my best in clear up any confusion.
To get all of the states (in precedence order: correct, wrong-position, incorrect), you should check in that precedence order, and remove letters from contention once the state in that letter's position has been set.
function check(guess, targetWord) {
const checkStates = new Array(5).fill("incorrect");
const letters = guess.split('');
const targetLetters = targetWord.split('');
for (let i=0; i<5; i++) {
if (targetLetters[i] === letters[i]) {
checkStates[i] = "correct";
targetLetters[i] = '';
letters[i] = '';
}
}
for (let i=0; i<5; i++) {
if (!letters[i]) continue;
let fi = targetLetters.findIndex(t => t===letters[i]);
if (fi > -1) {
checkStates[i] = "contains";
targetLetters[fi] = '';
}
}
return checkStates;
}
let solution = "ready"
console.log(`solution is ${solution}`);
let guess = "agree";
console.log(`checking ${guess}... ${check(guess, solution)}`);
guess = "roars";
console.log(`checking ${guess}... ${check(guess, solution)}`);
guess = "boars";
console.log(`checking ${guess}... ${check(guess, solution)}`);
solution = "beast"
console.log(`\nsolution is ${solution}`);
guess = "treat";
console.log(`checking ${guess}... ${check(guess, solution)}`);
Well, here's something to consider in simplifying. You can use a simple MAP loop to determine which letters are incorrect, then later highlight those in the word.
let guessWord = "agree"; // the first word thats inputed
let answerWord = "ready"; // the answer to the wordle
let tmpGuess = guessWord.split('');
let incorrects = answerWord.split('').map(e => {
let i = tmpGuess.indexOf(e);
if (i > -1) { // found it!
tmpGuess.splice(i, 1); // remove
return '';
}
return e;
}).filter(f => f);
console.log(incorrects);
Related
good morning, sorry first of all for my english. I'm trying to do a double loop to iterate through two strings, the thing is, I want the ocrString to start one position later each time, so that it can iterate through the string in order to see if there are any matches. That is, I want to find the matches without necessarily being equal in length and without being able to order it.
let ocrString = "casaidespcasa";
let pattern = "idesp";
let conteo = 0;
checkIDESP(ocrString, pattern);
function checkIDESP(ocrString, pattern) {
let ocrStringSeparado = ocrString.split("");
let patternSeparado = pattern.split("");
for (i = 0; i < ocrStringSeparado.length; i++) {
for (x = 0; x < patternSeparado.length; x++) {
console.log(ocrStringSeparado[i], pattern[x]);
if (ocrStringSeparado[i] == pattern[x]) {
conteo++;
}
}
}
if (conteo <= 3) {
console.log(conteo, "No sé si es un dni");
} else {
console.log(conteo, "es un dni");
}
}
Some way to go through the position of an array so that it first starts with 'Casaidespcasa' and then 'Asaidespcasa' etc.
That won't answer totally to your question (I don't really understand by the way).
Now for the last part:
"Some way to go through the position of an array so that it first starts with 'Casaidespcasa' and then 'Asaidespcasa' etc."
Perhaps that can help for you to solve your problem.
let ocrString = "casaidespcasa";
let ocrStringSeparado = ocrString.split("");
decreaseArr(ocrStringSeparado);
decreaseStr(ocrString);
function decreaseArr(arr) {
console.log(arr);
arr.shift();
// do something...
if (arr.length > 0) {
decreaseArr(arr);
}
}
function decreaseStr(str) {
console.log(str);
str = str.substring(1);
// do something...
if (str.length > 0) {
decreaseStr(str);
}
}
First function is with array, second with string.
Well, maybe the following would work for you?
const string = "casaidespcasa";
function comp(str, pat){
let pl=pat.length, sl=str.length, res=[];
for (let i=0; i<=sl-pl; i++){
let s=str.slice(i,i+pl); // get a portion of the original string
let n=s.split("").reduce((a,c,j)=>a+(c==pat[j] ? 1 : 0), 0); // count matches
if (n>2) res.push([i,s]); // at least 3 character must match!
}
return res;
}
// do the string comparison with an array of patterns:
["idesp","1detp","deaspdc","cosa","asaic"].forEach(p=>console.log(p+":",comp(string,p)))
The function returns an array of possible "fuzzy" matches: Each entry is an array, containing the position and the matching substring.
we are in our 3 week of our Bootcamp.
Project is to code the Hangman game, it is running in the terminal.
We got preety far in just 2 days I think.
We are now stucked on the case insensesetivity, meaning if the word we are looking for is Java that if you type j or J, you still would get the same result J_ _ _.
I looked all over the Internet and found the locale.compare method which isn´t working for me. If i type a wrong letter everything is fine but if I type a right one I get a error and also it doesn´t ignore the case of the letter.
We are not using functions yet because we haven´t learned how they work.
const constants = require('./constants');
// In node.js: install a prompt library by running: `npm install prompt-sync` in the current folder
const prompt = require("prompt-sync")();
// Here you see an example how to get your
// constants from constants.js
/*for(let figure of constants.HANGMAN_PICS)
{
console.log(figure);
}
*/
let Answer = [];
var Words = constants.WORDS_TO_GUESS[Math.floor(Math.random() * constants.WORDS_TO_GUESS.length)];
/*console.log(Words.length); //Wortlänge von random Word
*/
for (let i = 0; i < Words.length; i++) {
Answer[i] = "_";
}
console.log(Answer.join(" "));
for (; Answer !== Words;) {
input = prompt(`Finde das Wort.`);
if (Words.includes(input)) {
for (let i = 0; i < Words.length; i++) {
if (Words[i] === input) {
Answer[i] = input;
}
}
console.log(Answer.join(" "));
console.log(Answer.localeCompare(Words, {
sensitivity: `base`
}));
} else if (!Words.includes(input)) {
console.log("Falsche Eingabe - keep trying!");
console.log(Answer.join(" "))
}
}
// how to use the prompt - e.g.:
// const name = prompt('What is your name?');
my way of doing this (which may not be the best) is setting both strings to lower case and comparing them with includes() function.
The for loop afterwards will just collect right position/s that you want to show up after right guess.
const word = 'JavaScript'
const char = 's' //input
const indexes = [] //output
//https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
//includes() function
if(word.toLowerCase().includes(char.toLowerCase())){
//if you need to get the index/es of the latter/s
for (let i = 0; i < word.toLowerCase().length; i++) {
if (word.toLowerCase()[i] === char.toLowerCase()) {
indexes.push(i)
}
}
}
console.log(indexes)
Make sure that Words (which should be word, singular and not capitalized) is in lower case (or upper case) by doing .toLocaleLowerCase() (or .toLocaleUpperCase()) at the end of where you assign it:
var Words = constants.WORDS_TO_GUESS[Math.floor(Math.random() * constants.WORDS_TO_GUESS.length)].toLocaleLowerCase();
// >>> −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^
Then make sure input is lower case (or upper case):
input = prompt(`Finde das Wort.`).toLocaleLowerCase();
At that point, you'll be comparing j and j (or J and J), so you'll find a match.
If you don't want to consistently use one capitalization in your code, it's possible to do it differently, but more complicated.
Basically, you do the .toLocaleLowerCase() (or .toLocaleUpperCase()) everywhere you do a comparison:
for (; Answer !== Words;) {
input = prompt(`Finde das Wort.`).toLocaleLowerCase();
// Get the input in lower case −−−−−−^^^^^^^^^^^^^^^^^^^^
if (Words.toLocaleLowerCase().includes(input)) {
// ^^^^^^^^^^^^^^^^^^^^−−− make Words lower case for the includes call
for (let i = 0; i < Words.length; i++) {
if (Words[i].toLocaleLowerCase() === input) {
// ^^^^^^^^^^^^^^^^^^^^−− make Words[i] lower case for the ===
Answer[i] = Words[i];
// ^^^^^^^^−−− use the one from Words, not the user's input
}
}
// ...
}
}
I am trying to create my own implementation of wordle from scratch in angular and am stuck trying to resolve one of the edge cases. Assume the word to guess for all the following cases is "CLOSE".
CASE 1 - Both duplicate letters are in the wrong position.
If my guess is "CHEER", both of the 'E's are in the wrong position, but "CLOSE" only has one 'E' and hence only one should get a yellow highlight. My code IS dealing with this as desired
CASE 2 - The letter with the correct position comes first followed by the same letter in the incorrect position
If my guess is "COCKS", the first 'C' is in the correct position and should get a yellow highlight whereas 'C' should get a gray highlight since "CLOSE" only has one 'C'. My code IS dealing with this as desired as well
CASE 3 - The letter with the incorrect position comes first followed by the same letter in the correct position
If my guess is "LEAVE", the first 'E' is in the incorrect position, but because the second 'E' is in the correct position, and "CLOSE" only has one 'E', it should get a gray highlight and the second 'E' should get a green highlight. This is the edge case that I want to fix
The outcome of my implementation:
The desired outcome of this case:
The logic that I am using at the moment:
let remainingLettersInWord: string = this.chosenWord;
for (let i = 0; i < 5; i++) {
if (this.currentGuess[i] === this.chosenWord[i]) {
remainingLettersInWord = remainingLettersInWord.replace(this.currentGuess[i], '');
this.setAttributeStyle(i, COLOR.Green);
} else if (remainingLettersInWord.includes(this.currentGuess[i])) {
remainingLettersInWord = remainingLettersInWord.replace(this.currentGuess[i], '');
this.setAttributeStyle(i, COLOR.Yellow);
} else {
this.setAttributeStyle(i, COLOR.Gray);
}
}
Here, chosenWord = "CLOSE", currentGuess = "CHEER" for case 1, "COCKS" for case 2 and "LEAVE" for case 3
I ended up fixing the problem with the help of one of my senior devs and the comment from #Chris G
Here is the solution I used:
let remainingLettersInWord: string = this.chosenWord;
for (let i = 0; i < 5; i++) {
if (this.currentGuess[i] === this.chosenWord[i]) {
remainingLettersInWord = remainingLettersInWord.replace(this.currentGuess[i], '');
this.setAttributeStyle(i, COLOR.Green);
} else {
this.setAttributeStyle(i, COLOR.Gray);
}
}
for (let i = 0; i < 5; i++) {
if (remainingLettersInWord.includes(this.currentGuess[i]) && this.currentGuess[i] !== this.chosenWord[i]) {
remainingLettersInWord = remainingLettersInWord.replace(this.currentGuess[i], '');
this.setAttributeStyle(i, COLOR.Yellow);
}
}
I split the logic for the yellow check into a separate loop and I am removing the green words from the list before checking the yellow list. That adheres to all my edge cases now
A different approach by counting the letters of the wanted word.
const
getColors = ([...word], [...input]) => {
const
count = {},
result = Array(5).fill('-'); // only to show the result.
for (let i = 0; i < input.length; i++) {
if (word[i] === input[i]) result[i] = 'green';
else count[word[i]] = (count[word[i]] || 0) + 1;
}
for (let i = 0; i < input.length; i++) {
if (word[i] === input[i] || !count[input[i]]) continue;
count[input[i]]--;
result[i] = 'yellow';
}
return result;
};
console.log(...getColors("CLOSE", "CHEER"));
console.log(...getColors("CLOSE", "COCKS"));
console.log(...getColors("CLOSE", "LEAVE"));
Say you have the following string:
FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNT
LDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFY
FFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQ
XBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR
AMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR
I'm trying to find the smallest substring containing the letters ABCDA.
I tried a regex approach.
console.log(str.match(/[A].*?[B].*?[C].*?[D].*?[A]/gm).sort((a, b) => a.length - b.length)[0]);
This works, but it only find strings where ABCDA appear (in that order). Meaning it won't find substring where the letters appear in a order like this: BCDAA
I'm trying to change my regex to account for this. How would I do that without using | and type out all the different cases?
You can't.
Let's consider a special case: Assume the letters you are looking for are A, A, and B. At some point in your regexp there will certainly be a B. However, the parts to the left and to the right of the B are independent of each other, so you cannot refer from one to the other. How many As are matched in the subexpression to the right of the B depends on the number of As being already matched in the left part. This is not possible with regular expressions, so you will have to unfold all the different orders, which can be many!
Another popular example that illustrates the problem is to match opening brackets with closing brackets. It's not possible to write a regular expression asserting that in a given string a sequence of opening brackets is followed by a sequence of closing brackets of the same length. The reason for this is that to count the brackets you would need a stack machine in contrast to a finite state machine but regular expressions are limited to patterns that can be matched using FSMs.
This algorithm doesn't use a regex, but found both solutions as well.
var haystack = 'FJKAUNOJDCUTCRHBYDLXKEODVBWTYPTSHASQQFCPRMLDXIJMYPVOHBDUGSMBLMVUMMZYHULSUIZIMZTICQORLNTOVKVAMQTKHVRIFMNTSLYGHEHFAHWWATLYAPEXTHEPKJUGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGRAMELUTEPYILBIUOCKKUUBJROQFTXMZRLXBAMHSDTEKRRIKZUFNLGTQAEUINMBPYTWXULQNIIRXHHGQDPENXAJNWXULFBNKBRINUMTRBFWBYVNKNKDFR';
var needle = 'ABCDA'; // the order of letters doesn't matter
var letters = {};
needle.split('').forEach(function(ch) {
letters[ch] = letters[ch] || 0;
letters[ch]++;
});
var shortestSubstringLength = haystack.length;
var shortestSubstrings = []; // storage for found substrings
var startingPos = 0;
var length;
var currentPos;
var notFound;
var letterKeys = Object.keys(letters); // unique leters
do {
lettersLeft = JSON.parse(JSON.stringify(letters)); // copy letters count object
notFound = false;
posStart = haystack.length;
posEnd = 0;
letterKeys.forEach(function(ch) {
currentPos = startingPos;
while (!notFound && lettersLeft[ch] > 0) {
currentPos = haystack.indexOf(ch, currentPos);
if (currentPos >= 0) {
lettersLeft[ch]--;
posStart = Math.min(currentPos, posStart);
posEnd = Math.max(currentPos, posEnd);
currentPos++;
} else {
notFound = true;
}
}
});
if (!notFound) {
length = posEnd - posStart + 1;
startingPos = posStart + 1; // starting position for next iteration
}
if (!notFound && length === shortestSubstringLength) {
shortestSubstrings.push(haystack.substr(posStart, length));
}
if (!notFound && length < shortestSubstringLength) {
shortestSubstrings = [haystack.substr(posStart, length)];
shortestSubstringLength = length;
}
} while (!notFound);
console.log(shortestSubstrings);
Maybe not as clear as using regex could be (well, for me regex are never really clear :D ) you can use brute force (not so brute)
Create an index of "valid" points of your string (those with the letters you want) and iterate with a double loop over it getting substrings containing at least 5 of those points, checking that they are valid solutions. Maybe not the most efficient way, but easy to implement, to understand, and probably to optimize.
var haystack="UGDVWUDDPRQLUZMSZOJPSIKAIHLTONYXAULECXXKWFQOIKELWOHRVRUCXIAASKHMWTMAJEWGEESLWRTQKVHRRCDYXNTLDSUPXMQTQDFAQAPYBGXPOLOCLFQNGNKPKOBHZWHRXAWAWJKMTJSLDLNHMUGVVOPSAMRUJEYUOBPFNEHPZZCLPNZKWMTCXERPZRFKSXVEZTYCXFRHRGEITWHRRYPWSVAYBUHCERJXDCYAVICPTNBGIODLYLMEYLISEYNXNMCDPJJRCTLYNFMJZQNCLAGHUDVLYIGASGXSZYPZKLAWQUDVNTWGFFYFFSMQWUNUPZRJMTHACFELGHDZEJWFDWVPYOZEVEJKQWHQAHOCIYWGVLPSHFESCGEUCJGYLGDWPIWIDWZZXRUFXERABQJOXZALQOCSAYBRHXQQGUDADYSORTYZQPWGMBLNAQOFODSNXSZFURUNPMZGHTAJUJROIGMRKIZHSFUSKIZJJTLGOEEPBMIXISDHOAIFNFEKKSLEXSJLSGLCYYFEQBKIZZTQQXBQZAPXAAIFQEIXELQEZGFEPCKFPGXULLAHXTSRXDEMKFKABUTAABSLNQBNMXNEPODPGAORYJXCHCGKECLJVRBPRLHORREEIZOBSHDSCETTTNFTSMQPQIJBLKNZDMXOTRBNMTKHHCZQQMSLOAXJQKRHDGZVGITHYGVDXRTVBJEAHYBYRYKJAVXPOKHFFMEPHAGFOOPFNKQAUGYLVPWUJUPCUGGIXGR";
var needle="ABCD";
var size=haystack.length;
var candidate_substring="";
var minimal_length=size;
var solutions=new Array();
var points=Array();
for(var i=0;i<size;i++){
if(needle.indexOf(haystack[i])>-1) points.push(i);
}
var limit_i= points.length-4;
var limit_k= points.length;
for (var i=0;i<limit_i;i++){
for(var k=i;k<limit_k;k++){
if(points[k]-points[i]+1<=minimal_length){
candidate_substring=haystack.substr(points[i],points[k]-points[i]+1);
if(is_valid(candidate_substring)){
solutions.push(candidate_substring);
if(candidate_substring.length < minimal_length) minimal_length=candidate_substring.length;
}
}
}
}
document.write('<p>Solution length:'+minimal_length+'<p>');
for(var i=0;i<solutions.length;i++){
if(solutions[i].length<=minimal_length) document.write('<p>Solution:'+solutions[i]+'<p>');
}
function is_valid(candidate_substring){
//verify we've got all characters
for(var j=0;j<candidate_substring.length;j++){
if(candidate_substring.indexOf(needle.charAt(j))<0) return false;
}
//...and verify we have two "A"
if(candidate_substring.indexOf("A")==candidate_substring.lastIndexOf("A")) return false;
return true;
}
Just had this problem in an interview as a coding assignment and came up with another solution, (it's not as optimal as the one above but maybe it's easier to understand).
function MinWindowSubstring(strArr) {
const N = strArr[0];
const K = strArr[1];
const letters = {};
K.split('').forEach( (character) => {
letters[character] = letters[character] ? letters[character] + 1 : 1;
});
let possibleSequencesList = [];
const letterKeys = Object.keys(letters);
for(let i=0; i< N.length; i++) {
const char = N[i];
if (new String(letterKeys).indexOf(char) !== -1) {
// found a character in the string
// update all previus sequences
possibleSequencesList.forEach((seq) => {
if(!seq.sequenceComplete) {
seq[char] = seq[char]-1;
seq.lastIndex = i;
// check if sequence is complete
var sequenceComplete = true;
letterKeys.forEach( (letter) => {
if(seq[letter] > 0) {
sequenceComplete = false;
}
});
seq.sequenceComplete = sequenceComplete
}
})
// create a new sequence starting from it
const newSeq = {
startPoint: i,
lastIndex: i,
sequenceComplete: false,
...letters
}
newSeq[char] = newSeq[char]-1;
possibleSequencesList.push(newSeq);
}
}
// cleanup sequences
let sequencesList = possibleSequencesList.filter(sequence => sequence.sequenceComplete);
let output = [];
let minLength = N.length;
// find the smalles one
sequencesList.forEach( seq => {
if( (seq.lastIndex - seq.startPoint) < minLength) {
minLength = seq.lastIndex - seq.startPoint;
output = N.substring(seq.startPoint, seq.lastIndex + 1);
}
})
return output;
}
I'm attempting to teach myself javascript. I chose something I assumed was simple, but ran into problems relatively quickly.
I'm attempting to search a string for another string given by the user.
My code so far is:
var source = "XREs2qqAQfjr6NZs6H5wkZdOES5mikexRkOPsj6grQiYNZfFoqXI4Nnc1iONKVrA";
var searchString = []; //the users input
searchString = prompt("Enter search string");
var hits = [];
var one = 0;
var two = 0;
var k = 0;
var sourceSearch = function(text) {
for(i = 0; i < source.length; i++) { //for each character in the source
if(source[i] === searchString[0]) { //if a character in source matches the first element in the users input
one = source.indexOf(i); //confused from here on
for(p = searchString.length; p > 0; p--) {
}
}
}
};
sourceSearch(searchString);
My idea was:
check to see if the first loop finds a character that matches the first character in the user input
if it matches, check to see if the next X characters after the first match the next X characters in the source string
if they all match, push them to the hits array
My problem: I have no idea how to iterate along the arrays without nesting quite a few if statements, and even then, that wouldn't be sufficient, considering I want the program to work with any input.
Any ideas would be helpful. Thanks very much in advance.
Note: There are a few un-used variables from ideas I was testing, but I couldn't make them work.
You can try:
if (source.indexOf(searchString) !== -1) {
// Match!
}
else
{
//No Match!
}
As the other answers so far point out, JavaScript strings have an indexOf function that does what you want. If you want to see how it's done "by hand", you can modify your function like this:
var sourceSearch = function(text) {
var i, j, ok; // always declare your local variables. globals are evil!
// for each start position
for(i = 0; i < source.length; i++) {
ok = true;
// check for a match
for (j = searchString.length - 1; ok && j >= 0; --j) {
ok = source[i + j] === searchString[j];
}
if (ok) {
// searchString found starting at index i in source
}
}
};
This function will find all positions in source at which searchString was found. (Of course, you could break out of the loop on the first success.) The logic is to use the outer loop to advance to each candidate start position in source and use the inner loop to test whether that position actually is the position of a match to searchString.
This is not the best algorithm for searching strings. The built-in algorithm is much faster (both because it is a better algorithm and because it is native code).
to follow your approach, you can just play with 2 indexes:
var sourceSearch = function(text) {
j = 0;
for(i = 0; i < source.length; i++) {
if(source[i] === text[j]) {
j++;
} else {
j = 0;
}
if (j == text.length) {
console.log(i - j); //this prints the starting index of the matching substring
}
}
};
These answers are all pretty good, but I'd probably opt for something like this:
var source = "XREs2qqAQfjr6NZs6H5wkZdOES5mikexRkOPsj6grQiYNZfFoqXI4Nnc1iONKVrA";
var searchString = []; //the users input
searchString = prompt("Enter search string");
var hits = source.split(searchString);
var hitsCount = hits.length - 1;
This way you have all of the data you need to figure out where each hit occurred in he source, if that's important to you.