issue with substring indexing - javascript

Instructions for this kata:
In this Kata, we will check if a string contains consecutive letters as they appear in the English alphabet and if each letter occurs only once.
It seems that my code is indexing the strings differently per function call on this one. for example, on the first test "abcd", the starting index is shown as 0, which is correct, and on the second example, "himjlk", the
var subString = alphabet.substring(startIndex, length);
returns "g", instead of "h"
troubleshooting this section
var length = orderedString.length;
//startChar for string comparison
var startChar = orderedString.charAt(0);
//find index in aphabet of first character in orderedString.
var startIndex = alphabet.indexOf(startChar);
//create substring of alphabet with start index of orderedString and //orderedString.length
var subString = alphabet.substring(startIndex, length);
function solve(s) {
//alphabet string to check against
const alphabet = `abcdefghijklmnopqrstuvwxyz`;
//check s against alphabet
//empty array to order input string
var ordered = [];
//iterate through alphabet, checking against s
//and reorder input string to be alphabetized
for (var z in alphabet) {
var charToCheck = alphabet[z];
for (var i in s) {
if (charToCheck === s[i]) {
ordered.push(s[i]);
}
//break out of loop if lengths are the same
if (ordered.length === s.length) {
break;
}
}
if (ordered.length === s.length) {
break;
}
}
//join array back into string
var orderedString = ordered.join(``);
//length for future alphabet substring for comparison
var length = orderedString.length;
//startChar for string comparison
var startChar = orderedString.charAt(0);
//find index in aphabet of first character in orderedString.
var startIndex = alphabet.indexOf(startChar);
//create substring of alphabet with start index of orderedString and orderedString.length
var subString = alphabet.substring(startIndex, length);
//return if the two are a match
return subString == orderedString ? true : false;
}
console.log(solve("abdc")); //expected `true`
console.log(solve("himjlk")); // expected `true`
console.log(solve("abdc")); should provide the substring "abcd" and return true, which it does.
console.log(solve("himjlk")); should put together "hijklm" and return true, but instead gives me g based on index 6 of alphabet, not sure why it's doing this, should be index 7 "h" returns false based upon this error.

The problem is that you're using substring() instead of substr(). Though that might sound similar there's a difference.
With substring the second parameter doesn't determine the length as you might have expected. It's actually the index to stop.
That your function works as expected with the string abcd is pure coincidence since in this case the length from index 0 and the end index are the same.
function solve(s){
const alphabet = `abcdefghijklmnopqrstuvwxyz`;
var ordered = [];
for(var z in alphabet){
var charToCheck = alphabet[z];
for(var i in s){
if(charToCheck === s[i]){
ordered.push(s[i]);
}
if(ordered.length === s.length){ break; }
}
if(ordered.length === s.length){ break; }
}
var orderedString = ordered.join(``);
var length = orderedString.length;
var startChar = orderedString.charAt(0);
var startIndex = alphabet.indexOf(startChar);
var subString = alphabet.substr(startIndex, length);
return subString == orderedString ? true: false;
}
console.log(solve("himjlk"));

