JS detectNetwork Coding Exercise - javascript

I'm having trouble trying to solve the following JavaScript coding exercise.
Write the function detectNetwork. It should accept a string or a
number for its cardNumber argument, and should return the appropriate
network string (or undefined if there's no match), based on the
provided cardData.
What I'm struggling to figure out is how to write this function without hardcoding every behavior of the cardData array. I want to effectively use the cardData array as an argument of the function, but have no idea how to.
var cardData = [{
network: 'Visa', // card issuer (network)
prefixes: ['4'], // beginning digits
lengths: [13, 16, 19] // lengths of card numbers
},{
network: 'Mastercard',
prefixes: ['51', '52', '53', '54', '55'],
lengths: [16]
},{
network: 'American Express',
prefixes: ['34', '37'],
lengths: [15]
},{
network: 'Diner\'s Club',
prefixes: ['38', '39'],
lengths: [14]
}];
function detectNetwork(cardNumber, cardData) {
// your solution here
}
// example
var network = detectNetwork('343456789012345', cardData);
console.log(network); // --> 'American Express'

Here is my solution that works it this particular case)
function detectNetwork(cardNumber, cardData) {
// your solution here
if(typeof cardNumber === "number"){
cardNumber = cardNumber.toString()
}
for(let i = 0; i < cardData.length; i++){
if(cardData[i]['lengths'].includes(cardNumber.length) && cardData[i]['prefixes'].toString() === cardNumber[0]){
return cardData[i]['issuer/network'];
} else if (Number(cardData[i]['lengths'].toString()) === cardNumber.length){
return cardData[i]['issuer/network'];
};
};
return undefined
}

Main idea is first checking the length of card. If we found the element in the array with similar length, we are checking elements in array with prefixes inside this object. If we found similar prefix, returning the name of the network.
function detectNetwork(cardNumber, cardData) {
var answer = '';
var cardLength = cardNumber.length;
var firstTwoDigits = cardNumber[0] + cardNumber[1];
for (var i =0; i < cardData.length; i++){
var cardObj = cardData[i];
if (cardObj['lengths'] == cardLength){
var arrOfPrefixes = cardObj['prefixes'];
for (var j =0; j < arrOfPrefixes.length; j++){
if (arrOfPrefixes[j] === firstTwoDigits){
answer = cardObj['network'];
}
}
}
}
return answer;
}

My approach is
compare the cardNumber length with each issuer's lengths
if there's a match, find the target prefix length and slice the card number to match it
comparing each issuer's prefixes with card number's prefixes using for loop
if there's a match, update the issuer
function detectNetwork(cardNumber, cardData) {
if(typeof cardNumber === 'number'){
cardNumber = cardNumber.toString();
}
let cardLength = cardNumber.length;
let issuer;
for(let x = 0; x < cardData.length; x ++ ){
let currentIssueObj = cardData[x];
let currentIssueLengths = cardData[x].lengths;
let currentIssuePrefixes = currentIssueObj.prefixes;
if ( currentIssueLengths.includes(cardLength)) {
let targetPrefixesLength = findMaxPrefixesLength(currentIssuePrefixes);
let cardPrefixes = cardNumber.slice(0,targetPrefixesLength);
for( let index = 0; index < currentIssuePrefixes.length; index ++ ){
if( cardPrefixes === currentIssuePrefixes[index]) {
issuer = currentIssueObj['issuer/network'];
break;
}
}
}
}
return issuer;
}
//helper function
function findMaxPrefixesLength(arr){
let maxLength = arr[0].length;
for(let x = 1; x < arr.length; x ++ ){
if (arr[x].length > maxLength){
maxLength = arr[x].length;
}
}
return maxLength;
}

function detectNetwork(cardNumber, cardData) {
var output = "";
var input = String(cardNumber);
var firstDig = input.slice(0, 1);
var first2Dig = input.slice(0, 2);
for(var i = 0; i < cardData.length; i++){
if(cardData[i].prefixes.includes(firstDig) || cardData[i].prefixes.includes(first2Dig)){
output += cardData[i]['issuer/network'];
return output;
}
}
return undefined;
}

