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 :)
Trying a fun problem of replacing vowels in a string with the next vowel in line aka a->e, e->i, i->o, o->u, not accounting for "u". Starting with an array instead of a string. My second loop (to iterate over vowel array elements) is ignoring my "j
var vowelChange = function(vowelArray, stringToChange) {
for (var i = 0; i<stringToChange.length; i++) {
for (var j = 0; j<vowelArray.length; j++) {
if (stringToChange[i]===vowelArray[j]) {
var newCharacter = vowelArray[j+1]
stringToChange[i] = newCharacter
i++
}
}
}
return stringToChange
};
I'm using node-debug to set breakpoints in a browser, and j is looping to 5 before starting over at 0. I get the correct output, but j should stop at 4...
EDIT
Can somebody explain how I'm using join incorrectly, because I can't get my function to output a string instead of just an array.
var vowelChange = function(vowelArray, stringToChange) {
for (var i = 0; i<stringToChange.length; i++) {
for (var j = 0; j<vowelArray.length-1; j++) {
if (stringToChange[i]===vowelArray[j]) {
stringToChange[i] = vowelArray[j+1]
break
}
}
}
stringToChange = stringToChange.join('')
return stringToChange
};
var vowels = ['a','e','i','o','u']
var firstName = ['t', 'e', 's', 't']
vowelChange(vowels, firstName)
console.log(firstName)
Assuming vowelArray is 0-indexed...
var vowelChange = function(vowelArray, stringToChange) {
for (var i = 0; i<stringToChange.length; i++) {
for (var j = 0; j<vowelArray.length - 1; j++) {
if (stringToChange[i]===vowelArray[j]) {
stringToChange[i] = vowelArray[j+1];
break;
}
}
}
return stringToChange
};
In JavaScript, strings are immutable objects, which means that the
characters within them may not be changed and that any operations on
strings actually create new strings.
So,if you try to change any index of the string, the original string won't change
node
> str = "hello this is dummy string";
'hello this is dummy string'
> str[0] = "w";
'w'
> str
'hello this is dummy string'
So, stringToChange[i] = vowelArray[j+1]; won't work
Could split the string and then join
var vowelChange = function(vowelArray, stringToChange) {
stringToChange = stringToChange.split('');
for(var i=0; i<stringToChange.length;i++){
for(var j=0;j<vowelArray.length-1;j++){
if(stringToChange[i] == vowelArray[j]){
stringToChange[i] = vowelArray[j+1];
break;
}
}
}
stringToChange = stringToChange.join('');
return stringToChange;
};
Example
This is my code so far; All I've done is loop through each letter.
I'm stuck on how to test whether or not the strings have the same characters.
function mutation(arr) {
for (var i=0;i<arr.length;i++){
for(var j =0;j<arr[i].length;j++){
}
}
}
mutation(['hello', 'hey']);
The characters don't have to be at the same index, so for example, ['Alien', 'line'], should return true.
I used the answer above but modified it to remove the case, otherwise i was getting a false on Hello and hello
function mutation(arr) {
//first split the arr into the two inputs and convert all to lower case
var firstArray = arr[0].toLowerCase().split("");
var secondArray = arr[1].toLowerCase().split("");
//now using the code provided by the above comment which is really clean
var count = 0;
for (var i =0; i < secondArray.length; i++) {
if(firstArray.indexOf(secondArray[i]) > -1 ) {
count++;
}
}
if (count == secondArray.length) {
return true
}
//changed the code provided above to handle the true/false criteria of the excercise
else {return false; }
}
mutation(['hello', 'hey']);
You only have to loop once, and compare the second array against the first.
function mutation(arr) {
var arr1 = arr[0].split('');
var arr2 = arr[1].split('');
var count = 0;
for (var i =0; i < arr2.length; i++) {
if(arr1.indexOf(arr2[i]) > -1 ) {
count++;
}
}
if (count == arr2.length) {
console.log('all in');
}
}
mutation(['alien', 'line']);
Or you could use filter:
function mutation(arr) {
var arr1 = arr[0].split('');
var arr2 = arr[1].split('');
if (arr2.filter(function(element, index) { return arr1.indexOf(element); }).length === arr2.length) {
console.log('all in');
}
}
mutation(['alien', 'line']);
The counter in the accepted answer is unnecessary.
function mutation(arr) {
var arr1 = arr[0].toLowerCase().split('');
var arr2 = arr[1].toLowerCase().split('');
for (var i=0; i < arr2.length; i++) {
if(arr1.indexOf(arr2[i]) == -1 ) {
return false;
}
}
return true;
}
mutation(["hello", "hey"]);
Clean, modern and easy to read:
function mutation(arr) {
const firstEl = arr[0].toLocaleLowerCase();
const secondEl = arr[1].toLocaleLowerCase().split('');
return secondEl.every(el => firstEl.includes(el));
}
console.log('mutation(["hello", "hey"]): ', mutation(["hello", "hey"]));
console.log('mutation(["Alien", "line"]): ', mutation(["Alien", "line"]));
How about something cleaner? Just a little modification to an above code.
function mutation(arr) {
var first = arr[0].toLowerCase().split('');
var second = arr[1].toLowerCase().split('');
var count = 0;
// Check every character and if the index is found add one
for (var s in second){
if (first.indexOf(second[s]) > -1) {
count+= 0;
} else
count++;
}
if (count === 0)
return true;
else
return false;
}
even lesser code for this problem
function mutation(arr) {
var arr1 = arr[0].toLowerCase().split('');
var arr2 = arr[1].toLowerCase().split('');
for(var i of arr2)
if(arr1.indexOf(i)===-1)
return false;
return true;
}
mutation(["hello", "hey"]);
the code above will not work in all cases
because if the first word in array is shorter than second one you need second for loop
for (var i=0; i<arr1.length; i++){
if (arr2.indexOf(arr1[i])==-1){
return false;
you need 2 for loops
function mutation(arr) {
var arr1=arr[0].toLowerCase().split("");
var arr2=arr[1].toLowerCase().split("");
if (arr1.length<arr2.length ){
for (var i=0; i<arr1.length; i++){
if (arr2.indexOf(arr1[i])==-1){
return false;
}
}
}
else if (arr1.length>=arr2.length ){
for (var j=0; j<arr2.length; j++){
if (arr1.indexOf(arr2[j])==-1){
return false;
}
}
}
return true;
}
//mutation(["zyxwvutsrqponmlkjihgfedcba", "qrstu"]);
mutation([ "qrstu", "zyxwvutsrqponmlkjihgfedcba"]);
enter code here
This question already has answers here:
In Javascript, how do I check if an array has duplicate values?
(9 answers)
Closed 10 months ago.
I wanted to write a javascript function which checks if array contains duplicate values or not.
I have written the following code but its giving answer as "true" always.
Can anybody please tell me what am I missing.
function checkIfArrayIsUnique(myArray)
{
for (var i = 0; i < myArray.length; i++)
{
for (var j = 0; j < myArray.length; j++)
{
if (i != j)
{
if (myArray[i] == myArray[j])
{
return true; // means there are duplicate values
}
}
}
}
return false; // means there are no duplicate values.
}
An easy solution, if you've got ES6, uses Set:
function checkIfArrayIsUnique(myArray) {
return myArray.length === new Set(myArray).size;
}
let uniqueArray = [1, 2, 3, 4, 5];
console.log(`${uniqueArray} is unique : ${checkIfArrayIsUnique(uniqueArray)}`);
let nonUniqueArray = [1, 1, 2, 3, 4, 5];
console.log(`${nonUniqueArray} is unique : ${checkIfArrayIsUnique(nonUniqueArray)}`);
let arr = [11,22,11,22];
let hasDuplicate = arr.some((val, i) => arr.indexOf(val) !== i);
// hasDuplicate = true
True -> array has duplicates
False -> uniqe array
This should work with only one loop:
function checkIfArrayIsUnique(arr) {
var map = {}, i, size;
for (i = 0, size = arr.length; i < size; i++){
if (map[arr[i]]){
return false;
}
map[arr[i]] = true;
}
return true;
}
You got the return values the wrong way round:
As soon as you find two values that are equal, you can conclude that the array is not unique and return false.
At the very end, after you've checked all the pairs, you can return true.
If you do this a lot, and the arrays are large, you might want to investigate the possibility of sorting the array and then only comparing adjacent elements. This will have better asymptotic complexity than your current method.
Assuming you're targeting browsers that aren't IE8,
this would work as well:
function checkIfArrayIsUnique(myArray)
{
for (var i = 0; i < myArray.length; i++)
{
if (myArray.indexOf(myArray[i]) !== myArray.lastIndexOf(myArray[i])) {
return false;
}
}
return true; // this means not unique
}
Here's an O(n) solution:
function hasDupes(arr) {
/* temporary object */
var uniqOb = {};
/* create object attribute with name=value in array, this will not keep dupes*/
for (var i in arr)
uniqOb[arr[i]] = "";
/* if object's attributes match array, then no dupes! */
if (arr.length == Object.keys(uniqOb).length)
alert('NO dupes');
else
alert('HAS dupes');
}
var arr = ["1/1/2016", "1/1/2016", "2/1/2016"];
hasDupes(arr);
https://jsfiddle.net/7kkgy1j3/
Another solution:
Array.prototype.checkIfArrayIsUnique = function() {
this.sort();
for ( var i = 1; i < this.length; i++ ){
if(this[i-1] == this[i])
return false;
}
return true;
}
function hasNoDuplicates(arr) {
return arr.every(num => arr.indexOf(num) === arr.lastIndexOf(num));
}
hasNoDuplicates accepts an array and returns true if there are no duplicate values. If there are any duplicates, the function returns false.
Without a for loop, only using Map().
You can also return the duplicates.
(function(a){
let map = new Map();
a.forEach(e => {
if(map.has(e)) {
let count = map.get(e);
console.log(count)
map.set(e, count + 1);
} else {
map.set(e, 1);
}
});
let hasDup = false;
let dups = [];
map.forEach((value, key) => {
if(value > 1) {
hasDup = true;
dups.push(key);
}
});
console.log(dups);
return hasDup;
})([2,4,6,2,1,4]);
Late answer but can be helpful
function areThereDuplicates(args) {
let count = {};
for(let i = 0; i < args.length; i++){
count[args[i]] = 1 + (count[args[i]] || 0);
}
let found = Object.keys(count).filter(function(key) {
return count[key] > 1;
});
return found.length ? true : false;
}
areThereDuplicates([1,2,5]);
The code given in the question can be better written as follows
function checkIfArrayIsUnique(myArray)
{
for (var i = 0; i < myArray.length; i++)
{
for (var j = i+1; j < myArray.length; j++)
{
if (myArray[i] == myArray[j])
{
return true; // means there are duplicate values
}
}
}
return false; // means there are no duplicate values.
}
Returns the duplicate item in array and creates a new array with no duplicates:
var a = ["hello", "hi", "hi", "juice", "juice", "test"];
var b = ["ding", "dong", "hi", "juice", "juice", "test"];
var c = a.concat(b);
var dupClearArr = [];
function dupArray(arr) {
for (i = 0; i < arr.length; i++) {
if (arr.indexOf(arr[i]) != i && arr.indexOf(arr[i]) != -1) {
console.log('duplicate item ' + arr[i]);
} else {
dupClearArr.push(arr[i])
}
}
console.log('actual array \n' + arr + ' \nno duplicate items array \n' + dupClearArr)
}
dupArray(c);
const containsMatches = (a1, a2) => a1.some((v) => a2.includes(v));
If your array nests other arrays/objects, using the Set approach may not be what you want since comparing two objects compares their references. If you want to check that their contained values are equal, something else is needed. Here are a couple different approaches.
Approach 1: Map using JSON.stringify for keys
If you want to consider objects with the same contained values as equal, here's one simple way to do it using a Map object. It uses JSON.stringify to make a unique id for each element in the array.
I believe the runtime of this would be O(n * m) on arrays, assuming JSON.stringify serializes in linear time. n is the length of the outer array, m is size of the arrays. If the objects get very large, however, this may slow down since the keys will be very long. Not a very space-efficient implementation, but it is simple and works for many data types.
function checkArrayDupeFree(myArray, idFunc) {
const dupeMap = new Map();
for (const el of myArray) {
const id = idFunc(el);
if (dupeMap.has(id))
return false;
dupeMap.set(id, el);
}
return true;
}
const notUnique = [ [1, 2], [1, 3], [1, 2] ];
console.log(`${JSON.stringify(notUnique)} has no duplicates? ${checkArrayDupeFree(notUnique, JSON.stringify)}`);
const unique = [ [2, 1], [1, 3], [1, 2] ];
console.log(`${JSON.stringify(unique)} has no duplicates? ${checkArrayDupeFree(unique, JSON.stringify)}`);
Of course, you could also write your own id-generator function, though I'm not sure you can do much better than JSON.stringify.
Approach 2: Custom HashMap, Hashcode, and Equality implementations
If you have a lot of big arrays, it may be better performance-wise to implement your own hash/equality functions and use a Map as a HashMap.
In the following implementation, we hash the array. If there is a collision, map a key to an array of collided values, and check to see if any of the array values match according to the equality function.
The downside of this approach is that you may have to consider a wide range of types for which to make hashcode/equality functions, depending on what's in the array.
function checkArrayDupeFreeWHashes(myArray, hashFunc, eqFunc) {
const hashMap = new Map();
for (const el of myArray) {
const hash = hashFunc(el);
const hit = hashMap.get(hash);
if (hit == null)
hashMap.set(hash, [el]);
else if (hit.some(v => eqFunc(v, el)))
return false;
else
hit.push(el);
}
return true;
}
Here's a demo of the custom HashMap in action. I implemented a hashing function and an equality function for arrays of arrays.
function checkArrayDupeFreeWHashes(myArray, hashFunc, eqFunc) {
const hashMap = new Map();
for (const el of myArray) {
const hash = hashFunc(el);
const hit = hashMap.get(hash);
if (hit == null)
hashMap.set(hash, [el]);
else if (hit.some(v => eqFunc(v, el)))
return false;
else
hit.push(el);
}
return true;
}
function arrayHasher(arr) {
let hash = 19;
for (let i = 0; i < arr.length; i++) {
const el = arr[i];
const toHash = Array.isArray(el)
? arrayHasher(el)
: el * 23;
hash = hash * 31 + toHash;
}
return hash;
}
function arrayEq(a, b) {
if (a.length != b.length)
return false;
for (let i = 0; i < a.length; i++) {
if ((Array.isArray(a) || Array.isArray(b)) && !arrayEq(a[i], b[i]))
return false;
else if (a[i] !== b[i])
return false;
}
return true;
}
const notUnique = [ [1, 2], [1, 3], [1, 2] ];
const unique = [ [2, 1], [1, 3], [1, 2] ];
console.log(`${JSON.stringify(notUnique)} has no duplicates? ${checkArrayDupeFreeWHashes(notUnique, arrayHasher, arrayEq)}`);
console.log(`${JSON.stringify(unique)} has no duplicates? ${checkArrayDupeFreeWHashes(unique, arrayHasher, arrayEq)}`);
function checkIfArrayIsUnique(myArray)
{
isUnique=true
for (var i = 0; i < myArray.length; i++)
{
for (var j = 0; j < myArray.length; j++)
{
if (i != j)
{
if (myArray[i] == myArray[j])
{
isUnique=false
}
}
}
}
return isUnique;
}
This assume that the array is unique at the start.
If find two equals values, then change to false
i think this is the simple way
$(document).ready(function() {
var arr = [1,2,3,9,6,5,6];
console.log( "result =>"+ if_duplicate_value (arr));
});
function if_duplicate_value (arr){
for(i=0;i<arr.length-1;i++){
for(j=i+1;j<arr.length;j++){
if(arr[i]==arr[j]){
return true;
}
}
}
return false;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
var c=[2,2,3,3,5,5,4,4,8,8];
for(var i=0; i<b.length; i++){
for(var j=i+1; j<b.length; j++){
if(c[i]==c[j]){
console.log(c[j]);
}
}
}