You approach is also correct. I am giving another solution using sort() and charCodeAt. Instead of getting the index and then breaking string into parts to compare just use includes()
function check(str){
let org = [...Array(26)].map((x,i) => String.fromCharCode(i + 97)).join('');
str = str.split('').sort((a, b) => a.charCodeAt(0) - b.charCodeAt(0)).join('');
return org.includes(str);
}
console.log(check("abdc"))//true
console.log(check("himjlk"));//true
console.log(check("himjlkp"));//false
Explanation:
Frist Line:
let org = [...Array(26)].map((x,i) => String.fromCharCode(i + 97)).join('');
is use to create string "abcd....xyz".
[...Array(26)] will create an array of 26(no of alphabets) undefined values.
map() is a function which takes a callback and the create an array based the values of previous. The first parameter of map() callback x is the value itself which will be undefined(because all the values in array are undefined).
i the second parameter will be the index of the element. Which will start from 0 upto 25.
String.fromCharCode is function which takes a character code(integer) and then convert it to string. For example character code for a is 97 so String.fromCharCode(97) will return "a". 98 for "b", 99 for "c" etc.
So after map() an array like ["a","b"....,"z"] will be generated.
-join() will convert that to string
Second Line:
str is given string. str.split('') will convert string to array. For example
if str is "abdc" it will return ["a","b","d","c"]
sort() is the array method which takes the callback. The two parameters are two values to be compared during sort(). a and b are two values.
charCodeAt acts in reverse as String.fromCharCode. For example "a".charCodeAt(0) will be return 97 for "b" it will 98 and so on.
a.charCodeAt(0) - b.charCodeAt(0) which is returned from sort() will sort array is ascending order. And join() will convert array to string.
So string "abdc" will become "abcd"
Third Line:
The third line is the main one. org is string "abcdefghijklmnopqrstuvwxyz". Now if any string is a substring of this string then it means its in alphabetical order. So we check the sorted str is includes in the string or not.
You can clean up the second line by
str = str.split('').sort().join('');
Because if no callback is passed to sort() it will sort in default order. Mean alphabetical order.

Related

How can I sum all numbers 1 from a string?

I need to sum all numbers 1 from a string!
For example: "00110010" = 1+1+1 = 3...
psum will receive this result and then I will check
if(psum >= 3){
return person;
}
I need to find a way to solve it in javascript ES6 but I can't use any for, while or forEach loop, unfortunately!!!
Could you help me?
You need to use the reduce() method.
let input = '00110010'
let array = input.split("").map(x => parseInt(x));
let sum = array.reduce((acc, val) => {
return acc + val;
});
console.log(sum)
In one statement:
let psum = "00110010".split('').reduce((t, n) => {return t + parseInt(n)}, 0);
console.log(psum);
Note that summing the numbers 1 comes down to counting the numbers 1, which is what the following solutions do in different ways:
With match
You could use a regular expression /1/g:
var p = "00110010";
var psum = (p.match(/1/g) || []).length;
console.log(psum);
match returns an array of substrings that match with the pattern 1. The / just delimit this regular expression, and the g means that all matches should be retrieved (global). The length of the returned array thus corresponds to the number of 1s in the input. If there are no matches at all, then match will return null, so that does not have a .length property. To take care of that || [] will check for that null (which is falsy in a boolean expression) and so [] will be taken instead of null.
With replace
This is a similar principle, but by matching non-1 characters and removing them:
var p = "00110010";
var psum = p.replace(/[^1]/g, "").length;
console.log(psum);
[^1] means: a character that is not 1. replace will replace all matches with the second argument (empty string), which comes down to returning all characters that do not match. This is like a double negative: return characters that do not match with not 1. So you get only the 1s :-) .length will count those.
With split:
var p = "00110010";
var psum = p.split("1").length - 1;
console.log(psum);
split splits the string into an array of substrings that do not have the given substring ("1"). So even if there are no "1" at all, you get one such substring (the whole string). This means that by getting the length, we should reduce it by 1 to get the number of 1s.
With a recursive function:
var p = "00110010";
var count1 = p => p.length && ((p[0] == "1") + count1(p.slice(1)));
var psum = count1(p);
console.log(psum);
Here the function count1 is introduced. It first checks if the given string p is empty. If so, length is zero, and that is returned. If not empty, the first character is compared with 1. This can be false or true. This result is converted to 0 or 1 respectively and added to a recursive call result. That recursive call counts the number 1s in the rest of the input (excluding the first character in which the 1s were already counted).

Why does 'duplicates' property keeps returning 3 instead of 2?