I know this question was posed a long time ago, but I saw it on the hack reactor website. If the answer helps anyone then I'm glad.
// This function loops over cardData and uses helper functions below to validate if the lengths and prefixes are found in cardNumber.
function detectNetwork(cardNumber, cardData) {
for (i = 0; i < cardData.length; i++) {
let card = cardData[i]
let cardLengthFound = findLengths(card.lengths)
let cardPrefixFound = findPrefixes(card.prefixes)
if(cardLengthFound && cardPrefixFound) {
return card['issuer/network']
}
}
}
// This function checks to see if the prefixes in cardNumber is same as the ones in cardData
function findPrefixes(arr) {
for (j = 0; j < arr.length; j++) {
if (cardNumber.toString().slice(0, 2).includes(arr[j])) {
return true;
}
}
}
// This function checks to see if the length in cardNumber is same as the ones in cardData
function findLengths(arr) {
for (k = 0; k < cardNumber.toString().length; k++) {
if (cardNumber.toString().length === arr[k]) {
return true;
}
}
}

I really liked Max's answer but I don't have enough rep to leave a comment under his post. I pretty much refactored his code because it was easy to understand. Here's my solution:
function detectNetwork(cardNumber, cardData) {
// your solution here
var inputCardLength = cardNumber.length;
var inputCardNumbers = cardNumber[0] + cardNumber[1];
for(let counter = 0; counter < cardData.length; counter++){
let length = cardData[counter]['lengths'];
let prefix = cardData[counter]['prefixes'];
let network = cardData[counter]['issuer/network'];
if((length.includes(inputCardLength)) && (prefix.toString() === inputCardNumbers)) {
return network;
} else if(Number(length) === inputCardLength) {
return network;
};
};
return undefined
};

One way to do this is to iterate over the cardData array, and then test the values of the prefixes and length keys against the cardNumber, ensuring we cast to either a string or an int, as the question states that cardNumber can be either a string or an int.
function detectNetwork(cardNumber, cardData) {
for(let i = 0, {length} = cardData; i < length; i++){
if(cardData[i].lengths.some(length => length === cardNumber.length) && cardData[i].prefixes.some(prefix => cardNumber.toString().startsWith(prefix.toString()))){
return cardData[i].network
}
}
}
The key parts are the Array.prototype.some() and String.prototype.startsWith() methods
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
Hope this helps - good luck with JS - check out https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw for a good intro for a new JS programmer :)

Related

Longest Common Prefix in Javascript

