I came across this question on Mettl :
The function takes four parameters -
first name(String)
last name(String)
pin(int) : a number that contains digits of any length
eg: 1234,34565789
number(int) : a number(1-9 both inclusive and less than the length of the pin) that determines the number of digits that has to be used for the generation of id, from the beginning and the end of the pin entered above.
Conditions for generating the id are:
the name with smaller length is taken as the small name and the name with bigger length is taken as large name.
if the lengths of both the first and last name are equal, then the name occurring alphabetically first is taken as the small name.
the number for the generation is obtained using the 'number' and 'pin' inputs.
after the id is generated the letters are toggled,i.e the lowercase letters are changed to uppercase and vice-versa.
Example 1:
first name: Rajiv
last name: Roy
pin: 123456
number: 3
Here 'Roy' is smaller in length than 'Rajiv'. So 'Roy' becomes the small name and 'Rajiv' becomes the large name. The 3rd digit from starting of the pin is '3' and the 3rd digit from the end of the pin is '4'. Therefore the number generated is 34.
The last letter of smaller name is 'y'. So the id generated is yRajiv34.
After toggling the letters, the final id generated is YrAJIV34.
function generateId(firstName, lastName, pin, n) {
var result = '';
var smallName = '';
var largeName = '';
if (firstName.length != lastName.length) {
if (firstName.length < lastName.length) {
smallName = firstName;
largeName = lastName;
} else {
largeName = firstName;
smallName = lastName;
}
} else {
var names = [firstName.toLowerCase(), lastName.toLowerCase()];
var sorted = names.sort(function (a, b) {
return a > b;
});
smallName = sorted[0];
largeName = sorted[1];
}
result += smallName.substr(smallName.length - 1, 1).toUpperCase();
result += largeName.substr(0, 1).toLowerCase() + largeName.substr(1, largeName.length).toUpperCase();
var pinString = pin.toString();
var numberLength = pinString.length;
result += pinString.charAt(n - 1) + pinString.charAt(numberLength - n);
return result;
}
var x = generateId('Kumar', 'Kumud', 530076, 2);
console.log(x);
var y = generateId('Rajiv', 'Roy', 345678, 3);
console.log(y);
problem:
its working well for all the test cases I pass and even got 7/10 test cases correct in the mettl site. I don't know what the reason is for the failure of the remaining 3 test cases. Is there a way to improve the algorithm or anything wrong with my code?
You are not toggling the characters, you are just assuming that first character of the string will be uppercase and making it lower case and all other characters will be in lower case and making them uppercase.
This won't work for cases such as:
generateId('RajIV', 'Roy', 345678, 6); // YrAJIV83
Also you are not handling the case where number is greater than the length of pin. It still gives an output.
generateId('RajIV', 'Roy', 345678, 7); // YrAJIV
Related
I keep a spreadsheet of the titles and issue numbers I've read for various comics. I want to have a column that provides the count of the issues read for each title. Some titles have issue numbers in multiple lines. For example, the run of Avengers starting in 2016 has the following issue numbers listed in one cell, each range on a new line within the cell:
#1-11
#1.1-5.1
#1MU
#672-676
I tried to write a script that would separate each line into an array item, and then for each item in the array, extract the numbers using regular expressions to perform calculations to determine the total count. (The count of the above issues is 22, but the problem is getting the script to determine that for me and for the other titles as I update issue numbers.)
Here's what I have so far:
function calcIssueCount(x) {
// Set initial value to 0:
var issueCount = 0;
// Make an array from the lines of the cell
// split by the line break:
var box = x.split("\n");
for (var line in box) {
// Determine if the line includes a
// range of issue numbers, indicated
// by presence of a hyphen:
if ("-" in line === True) {
// Remove the # symbol from the string:
line = line.replace("#","");
// Extract the number before the hyphen
// and delete the hyphen:
var a = line(/[0-9}+\.|[0-9]+-/);
a = a.replace("-","");
// Extract the number after the hyphen
// and delete the hyphen:
var b = line(/-[0-9}+\.|[0-9]+/);
b = b.replace("-","");
// Turn a and b into numbers:
a = number(a)
b = number(b)
// Subtract the range start from the
// range end:
var c = b - a;
// Add 1 because a range of 1-5 includes 5
// issues, while 5-1 is only 4:
c += 1;
// Update the count for the number of
// issues in the cell by adding the
// line's value:
issueCount += c;
}
else {
// If there's no hyphen, there's no
// range; the count of issues on the
// line is 1:
issueCount += 1;
}
}
}
calcIssueCount(x) would have x as the cell name (e.g., D15).
I'm likely making mistakes all over the place, but I especially think I'm not understanding something about getting the cell data into the array into the first place. What am I missing here?
You want to retrieve "22" from the following value in a cell.
#1-11
#1.1-5.1
#1MU
#672-676
As the logic for retrieving "22" from the value, I understood as follows.
Retrieve 1 and 11 from #1-11.
Retrieve 1.1 and 5.1 from #1.1-5.1.
Retrieve 672 and 676 from #672-676.
Subtract before number from after number for each line.
Add 1 to all lines.
For the line without -, add only 1.
From above logic, (11 - 1 + 1) + (5.1 - 1.1 + 1) + (1) + (676 - 672 + 1) = 22 can be obtained. If my understanding of your logic is correct, how about the modification like below?
Modification points :
When for (var line in box) { is used, each element can be retrieved by box[line].
In this modicication, I used forEach.
In Javascript, the boolean express "true" and "false".
Line is not an object. So "-" in line occurs an error.
In this modification, I used indexOf().
In Javascript, number() is Number().
When you want to use calcIssueCount(x) as a custom function, in your current script, no values are returned. So if you want to retrieve issueCount, please add return issueCount.
I couldn't understand about the function of line(/[0-9}+\.|[0-9]+-/). I'm sorry for this.
In my modification, I retrieved both the before and after numbers using a regex of ([0-9.]+)-([0-9.]+). I think that there are several solutions for your situation. So please think of this as one of them.
Modified script :
Pattern 1 :
In this pattern, your script was modified.
function calcIssueCount(x) {
// Set initial value to 0:
var issueCount = 0;
// Make an array from the lines of the cell
// split by the line break:
var box = x.split("\n");
box.forEach(function(e){
if (e.indexOf("-") > -1) {
var numbers = e.match(/([0-9.]+)-([0-9.]+)/);
// Extract the number before the hyphen
// and delete the hyphen:
var a = Number(numbers[1]);
// Extract the number after the hyphen
// and delete the hyphen:
var b = Number(numbers[2]);
// Subtract the range start from the
// range end:
var c = b - a;
// Add 1 because a range of 1-5 includes 5
// issues, while 5-1 is only 4:
c += 1;
// Update the count for the number of
// issues in the cell by adding the
// line's value:
issueCount += c;
} else {
// If there's no hyphen, there's no
// range; the count of issues on the
// line is 1:
issueCount += 1;
}
});
return issueCount;
}
Pattern 2 :
This is other sample script. In this pattern, the regex is not used. The result is the same with pattern 1.
function calcIssueCount(x) {
return x.split("\n").reduce(function(c, e) {
if (e.indexOf("-") > -1) {
var numbers = e.slice(1).split("-");
c += (Number(numbers[1]) - Number(numbers[0])) + 1;
} else {
c += 1;
}
return c;
}, 0);
}
Result :
Note :
In this modified script, the regex of ([0-9.]+)-([0-9.]+) is used for the sample value. If you want to use values with other patterns, please tell me. At that time, can you show me more samples?
References :
Custom Functions in Google Sheets
Array.prototype.forEach()
String.prototype.indexOf()
Number
String.prototype.match()
If I misunderstand your question, I'm sorry.
Case 1:
input: 145
output: 146
Case 2:
input 199
output 1001
Case 3:
input: 59
output 501
Case 4:
input: 99
output: 901
So the first number should never change.
Reason is the first number is a classification (1-9). The number following is serial numbers.
So 199 is classifcation '1' and serial number 99. The next serial number is 1001. Henche the odd logic (I didn't decide this).
Any smart way of doing this?
Convert the number to a string. Get the first digit as the first character of the string, and the rest as the remaining characters. If the rest is all 9, increment it to that many 0's + 1, otherwise just add 1 to it. Then concatenate this back with the original first digitl
function increment_serial(input) {
var str = input.toString();
var first = str[0];
var rest = str.substr(1);
if (rest.match(/^9+$/)) {
var newrest = rest.replace(/9/g, '0') + '1';
} else {
newrest = parseInt(rest, 10) + 1;
}
return first + newrest;
}
console.log(increment_serial(145));
console.log(increment_serial(199));
console.log(increment_serial(59));
console.log(increment_serial(99));
Have two variables, a prefix and the number:
var prefix = "5";
var number = 99;
var output = prefix + number;
alert(output);
number = number + 1;
output = prefix + number;
alert(output);
And here is the fiddle: https://jsfiddle.net/xedret/0ujgjsbj/
function incrementNumberLeavingFirst(number) {
var numberAsString = number+"";
incrementedNumber = parseInt(numberAsString.slice(1))+1
return parseInt(numberAsString[0]+incrementedNumber);
}
alert(incrementNumberLeavingFirst(599))
We take an integer, number, as the first parameter of our function. This is the number that we want to increment.
Adding a string to an integer in JavaScript converts the integer to a string, so we get the string version of the number parameter by adding an empty string to it, and store that in the variable numberAsString.
Then, we take all of that string except for the first character using numberAsString.slice(1), convert it to an integer using parseInt, and add one. This increments the number (excluding the first digit). We store the incremented number in a variable called incrementedNumber.
Finally, we concatenate the first digit of the number to the incremented number (note that the first digit is still a string, so this converts incrementedNumber to a string, and adds the first number to the start of that string. Then we convert the whole thing to an integer using parseInt, and return it.
I had a technical interview and this question was particularly hard for me to answer. Not sure how to go about this issue, would really appreciate if someone helped me figure out this question.
You have a string that states the price of an item. You want to return a string with the all the numbers 15% off the original number.
example.)
if an item was 15% off
input string:
str1 = "This person bought an iphone for $500 and a macbook for $1400"
output string:
str2 = "This person bought an iphone for $425 and a macbook for $1190"
I know I should split the string, and probably locate all the numbers that proceeds a "$" symbol but I am not sure how to go from there.
Based on #goat's idea
var x = "This person bought an iphone for $500 and a macbook for $1400";
function getDiscount(str) {
var origPrices = str.match(/(\d{1,})/gi);
var discountPrices = origPrices.map(function(value) {
return value - value * 0.15;
});
origPrices.forEach(function(currentvalue, index){
str = str.replace(currentvalue, discountPrices[index]);
});
return str;
}
getDiscount(x);
Using String.replace with a RegExp and a callback receiving the full match and the matching group as parameters:
let str = "This person bought an iphone for $500 and a macbook for $1400"
let result = str.replace(/\$(\d+)/g, (match, price) => '$' + 0.85 * price);
console.log(result);
I assumed you only want to match prices preceded by a dollar symbol. Depending on your needs, you could refine the RegExp to match prices with decimal points etc.
String input = "This person bought an iphone for $500 and a macbook for $1400";
int index = input.indexOf('$');
int inNumber = 0;
String number = "";
while (inNumber != -1) {
inNumber++;
char temp = input.charAt(index + inNumber);
int value = Character.getNumericValue(temp);
if (value >= 0 && value < 10) {
number = number + value;
} else
break;
if(temp==' ')
break;
}
in variable number, you have got your first numeric, and again run the method to get the second, by using String.subString(index)
and running the same process again, use Integer.parseInt() to get the int calcuate discounts and replace it with original index respectively , you know starting indexes of both strings
this is a quick logic if you do not know the regex at the interview
I am trying to solve the problem described here with JavaScript...
https://www.hackerrank.com/challenges/ctci-making-anagrams
I have to output the number of letters that would need to be removed from two strings in order for there to only be matching letters (compare the two strings for matching letters and total the letters that don't match)
for example...
string a = cbe
string b = abc
the only matching letter is between both strings is the two c's so I would be removing 4 letters (beab).
My code works, but it seems to keep timing out. If I download the individual test case instances, I seem to fail when variables a and b are set to large strings. If I test these individually, I seem to get the right output but i still also get the message "Terminated due to timeout".
I'm thinking it might be obvious to someone why my code timesout. I'm not sure it's the most elegant way of solving the problem but I'd love to get it working. Any help would be much appreciated...
function main() {
var a = readLine();
var b = readLine();
var arraya = a.split('');
var arrayb = b.split('');
var arraylengths = arraya.length + arrayb.length;
//console.log(arraylengths);
if (arraya.length <= arrayb.length) {
var shortestarray = arraya;
var longestarray = arrayb;
} else {
var shortestarray = arrayb;
var longestarray = arraya;
}
var subtract = 0;
for (x = 0; x < shortestarray.length; x++) {
var theletter = shortestarray[x];
var thenumber = x;
if (longestarray.indexOf(theletter, 0) > -1) {
var index = longestarray.indexOf(theletter, 0);
longestarray.splice(index, 1);
subtract = subtract + 2;
}
}
var total = arraylengths - subtract;
console.log(total);
}
Your algorithm is good. It's straight forward and easy to understand.
There are certain things you can do to improve the performance of your code.
You don't have to calculate the indexOf operation twice. you can reduce it to one.
the splice operation is the costliest operation because the JS engine has to delete the element from an array and reassign the indexes of all the elements.
A point to be noted here is that the JS engine does an extra step of correcting the index of the array, which is not required for your purpose. So you can safely remove longestarray.splice(index, 1); and replace it with delete longestarray[index]
Here is a snippet which will increase your performance of the code without changing your logic
for (var x = 0; x < shortestarray.length; x++) {
var theletter = shortestarray[x];
var thenumber = longestarray.indexOf(theletter, 0); // <-- check only once
if (thenumber > -1) {
var index = thenumber;
delete longestarray[index]; // <-- less costlier than splice
subtract = subtract + 2;
}
}
Note: I am not suggesting you to use delete for all the cases. It's useful here because you are not going to do much with the array elements after the element is deleted.
All the best. Happy Coding
I would suggest you hashing. make the characters of string key and its numbers of occurrences value. Do the same for both strings. After that take string 1 and match the count of its every character with the count of same character in string then calculate the difference in the number of occurrences of the same character and delete that character till the difference becomes 0 and count that how many times you performed delete operation.
ALGORITHM:
step 1: Let arr1[255]= an integer array for storing the count of string1[i]
and initialized to zero
ex: string1[i]='a', then arr1[97]=1, because ASCII value of a is 97
and its count is 1. so we made hash table for arr1 where key is
ASCII value of character and value is its no of occurrences.
step 2: Now declare an another array of same type and same size for string 2
step 3: For i=0 to length(string1):
do arr1[string1[i]]++;
step 4: For i=0 to length(string2):
do arr2[string2[i]]++;
step 5: Declare an boolean char_status[255] array to check if the
character is visited or not during traversing and initialize it to
false
step 6: set count=0;
step 7: For i=0 to length(string1):
if(char_status[string1[i]]==false):
count=count+abs(arr1[string1[i]]-arr2[string1[i]])
char_status[string1[i]]=true
step 8: For i=0 to length(string2):
if(char_status[string2[i]]==false):
count=count+abs(arr1[string2[i]]-arr2[string2[i]])
char_status[string2[i]]=true
step 9: print count
I have applied this algo just now and passed all test cases. You may improve this algo more if you have time.
First of all,
What am i doing ?
I have to set the limit of emails in our product in webpage.It's handled with the javascript for validation.It handles upto 8 digit numbers fine. But in our QA team enters the more than 17 digit number in the text box of other email field.It throw the negative message.What can i do ???
My sample code is:
if(form.otherEmails) {
if(validEmailArray.endsWith(',')){
var otherEmailLength = validEmailArray.substring(0,validEmailArray.length-1).split(",");
var setLimitOtherEmail = window.parent.document.getElementById('setLimitOtherEmail').value;
if(setLimitOtherEmail == '-1'){
form.otherEmails.value = otherEmailLength;
}
else if(otherEmailLength.length <= setLimitOtherEmail){
form.otherEmails.value = otherEmailLength;
}
else{
alert("More than "+setLimitOtherEmail+ " " +"Recipient emailIds not allowed in this section.\nIf you want to send it to more recipients, Please create a Bulk Contact Group.");
form.otherEmails.focus();
return false;
}
}
else
form.otherEmails.value = validEmailArray;
}
This is due to the limit being a string, and when a string is being compared to a number (length) the number is coerced into a string, not the other way around.
These are then compared lexicographically - and lexicographically "9" is more (>) than "19".
You need to use parseInt(setLimitOtherEmail, 10) to get the value as a number before comparing them.
Try parsing each of the numbers into Integers before performing any comparison operations on them.
var setLimitOtherEmail = parseInt(window.parent.document.getElementById('setLimitOtherEmail').value);
Other than that are you certain otherEmailLength is actually the number that you want? From the looks of it you are taking the substring of validEmail array and splitting it on "," but it doesn't look like you actually get the length of the array. Try adding .length to the end of the value of otherEmailLength.
var otherEmailLength = validEmailArray.substring(0,validEmailArray.length-1).split(",").length;