The task I am undertaking is as follows:
The function should be called removeDuplicates and should return an object literal containing a 'uniques' property, which should be the sorted input string but without any duplicates or special characters.
The returned object should also have a 'duplicates' property which should represent the total number of duplicate characters dropped.
So:
removeDuplicates('th#elex_ash?')
should return:
{uniques: 'aehlstx', duplicates: 2}
Here is my code:
function removeDuplicates(str) {
var stg = str.split("");
var nstr = [];
var allowed = ['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 count = 0;
for(var i = 0; i<stg.length;i++){
if(nstr.indexOf(stg[i])== -1){
if(allowed.indexOf(stg[i]) > -1){
nstr.push(str[i])
}
else{
count +=1;
}
}
}
return{uniques: nstr.sort().join(""),duplicates: count}
}
But the result returns {uniques: 'aehlstx', duplicates: 3} instead.
You are checking first if you've seen the character before, and THEN checking it was an allowed character and counting the number of times you see a non-allowed character.
You need to check if the character is allowed, and then see if you've seen it before.
Because you are counting the not allowed characters :)

How to check if one element of an array matches another element in same array?

Very new to javascript so bear with me...
I need to check one element of an array(arr[1]), which contains a string, against another element of the same array(arr[0]) to determine if any letters included in element arr[1] are included in arr[0]. Those letters can be in any order, upper or lower case, and don't have to occur the same number of times (i.e. arr[0]="hheyyy" and arr[1]="hey" is fine). This is what i have (which works) but I was curious if anyone has a better/more simple way of doing this? -thanks in advance.
function mutation(arr) {
//splits the array into two separate arrays of individual letters
var newArr0 = arr.join('').toLowerCase().split('').slice(0,arr[0].length);
var newArr1 = arr.join('').toLowerCase().split('').slice(arr[0].length);
var boolArr = [];
//checks each letter of arr1 to see if it is included in any letter of arr0
for(var i = 0; i < newArr1.length; i++)
boolArr.push(newArr0.includes(newArr1[i]));
//results are pushed into an array of boolean values
if (boolArr.indexOf(false) !==-1)
return false; //if any of those values are false return false
else return true;
}
mutation(["hello", "hey"]); //returns false
You could use a regular expression:
function mutationReg(arr) {
return !arr[1].replace(new RegExp('['+arr[0].replace(/(.)/g,'\\\\$1')+']', "gi"), '').length;
}
This escapes every character in the second string with backslash (so it cannot conflict with regular expression syntax), surrounds it with square brackets, and uses that as a search pattern on the first string. Any matches (case-insensitive) are removed from the result, so that only characters are left over that don't occur in the second string. The length of the result is thus an indication on whether there was success or not. Applying the ! to it gives the correct boolean result.
This might not be the fastest solution.
Here is another ES6 alternative using a Set for good performance:
function mutation(arr) {
var chars = new Set([...arr[0].toLowerCase()]);
return [...arr[1].toLowerCase()].every (c => chars.has(c));
}
You can use Array.from() to convert string to an array, Array.prototype.every(), String.prototype.indexOf() to check if every charactcer in string converted to array is contained in string of other array element.
var arr = ["abc", "cab"];
var bool = Array.from(arr[0]).every(el => arr[1].indexOf(el) > -1);
console.log(bool);

Adding numbers within a string