I am trying to solve the Leet Code challenge 14. Longest Common Prefix:
Write a function to find the longest common prefix string amongst an array of strings.
If there is no common prefix, return an empty string "".
Example 1:
Input: strs = ["flower","flow","flight"]
Output: "fl"
Example 2:
Input: strs = ["dog","racecar","car"]
Output: ""
Explanation: There is no common prefix among the input strings.
Constraints:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] consists of only lower-case English letters.
My solution:
let strs = ["flower", "flow", "flight"];
var longestCommonPrefix = function (strs) {
for (let i = 0; i < strs.length; i++) {
for (let j = 0; j < strs[i].length; j++) {
// console.log(strs[i+2][j]);
if (strs[i][j] == strs[i + 1][j] && strs[i + 1][j] ==strs[i + 2][j]) {
return (strs[i][j]);
} else {
return "0";
}
}
}
};
console.log(longestCommonPrefix(strs));
Output: f
How can I iterate over every character and check if it is same and then go for next and if it fails then the longest common prefix will be returned?
As the longest common prefix must occur in every string of the array you can jus iterate over the length and check if all words have the same char at that index until you find a difference
function prefix(words){
// check border cases size 1 array and empty first word)
if (!words[0] || words.length == 1) return words[0] || "";
let i = 0;
// while all words have the same character at position i, increment i
while(words[0][i] && words.every(w => w[i] === words[0][i]))
i++;
// prefix is the substring from the beginning to the last successfully checked i
return words[0].substr(0, i);
}
console.log(1, prefix([]));
console.log(2, prefix([""]));
console.log(3, prefix(["abc"]));
console.log(4, prefix(["abcdefgh", "abcde", "abe"]));
console.log(5, prefix(["abc", "abc", "abc"]));
console.log(6, prefix(["abc", "abcde", "xyz"]));
Some of the issues:
Your inner loop will encounter a return on its first iteration. This means your loops will never repeat, and the return value will always be one character.
It is wrong to address strs[i+1] and strs[i+2] in your loop, as those indexes will go out of bounds (>= strs.length)
Instead of performing character by character comparison, you could use substring (prefix) comparison (in one operation): this may seem a waste, but as such comparison happens "below" JavaScript code, it is very fast (and as string size limit is 200 characters, this is fine).
The algorithm could start by selecting an existing string as prefix and then shorten it every time there is a string in the input that doesn't have it as prefix. At the end you will be left with the common prefix.
It is good to start with the shortest string as the initial prefix candidate, as the common prefix can certainly not be longer than that.
var longestCommonPrefix = function(strs) {
let prefix = strs.reduce((acc, str) => str.length < acc.length ? str : acc);
for (let str of strs) {
while (str.slice(0, prefix.length) != prefix) {
prefix = prefix.slice(0, -1);
}
}
return prefix;
};
let res = longestCommonPrefix(["flower","flow","flight"]);
console.log(res);
An approach based on sorting by word length, and for the shortest word, for exiting early, an entirely Array.every-based prefix-validation and -aggregation ...
function longestCommonPrefix(arr) {
const charList = [];
const [shortestWord, ...wordList] =
// sort shallow copy by item `length` first.
[...arr].sort((a, b) => a.length - b.length);
shortestWord
.split('')
.every((char, idx) => {
const isValidChar = wordList.every(word =>
word.charAt(idx) === char
);
if (isValidChar) {
charList.push(char);
}
return isValidChar;
});
return charList.join('');
}
console.log(
longestCommonPrefix(["flower","flow","flight"])
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
not the best solution but this should work
function longestPrefix(strs){
if(strs.length <1){
return "";
}
const sharedPrefix=function(str1,str2){
let i=0;
for(;i<Math.min(str1.length,str2.length) /*todo optimize*/;++i){
if(str1[i] !== str2[i]){
break;
}
}
return str1.substr(0,i);
};
let curr = strs[0];
for(let i=1;i<strs.length;++i){
curr=sharedPrefix(curr,strs[i]);
if(curr.length < 1){
// no shared prefix
return "";
}
}
return curr;
}
this:
strs[i][j] == strs[i + 1][j] ==strs[i + 2][j]
makes no sense in JS... or at least, makes no sense in what you are doing... to do this you should use a && operator, like this:
strs[i][j] == strs[i + 1][j] && strs[i + 1][j] ==strs[i + 2][j]
Otherwise JS will evaluate the first condition, and then will evaluate the result of that operation (either true or false) with the third value
In addition to this, consider that you are looping with i over the array, and so i will also be str.length - 1 but in the condition you are referencing strs[i + 2][j] that will be in that case strs[str.length + 1][j] that in your case, makes no sense.
About the solution:
You should consider that the prefix is common to all the values in the array, so you can take in consideration one value, and just check if all the other are equals... the most obvious is the first one, and you will end up with something like this:
let strs = ["flower", "flow", "flight", "dix"];
function longestCommonPrefix (strs) {
// loop over the characters of the first element
for (let j = 0; j < strs[0].length; j++) {
// ignore the first elements since is obvious that is equal to itself
for (let i = 1; i < strs.length; i++) {
/* in case you have like
[
'banana',
'bana'
]
the longest prefix is the second element
*/
if(j >= strs[i].length){
return strs[i]
}
// different i-th element
if(strs[0][j] != strs[i][j]){
return strs[0].substr(0, j)
}
}
}
// all good, then the first element is common to all the other elements
return strs[0]
};
console.log(longestCommonPrefix(strs));
you can do it like this, it works fast enough ~ 110ms
function longestCommonPrefix(strs){
if (strs.length === 0) {
return ''
}
const first = strs[0];
let response = '';
let prefix = '';
for (let i = 0; i < first.length; i++) {
prefix += first[i];
let find = strs.filter(s => s.startsWith(prefix));
if (find.length === strs.length) {
response = prefix;
}
}
return response;
};
let strs = ["flower", "flow", "flight"];
var longestCommonPrefix = function (strs) {
for (let i = 0; i < strs.length; i++) {
for (let j = 0; j < strs[i].length; j++) {
console.log(strs[i+2][j]);
if (strs[i][j] == strs[i + 1][j] && strs[i][j] ==strs[i + 2][j]) {
return (strs[i][j]);
} else {
return "0";
}
}
}
};
console.log(longestCommonPrefix(strs));
This **return ** f
Increase the index while the letter is the same at that index for all words in the list. Then slice on it.
function prefix(words) {
if (words.length === 0) { return '' }
let index = 0;
while (allSameAtIndex(words, index)) {
index++;
}
return words[0].slice(0, index);
}
function allSameAtIndex(words, index) {
let last;
for (const word of words) {
if (last !== undefined && word[index] !== last[index]) {
return false;
}
last = word;
}
return true;
}
I assume you are here for Leetcode problem solution.
var longestCommonPrefix = function(strs) {
let arr = strs.concat().sort();
const a1 = arr[0];
const a2 = arr[arr.length -1];
const length = a1.length;
let i=0;
while(i<length && a1.charAt(i) == a2.charAt(i)) i++;
return a1.substring(0,i);
};
function prefixLen(s1, s2) {
let i = 0;
while (i <= s1.length && s1[i] === s2[i]) i++;
return i;
}
function commonPrefix(arr) {
let k = prefixLen(arr[0], arr[1]);
for (let i = 2; i < arr.length; i++) {
k = Math.min(k, prefixLen(arr[0], arr[i]));
}
return arr[0].slice(0, k);
}
console.log(commonPrefix(['pirate', 'pizza', 'pilates'])) // -> "pi"
var longestCommonPrefix = function(strs) {
let prefix = "";
for(let i = 0; i < strs[0].length; i++) {
for(let j = 1; j < strs.length; j++) {
if(strs[j][i] !== strs[0][i]) return prefix;
}
prefix = prefix + strs[0][i];
}
return prefix;
};
console.log(longestCommonPrefix);
It is as simple as one loop and compare each element of the strings
const longestPrefix = (strs) => {
[word1, word2, word3] = strs;
let prefix = [];
if(strs === null || strs.length <= 2 || strs.length > 3) return 'please
insert 3 elements'
for (let i=0; i < word1.length; i++){
if(word1[i] === word2[i] && word1[i] === word3[i]){
prefix.push(word1[i])
}
}
return prefix.join('')
}
I read in another answer: 'Increase the index while the letter is the same at that index for all words in the list. Then slice on it.'
that's how I came up with this:
const findPrefix = (strs) => {
let i = 0;
while (strs.every((item) => strs[0][i] === item[i])) {
i++;
}
return strs[0].slice(0, i);
};
console.log(findPrefix(["flo", "flow", "flomingo"]));
const findPrefix = (strs) => {
let broke = false;
return strs[0].split("").reduce(
(acc, curr, index) =>
broke || !strs.every((word) => word[index] === curr)
? (broke = true && acc)
: (acc += curr),
""
);
};
console.log(findPrefix(["flower", "flow", "flamingo"]));
Here is my solution, Today I had an interview and the dev asked me the same question, I think I failed because I got stuck hahaha kinda nervous when someone is watching me 😂, anyway I decided to figure it out after the interview is done and this is my answer (without google it I swear) and for those who don't feel comfortable with the common "for loop"
const arr = ["absence", "absolute", "absolutely", "absorb"]
function getPrefix(arr) {
if (arr.length === 0 || arr.some(s => !s)) return null //if it's an empty array or one of its values is null or '', return null
const first = arr[0].split("") // turns the first position of the array, into an array
const res = arr.map(w => {
// mapping the original array
const compare = w.split("") // every item of the array will be converted in another array of its characters
return first.map((l, idx) => (compare[idx] === l ? l : "")).join("") // loop through the "first" array and compare each character
})
const prefix = first.join("").startsWith(res[res.length - 1]) // compare if the first letter starts with the last item of the returned array
? res[res.length - 1] // if true, return the final response which is the prefix
: null // else, return null, which means there is no common prefix
console.log("prefix: ", prefix)
return prefix
}
getPrefix(arr)
let arr = ["flower", "flow", "flight"]
function checkPrefix(array) {
let index = []
for (let i = 0; i <= array[0].length; i++) {
if (check(array[0][i], i, array)) {
index.push(i)
} else {
break;
}
}
console.log(array[0].substring(index[0], index[index.length - 1] + 1));
}
const check = (str, index, stringArr) => {
debugger
let status = true
stringArr.map(ele => {
debugger
if (ele[index] != str) {
status = false
}
})
return status
}
checkPrefix(arr)
/**
* #param {string[]} strs
* #return {string}
*/
var longestCommonPrefix = function(strs) {
let compare = strs[0];
let str = "";
for (let i = 1; i < strs.length; i++) {
let j = 0;
while(compare[j] != undefined && strs[i][j] != undefined) {
if(strs[i][j] == compare[j]) {
str += strs[i][j];
}
else break;
j++;
}
compare = str;
str = "";
}
return compare;
};
Longest common prefix in Javascript (All test case accepted. Asked by many company interviews.)
var longestCommonPrefix = function (strs) {
let string = '';
if (strs.length > 1) {
for (let i = 0; i < strs[0].length; i++) {
let str = strs[0].charAt(i);
for (let s = 0; s < strs.length - 1; s++) {
if (!(strs[s + 1].charAt(i) && strs[s].charAt(i) && strs[s + 1].charAt(i) == strs[s].charAt(i))) {
str = '';
}
}
if (!str) {
break;
}
string += str;
}
return string;
} else {
return strs[0];
}
};
longestCommonPrefix(["flower","flow","flight"]);
Code to find longest prefix
var longestCommonPrefix = function(strs) {
let match = false;
let len = strs[0].length ;
let ans = "";
let prev_ans ="";
if(strs.length ==1){
return strs[0];
}
for (let i = 1; i < strs.length; i++){
if( strs[i-1].length > strs[i].length){
len = strs[i].length;
}
}
for (let i = 1; i < strs.length; i++){
for (let j = 0; j < len; j++){
if(strs[i-1].charAt(j) == strs[i].charAt(j)){
ans += strs[i-1].charAt(j);
match = true;
}
else{
break;
}
}
if(prev_ans != "" && prev_ans !=ans){
if(prev_ans.length > ans.length){
return ans;
}else{
return prev_ans;
}
}
prev_ans = ans;
ans = "";
if (match == false){
return "";
}
}
return prev_ans;
};
console.log(longestCommonPrefix(["flow","fly","flu"]));
My solution:
function longestCommonPrefix(...words) {
words.sort(); // shortest string will be first and the longest last
return (
words[0].split('') // converts shortest word to an array of chars
.map((char, idx) => words[words.length - 1][idx] === char ? char : '\0') // replaces non-matching chars with NULL char
.join('') // converts back to a string
.split('\0') // splits the string by NULL characters
.at(0) // returns the first part
);
}
Usage example:
longestCommonPrefix('abca', 'abda', 'abea'); // 'ab'
let testcases = [
["flower", "flow"], //should return "flow"
["flower", "flow", "flight"], //should return "fl"
["flower", "flow", "fight"], //should return "f"
["flower", "flow", "floor"], //should return "flo"
["flower"], //should return "flower"
]
var longestCommonPrefix = function(strs) {
for(var i=0; i<strs.length; i++){
for(var j=0; j<strs[i].length; j++){
if(strs[i][j] + strs[i][j+1] === strs[i+1][j]+strs[i+1][j+1]){
return strs[i][j]+strs[i][j+1];
}
else {
return "";
}
}
}
};
for (let strs of testcases)
console.log(longestCommonPrefix(strs));

I am stuck at a JavaScript function that should count all unique items in an array

I wanted to create a function, that counts all unique Items in an array, but somehow I do not get any output.
This is my array!
let arr = ["hi", "hello", "hi"];
And this is the code I wrote so far:
function countUnique(arr) {
var counts = {};
for (var i = 0; i < arr.length; i++) {
counts[arr[i]] = 1 + (counts[arr[i]] || 0);
}
countUnique(arr);
}
console.log(countUnique(arr));
Your are counting values correctly, however then you are calling this method recursively countUnique(arr); and it results an error of call stack exceeded.
So just remove recursive call of method countUnique(arr); and return counted value counts:
function countUnique(arr) {
var counts = {};
for (var i = 0; i < arr.length; i++) {
counts[arr[i]] = 1 + (counts[arr[i]] || 0);
}
return counts;
}
let arr = ["hi", "hello", "hi"];
console.log(countUnique(arr));
JavaScript engine limits the maximal recursion depth. We can rely on it being 10000, some engines allow more.
You could take a Set and return the size.
const countUnique = array => new Set(array).size;
console.log(countUnique(["hi", "hello", "hi"]));
let arr = ["hi", "hello", "hi"];
function countUnique(arr) {
var counts = {};
for (var i = 0; i < arr.length; i++) {
if(arr[i] in counts) {
counts[arr[i]]++;
} else {
counts[arr[i]] = 1;
}
}
return Object.keys(counts).length;
}
console.log(countUnique(arr));

Common Character Count in Strings JavaScript

Here is the problem:
Given two strings, find the number of common characters between them.
For s1 = "aabcc" and s2 = "adcaa", the output should be 3.
I have written this code :
function commonCharacterCount(s1, s2) {
var count = 0;
var str = "";
for (var i = 0; i < s1.length; i++) {
if (s2.indexOf(s1[i]) > -1 && str.indexOf(s1[i]) == -1) {
count++;
str.concat(s1[i])
}
}
return count;
}
console.log(commonCharacterCount("aabcc", "adcaa"));
It doesn't give the right answer, I wanna know where I am wrong?
There are other more efficient answers, but this answer is easier to understand. This loops through the first string, and checks if the second string contains that value. If it does, count increases and that element from s2 is removed to prevent duplicates.
function commonCharacterCount(s1, s2) {
var count = 0;
s1 = Array.from(s1);
s2 = Array.from(s2);
s1.forEach(e => {
if (s2.includes(e)) {
count++;
s2.splice(s2.indexOf(e), 1);
}
});
return count;
}
console.log(commonCharacterCount("aabcc", "adcaa"));
You can do that in following steps:
Create a function that return an object. With keys as letters and count as values
Get that count object of your both strings in the main function
Iterate through any of the object using for..in
Check other object have the key of first object.
If it have add the least one to count using Math.min()
let s1 = "aabcc"
let s2 = "adcaa"
function countChars(arr){
let obj = {};
arr.forEach(i => obj[i] ? obj[i]++ : obj[i] = 1);
return obj;
}
function common([...s1],[...s2]){
s1 = countChars(s1);
s2 = countChars(s2);
let count = 0;
for(let key in s1){
if(s2[key]) count += Math.min(s1[key],s2[key]);
}
return count
}
console.log(common(s1,s2))
After posting the question, i found that i havent looked the example well. i thought it wants unique common characters ..
and i changed it and now its right
function commonCharacterCount(s1, s2) {
var count = 0;
var str="";
for(var i=0; i<s1.length ; i++){
if(s2.indexOf(s1[i])>-1){
count++;
s2=s2.replace(s1[i],'');
}
}
return count;
}
Create 2 objects containing characters and their count for strings s1
and s2
Count the common keys in 2 objects and return count - Sum the common keys with minimum count in two strings
O(n) - time and O(n) - space complexities
function commonCharacterCount(s1, s2) {
let obj1 = {}
let obj2 = {}
for(let char of s1){
if(!obj1[char]) {
obj1[char] = 1
} else
obj1[char]++
}
for(let char of s2){
if(!obj2[char]) {
obj2[char] = 1
} else
obj2[char]++
}
console.log(obj1,obj2)
let count = 0
for(let key in obj1 ){
if(obj2[key])
count += Math.min(obj1[key],obj2[key])
}
return count
}
I think it would be a easier way to understand. :)
function commonCharacterCount(s1: string, s2: string): number {
let vs1 = [];
let vs2 = [];
let counter = 0;
vs1 = Array.from(s1);
vs2 = Array.from(s2);
vs1.sort();
vs2.sort();
let match_char = [];
for(let i = 0; i < vs1.length; i++){
for(let j = 0; j < vs2.length; j++){
if(vs1[i] == vs2[j]){
match_char.push(vs1[i]);
vs2.splice(j, 1);
break;
}
}
}
return match_char.length;
}
JavaScript ES6 clean solution. Use for...of loop and includes method.
var commonCharacterCount = (s1, s2) => {
const result = [];
const reference = [...s1];
let str = s2;
for (const letter of reference) {
if (str.includes(letter)) {
result.push(letter);
str = str.replace(letter, '');
}
}
// ['a', 'a', 'c'];
return result.length;
};
// Test:
console.log(commonCharacterCount('aabcc', 'adcaa'));
console.log(commonCharacterCount('abcd', 'aad'));
console.log(commonCharacterCount('geeksforgeeks', 'platformforgeeks'));
Cause .concat does not mutate the string called on, but it returns a new one, do:
str = str.concat(s1[i]);
or just
str += s1[i];
You can store the frequencies of each of the characters and go over this map (char->frequency) and find the common ones.
function common(a, b) {
const m1 = {};
const m2 = {};
let count = 0;
for (const c of a) m1[c] = m1[c] ? m1[c]+1 : 1;
for (const c of b) m2[c] = m2[c] ? m2[c]+1 : 1;
for (const c of Object.keys(m1)) if (m2[c]) count += Math.min(m1[c], m2[c]);
return count;
}

