I'd like to figure out if a substring is within a string without using the Javascript built in methods of includes, indexOf, (any similar to those), or regular expressions. Basically just looking to learn an algorithm approach.
This is what I have so far
function isSubstring(string, substring){
substringIndex = 0;
// loop through string
for(let i = 0; i< string.length; i++){
//check the current substring index if it matches
if(string[i] === substring[substringIndex]){
substringIndex++
//continue searching... Would i then have to call another function recursively?
}
}
}
I'm having trouble understanding how to build the algorithm. Once it finds that first character that matches, I would just go to the next one and if it matches continue to the next index of the string? Would I then need a recursive function that is separate from the looping I am currently doing? I'm trying to get better at algorithmic thinking. Thanks.
Many approaches are possible. Here is one: Create a simpler function which will check if a first string is at concrete specified position, let's call it start, in a second string. It is quite simple: You compare first[i] with second[start+i] for all i in range 0 to length of first - 1.
Then the second step will be to repeat this function for all start positions from 0 to length of second string, while checking the boundaries (you cannot read after end of a string).
You can also do some optimizations later, when the first version will work. :-)
Here is an optimized example of the algorythm isSubstring. It iterates only through the minimum number of characters required.
For example, if the string is 20 characters long and the substring is only 5 characters long, when we get to the 16th position of the string we can assume that the substring doesn't exist within the string (16 + 5 = 21 > 20)
function isSubstring(str, sub){
if(sub.length > str.length) return false;
for(let i = 0; i < str.length - sub.length + 1; i++){
if(str[i] !== sub[0]) continue;
let exists = true;
for(let j = 1; j < sub.length && exists; j++){
if(str[i+j] === sub[j]) continue;
exists = false;
}
if(exists) return true;
}
return false;
}
//expected true
console.log(isSubstring("hello world", "hello"));
console.log(isSubstring("hello world", "world"));
console.log(isSubstring("hello world", "d"));
console.log(isSubstring("hello world", "o w"));
console.log(isSubstring("hello world", "rl"));
console.log(isSubstring("hello world", ""));
//expected false
console.log(isSubstring("hello world", "hello world 1"));
console.log(isSubstring("hello world", "helloo"));
On each iteration over the length of the haystack, use slice to extract the characters from that index to (the index plus the length of the needle). If the sliced string matches the needle, return true:
function isSubstring(string, substring) {
for (let i = 0; i < string.length; i++) {
const sliced = string.slice(i, i + substring.length);
if (sliced === substring) {
return true;
}
}
return false;
}
console.log(isSubstring('foobar', 'oob'));
console.log(isSubstring('foobar', 'baz'));
Since you expressed interest in a recursive method, here's something to consider. Clicking on the yellow markdown parts reveal the spoilers.
function f(str, sub, i=0, j=0){
if (j && j == sub.length)
return true;
if (i == str.length)
return false;
if (str[i] == sub[j])
return f(str, sub,
i+1, j+1);
return f(str, sub,
i+1, 0);
}
function isSubstring(str, sub) {
return str.split(sub).length > 1
}
No includes, no .indexOf, no RegExp. Just strings.
using only one loop pseudo code :
const containSubstr = (str, substr) => {
let count = 0;
let i = 0;
let startIndex = 0;
while (i < str.length) {
if (substr[count] === str[i]) {
if (count === substr.length - 1) {
return true;
}
count++;
} else {
count = 0;
i = startIndex;
startIndex++;
}
i++;
}
return false;
};
console.log(containSubstr("ababad", "abad"));
Related
For some reason, my solution is returning null instead of the value that is stored in 's'. Instructions and code are below:
You are given a string s. Consider the following algorithm applied to this string:
Take all the prefixes of the string, and choose the longest palindrome between them.
If this chosen prefix contains at least two characters, cut this prefix from s and go back to the first step with the updated string. Otherwise, end the algorithm with the current string s as a result.
Your task is to implement the above algorithm and return its result when applied to string s.
test case
const s = "aaacodedoc"
expected output: ""
another test case
const s = "abbab"
expected output: "b"
function solution(s) {
const prefixes =[]
if(s.length === 0){
return ""
}
if(s.length === 1){
return s
}
for(let i = 0; i < 1; i++){
for(let j = i; j < s.length; j++){
const substr = s.substring(i, j + 1)
prefixes.push(substr)
}
}
const palindromes = prefixes.filter(prefix => {
let reverse = prefix.split('').reverse().join('');
return prefix === reverse
})
let longest = palindromes.sort(
function (a, b) {
return b.length - a.length;
})[0];
if(longest.length >= 2){
s = s.substring(longest.length)
solution(s)
} else{
return s;
}
}
Scott's comment was correct:
You're right. You are very close. You don't return anything in the recursive call. If you replace solution (s) with return solution (s), it should work.
Encountered this same issue and it was just a missing return statement on the recursive call. Explains why some of your test cases passed that checked for len < 2.
I have this exercise: Check if a string (first argument, str) ends with the given target string (second argument, target) without using the method endsWith() or any other method. What's wrong with my code?
function confirmEnding(str, target) {
for (var i = 1; i <= target.length; i++) {
val = false
if (str[str.length - i] === target[target.length - i]) {
val = true;
}
return val;
}
}
confirmEnding("Bastian", "n");
//original code from post above:
console.log(confirmEnding("Bastian", "n")); //added to provide working example
In your original code there are a few problems:
Will address them inline:
function confirmEnding(str, target) {
// using a for loop to iterate over the target string's length
for (var i = 1; i <= target.length; i++) {
//setting up a variable that says false
val = false
//trying to compare the individual characters
if (str[str.length - i] === target[target.length - i]) {
//so what happens here:
//when the two match this will set val to true
//but every time the loop is run is will reset to false.
val = true;
}
//the return value is in the loop, so the loop will run once
return val;
}
}
confirmEnding("Bastian", "n");
With the above script you have no way of knowing if all the characters match. If the last character matches it will return true, even if the other characters don't match.
string: Bastian target: irr
Will return true in the logic of your loop.
Take a look at the code below and the comments in it!
function confirmEnding(str, target) {
//get the length of the target string
const targetLength = target.length;
//set up an empty string
let endstr = "";
for (let i = 1; i <= targetLength; i++)
{
//start at 1 since str.length-1 is last character
//fill the empty string with the last characters of str
endstr = str[str.length-i] + endstr;
}
//compare and return
return target === endstr;
}
console.log(confirmEnding("Bastian", "ian")); //TRUE
console.log(confirmEnding("Bastian", "in")); //FALSE
The problem in your current code is that the variable for the result is initialised inside the loop.
So it actually only returns true/false depending on the last character it compares.
Which would be the first character of the target string.
What you can do is get out of that loop as soon a difference is found.
And additional, also check if the string isn't smaller than the target, because then it would be false anyway.
F.e.
function confirmEnding(str, target) {
if(str.length < target.length)
return false;
for (var i = 1; i <= target.length; i++) {
if (str[str.length - i] !== target[target.length - i]) {
return false;
}
}
return true;
}
console.log(confirmEnding("Bastian", "ian"));
console.log(confirmEnding("Bastian", "ion"));
console.log(confirmEnding("an", "ian"));
const confirmEnding = (str,target) => str.slice(-target.length) === target;
console.log(confirmEnding('peace','ace')); // true
console.log(confirmEnding('devotion','tio')); // false
console.log(confirmEnding('faith and love','nd love')); // true
we know that "slice" take 2 arguments: [ index | how many items ]
more so, a negative index would start extracting characters from the end of a string
we can use this to our advantage by utilizing the "length" property from "target" to set the "index" on the "slice" method
let's use "peace" as our example to breakdown the code:
('peace','e') => str.slice(-1) === 'e';
str.slice(-1) --> return the last character 'e' from "peace"
('peace','e') => 'e' === 'e'; --> "true"
I am doing JavaScript courses and got stuck at this question.
Define a function named countLetters that receives two parameters:
1) sentence - type: string
2) character - type: string
The function should return a count of every character present in sentence.
HINT: You can access a single character in a string just like accessing an element in an array - myString[3] would access the third character
Here is what I have done so far.
function countLetters(sentence, character) {
for(let i=0; i <= sentence.length; i++) {
let count = character[i];
return sentence.match(count).length;
}
}
Thank you!
function countLetters(sentence, character) {
var count = 0;
for(let i=0; i <= sentence.length; i++) {
if(character == sentence[i]){
count++;
}
}
return count;
}
console.log(countLetters("The quick brown fox jumps over the lazy dog","o"));
You can access a single character in a string just like accessing an element in an array
It means you can use access each character of string like "sentence[i]" and then compare with the desired character like character == sentence[i].
The algorithm is simple. Maintain a local variable to count the number of occurrences of a letter. As you loop through each character in the sentence, if it matches character, increment the counter. Lastly, return the value of the counter
function countLetters(sentence, character) {
let count = 0;
for (let i = 0; i < sentence.length; i++) {
if (sentence[i] === character) {
count++;
}
}
return count;
}
console.log(countLetters('apple', 'p')); // 2
console.log(countLetters('watermelon', 'e')); // 2
console.log(countLetters('mississippi', 's')); // 4
Here's a one-liner just for fun:
function countLetters(sentence, character) {
return sentence.split('').reduce((acc, char) => acc + Number(char === character), 0);
}
console.log(countLetters('apple', 'p')); // 2
console.log(countLetters('watermelon', 'e')); // 2
console.log(countLetters('mississippi', 's')); // 4
Traverse through the sentence and increase the counter if the character is found
function countLetters(sentence, character) {
var count = 0
for(let i=0; i < sentence.length; i++) {
if (sentence.charAt(i) === character)
counter++;
}
return counter
}
Considering that only one character needed to be checked for count in a string.
This code can help.
var mstring = My name is jhone doe;
var mchar = o;
function countLetters(string, char){
var count = 0;
if(string[i] == char){
count++;
}
return count;
}
console.log(countLetters(mstring, mchar));
It will output 2. As o appears 2 times in the given string.
I’m trying to figure out why my code is not giving the right output.
My input shouldn’t be contained within the array elements.
I found an easy way to solve it with regex, so I am not using regex for that one.
Please, break down my code, and tell me what is the problem with the code.
function checkInput(input, words) {
var arr = input.toLowerCase().split(" ");
var i, j;
var matches = 0;
for (i = 0; i < arr.length; i++) {
for (j = 0; j < words.length; j++) {
if (arr[i] != words[j]) {
matches++;
}
}
}
if (matches > 0) {
return true;
} else {
return false;
}
};
console.log(checkInput("Move an array element from one array", ["from"])); // should be false
console.log(checkInput("Move an array element from one array", ["elem"])); // should be true
if (arr[i] != words[j]) will be true at some point or another most of the time.
You want to check the opposite and return the opposite logic, so:
if(arr[i] == words[j]) {
matches++;
}
and
if (matches > 0) {
return false;
} else {
return true;
}
But a simpler way would be:
function checkInput(input, words){
let lowerCaseInput = input.toLowerCase().split(" ");
return words.find(word => lowerCaseInput.includes(word)) === undefined;
}
Array.prototype.find will return undefined iff no element is found that satisfies a specific rule provided by the callback function, word => lowerCaseInput.includes(word) in this case. So we check whether its return value is undefined which will tell us whether a word has been matched in input.
Note that your function unnecessarily checks for the entire words array even though it only matters whether one word matches.
Also, the words in words are case-sensitive! If you don’t want that, replace .includes(word) by .includes(word.toLowerCase()).
Because you are using matches > 0, you think it will return true only when no matches is found. But what happens is that when you have input ab aa cc and word aa
first iteration matches = 1
second iteration matches = 1
third iteration matches = 2
So matches will contain how many times word is different from items of input. So as result, it will always return true as long as input is more than two words long, for at least one word of input will be different from word. You can rather consider increasing the value of matches if word is found instead.
function checkInput(input, words) {
var arr = input.toLowerCase().split(" ");
var i, j;
var matches = 0;
for (i = 0; i < arr.length; i++) {
for (j = 0; j < words.length; j++) {
if (arr[i] === words[j]) {
matches++;
}
}
}
if (matches === 0) {
return true;
} else {
return false;
}
};
console.log(checkInput("Move an array element from one array", ["from"]));
console.log(checkInput("Move an array element from one array", ["elem"]));
I'm trying to count the number of specific characters in a string, but am getting "0" instead of the correct output. I know there are easier ways to get the solution, but I want to know how to do it this way and what I'm doing wrong.
function countCharacter(str, char) {
var count = 0;
for(var i = 0; i < str.length; i++){
if(str.charAt(i) === char){
count++;
}
return count;
}
}
countCharacter("My cat is driving my crazy", "a");
Youre returning in the for loop, so the forloop just iterates once:
function countCharacter(str, char) {
var count = 0;
for(var i = 0; i < str.length; i++){
if(str.charAt(i) === char){
count++;
}
}
return count;
}
countCharacter("My cat is driving my crazy", "a");
By the way, shorter:
countCharacter=(str,char)=>str.split("").reduce((count,letter)=>letter===char?count+1:count,0);
Return count should be in a place out of the loop
You can try split and filter. The length of the resulting Array is the number of found characters:
const aFound = "My cat is driving my crazy"
.split("") // split per character
.filter( chr => chr === "a" ); // filter on character 'a'
const numberOfz = findCharacterInString("I'm driving my cat crazy", "z");
document.querySelector("pre").textContent = `
There is/are ${aFound.length} a('s) in "My cat is driving my crazy"
There is/are ${numberOfz} z('s) in "I'm driving my cat crazy"`;
// As method (case insensitive)
function findCharacterInString(string2Search, character) {
return string2Search
.split("")
.filter( chr => chr.toLowerCase() === character.toLowerCase() )
.length;
}
<pre></pre>
You accidentally put your return statement inside your for loop, so it returns after the first time it runs through the loop. Fixed below:
function countCharacter(str, char) {
var count = 0;
for(var i = 0; i < str.length; i++){
if(str.charAt(i) === char){
count++;
}
}
return count;
}
countCharacter("My cat is driving my crazy", "a");