I want to take a string of numbers and characters and add up the numbers.
For example: "In 2015, I want to know how much does iPhone 6+ cost?"
Output: 2021
Here is my current code:
var str = "In 2015, I want to know how much does iPhone 6+ cost?";
function sumFromString(str){
var punctuationless = str.replace(/['!"#$%&\\'()\*+,\-\.\/:;<=>?#\[\\\]\^_`{|}~']/g,"");
var finalString = punctuationless.replace(/\s{2,}/g," ");
var StringList = finalString.split(" ");
var sum = [];
for (i = 0; i < StringList.length; i++)
if (isInt(StringList[i])
sum.add(StringList[i]);
sum.reduce( (prev, curr) => prev + curr );
}
sumFromString(str);
My code takes a string and strips it of punctuation and then places each individual word/number into the array, StringList.
I can't get the next part to work.
What I tried was to iterate through each value in the array. The if statement is supposed to check if the array element is an integer. If so, it will add the integer to an empty array called sum. I then add all the values of the array, sum, together.
Much simpler:
function sumFromString(str) {
return (str.match(/\d+/g)||[]).reduce((p,c)=>+c+p);
}
Note in particular that I use +c+p - +c casting the current value from a string to a number, then adding it to p. This matches all the numbers in the string - getting an empty array if there were none - and reduces that.
For the sake of variety, here's a way to do it without regular expressions:
var myString = "5 bunnies ate 6 carrots in 2days.";
var myArray = myString.split('');
var total = 0;
for (var i = 0; i < myArray.length; i++) {
if (!isNaN(parseInt(myArray[i]))) {
total += parseInt(myArray[i]);
}
}
Fiddle Demo
note: If there's a chance myString could be null, you'd want to add a check before the split.
Split the string into an array of all characters with the split function and then run the filter function to get all numbers. Use the map function to go through all elements that include numbers, and delete characters from them that aren't digits.
Then use reduce to get the sum of all numbers. Since we're dealing with strings here, we have to perform type conversion to turn them into numbers.
string.split(' ').filter(function(word) {
return /\d+/.test(word) }
}).map(function(s) {
return s.replace(/\D/, '')
}).reduce(function(a,b) {
return Number(a) + Number(b);
});

Tokenize a JavaScript String depending on the characters

In JavaScript, let's say I have a String like "23+var-5/422*b".
I want to split this String so that I get [23,+,var,-,5,/,422,*,b].
I want to tokenize it so that I split the string into 3 types of tokens:
Numerical literals, [0-9].
String literals, [A-z].
Operator characters, [-+*/].
So basically, go through the string, and for each "cluster of characters" that share the same class (each with 1 or more characters), convert that into a token.
I could probably use a for loop, comparing each character with each class, and manually create a token every time the current "character class" changes... it would be very tedious and use many variables and loops.
Does anyone know a more elegant (less verbose) way to get there?
A global regexp match will do this for you:
var str = "23+var-5/422*b";
var arr = str.match(/[0-9]+|[a-zA-Z]+|[-+*/]/g); // notice the creation of one token
// per operator (even if consecutive)
However, it simply ignores invalid characters instead of erroring out.
Here's a way to do it using Regex. Obviously the code can be simplified more if you use Underscore.js or CoffeeScript. So here's a longer version using vanilla JS:
var s = "23+var-5/422*b"; // your string
var re1 = /[0-9]/; // Regex for numerals
var re2 = /[a-zA-Z]/; // Regex for roman chars
var re3 = /[-+*\/]/; // Regex you wanted for operators
// Helper function, return true if n none-negative
function nonNegative(n) {
return n >= 0;
}
// helper function: add any none-negative n to array arr
function addNonNegative(n, arr) {
if (nonNegative(n)) {arr.push(n)};
}
// The main function to split string s
function split(s) {
var result = []; // The result array, initialized
// Do while string s is none empty.
while(s.length > 0) {
// The order of indices of regex found
var order = [];
// search for index or which the regex occurs, then if that index is none-negative, add it to the 'order' array
addNonNegative(s.search(re1), order);
addNonNegative(s.search(re2), order);
addNonNegative(s.search(re3), order);
// sort the order array
order = order.sort();
// variables to slice the string s.
// start is always 0. Marks the starting index of the first matched regex
var start = order.shift();
// Marks the starting index of the second matched regex
var end = order.shift(); // end is the second result in order
result.push(s.slice(start, end)); // slice the string s from start to end
// update s so that exclude what was sliced before
s = s.slice(end);
// boundary condition: finally when end is null once all regex have been pulled, set s = ""
if (end == null) {s = ""};
}
return result;
}

Categories

Resources