JavaScript function with loops not running fast enough

I'm trying to practice for an interview and found a challenge online to write a function that will take an array of numbers and only return values that exist just once in the array, and return those values in order. For example, the array [1, 3, 5, 6, 1, 4, 3, 6] should return [4, 5].
I have a script that is passing the tests but for some of the tests is running too slow. Am I going about this wrong? Is there some fundamental way to speed this up? The script starts with findTheNumbers, and a is the array input:
function findTheNumbers(a) {
var retVal = [];
var nonUnique = [];
for (var i = 0; i < a.length; i++){
var isUnique = true;
if (i != 0){
for (var j = 0; j < nonUnique.length; j++){
if (a[i] == nonUnique[j]){
isUnique = false;
break;
}
}
}
if (isUnique){
for (var k = 0; k < a.length; k++){
if (a[i] == a[k] && i != k){
isUnique = false;
nonUnique.push(a[i]);
break;
}
}
}
if (isUnique){
retVal.push(a[i]);
if (retVal.length == 2){
break;
}
}
}
retVal = sortArrayOfLengthOfTwo(retVal);
return retVal;
}
function sortArrayOfLengthOfTwo(array){
var retVal = [];
if (array[0] > array[1]){
retVal.push(array[1]);
retVal.push(array[0]);
} else {
retVal = array;
}
return retVal;
}
UPDATE -
Not sure where the best place for this is, but here is my new version based on the accepted answer's hints (which worked SO much faster):
function findTheNumbers(a) {
var retVal = [];
var dict = {};
for (var i = 0; i < a.length; i++){
dict[a[i]] = 1 + (dict[a[i]] || 0);
}
for (var key in dict){
if (dict[key] == 1){
retVal.push(parseInt(key));
}
}
return retVal;
}
When a program is too slow, the rule of thumb is to
first blame your algorithm
next blame your implementation (edit: as suggested by Bergi)
finally blame the language
In your case, you parse the whole array n times, that is, the complexity is o(n^2). You can do the same with only one full parse, and a dictionary of the count for each element.
Edit about your new algorithm
Your new algorithm is very good. For the sake of your interview I have some comments:
instead of var, you should consider using const as much as possible, and let otherwise
do not hesitate to put intermediate results within variables
use more descriptive names for your variables
Here's the way I would implement it:
function findTheUniqueNumbers(a) {
const numberCounts = {} // I prefer "new Map()" on recent env.
a.forEach( function(e) {
const previousCount = numberCounts[e] || 0
numberCounts[e] = previousCount + 1
} )
return Object.keys(numberCounts)
.filter( function(e) {
return numberCounts[e] === 1
} )
}
const uniqueNumber = findTheUniqueNumbers([1, 3, 5, 6, 1, 4, 3, 6])
console.log(uniqueNumber)

Checking if a string contains any part of an array element

I just started learning javascript and I'm working on small chrome extension that checks a certain website's item listings for any items
that contain keywords provided by the user. I'm looking for some help on a method to compare a string to an array of strings.
So I have these variables:
var itemDetails = '[alter] saber 1/8 figure unopened';
var trackingList = ['alter figure', 'magic', 'sword art'];
I need to check if itemDetails contains any of the strings in trackingList. I know I can use indexOf() like this:
function checkArray(str, arr){
for(var i=0; i < arr.length; i++){
if(str.indexOf(arr[i]) > -1)
return true;
}
return false;
}
checkArray(itemDetails,trackingList); // returns false
However, for strings with multiple words in them, such as 'alter figure', I want checkArray() to return true as long as both of the words in the string appear anywhere in itemDetails. So checkArray() should return true in the example above since both 'alter' and 'figure' are in itemDetails.
The method I'm currently using is to split each element in trackingList:
function splitTrackList(arr){
var newarr = [];
for(var i = 0; i < arr.length; i++){
newarr[i] = arr[i].split(" ");
}
return newarr;
}
trackList = splitTrackList(trackList);
// [['alter', 'figure'], ['magic'], ['sword', 'art']]
And then compare using indexOf():
function newCheckArray(str, arr){
var passed = true;
for(var i=0; i < arr.length; i++){
for(var j=0; j < arr[i].length; j++){
if(str.indexOf(arr[i][j]) == -1){
passed = false;
break;
}
else passed = true;
}
if(passed) //stop loop if match found
break;
}
return passed;
}
newCheckArray(itemDetails,trackingList); //returns true
My method works so far but I'm guessing there is a much faster/efficient way of doing this. Please let me know what you think. Thank you in advance.
I would do something like
https://jsfiddle.net/denov/FXjXq/3/
var arr = [
"cat dog mouse",
"blue yellow green",
"pizza burrito hamburger"
];
function isInString(needle, haystack) {
return new RegExp('\\b' + needle + '\\b').test(haystack);
}
function checkForStringInArray(stringToFind, arr) {
var inString = false;
for(var i=0; i < arr.length; i++){
inString = isInString(stringToFind, arr[i]);
if(inString) break;
}
return inString;
}
var inString = checkForStringInArray('yellow', arr);
You can use regular expressions. I am giving you an example, although it is not the most efficient
function checkArray(str, arr){
for(var i=0; i < arr.length; i++){
if(str.match((".*" + arr[i].trim() + ".*").replace(" ", ".*")))
return true;
}
return false;
}
Here I alter the string "key1 keyn" to ".*key1.keyn." so it could match those keys everywhere in the string. Have in mind that this will match them if they are part of another word. Good luck.
P.S. Learn regular expressions. They are pretty important in almost every language.
This should work :
function checkArray(str, arr){
for (var j = 0; j < arr.length;j++) {
wordtab = splitTrackList(arr[j]);
for(var i=0; i < tab.length; i++){
if(str.indexOf(wordtab[i]) > -1)
return true;
}
}
return false;
}
Here is a solution with lambda expressions:
var itemDetails = '[alter] saber 1/8 figure unopened';
var trackingList = ['alter saber', 'magic', 'sword art'];
var trackingList2 = ['alter was', 'magic', 'sword art'];
if(trackingList.map(str => str.split(" ")).filter(arrtemp => arrtemp.filter(strin => itemDetails.indexOf(strin) > -1).length == arrtemp.length).length > 0) {
console.debug("true");
} else {
console.debug("false")
}
if(trackingList2.map(str => str.split(" ")).filter(arrtemp => arrtemp.filter(strin => itemDetails.indexOf(strin) > -1).length == arrtemp.length).length > 0) {
console.debug("true");
} else {
console.debug("false")
}
https://jsfiddle.net/wrz1m0b5/
The regex solution only works if words are in correct order.

Categories