Finding first occurrence of each digit in an array - javascript

Taking each four digit number of an array in turn, return the number that you are on when all of the digits 0-9 have been discovered. If not all of the digits can be found, return "Missing digits!"
I've tried to loop through then set a conditional if (i != i+1) push into new array this just gave me the array, it's apparent my logic is wrong. could anyone help me out
For example calling this function with
arr = findAllDigits([5175, 4538, 2926, 5057, 6401, 4376, 2280, 6137, 8798, 9083])
the code should return 5057.
While calling
arr = findAllDigits([4883, 3876, 7769, 9846, 9546, 9634, 9696, 2832, 6822, 6868])
should return "missing numbers"
function findAllDigits(arr) {
newArr = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] != arr[i + 1]) newArr.push(arr[i]);
console.log(newArr);
}
}
Do I need to split because it is taking everything before the comma as
one number, then iterate over?

You can use Set here
Loop over the array and then create a set, You have to return the current number if set size becomes 10 because you need to check 0-9
function findAllDigits(arr) {
const set = new Set();
for (let n of arr) {
String(n)
.split("")
.forEach((c) => set.add(c));
if (set.size === 10) return n;
}
return "Missing digits!";
}
const arr1 = [5175, 4538, 2926, 5057, 6401, 4376, 2280, 6137, 8798, 9083];
const arr2 = [4883, 3876, 7769, 9846, 9546, 9634, 9696, 2832, 6822, 6868];
console.log(findAllDigits(arr1));
console.log(findAllDigits(arr2));

Your for loop is only checking to see if the array entry is equal to the next one. You need to split up the digits inside each entry and store them individually:
function findAllDigits(arr) {
newArr = [];
for (let i = 0; i < arr.length; i++) {
// now iterate the individual digits
const entryAsString = arr[i].toString();
for (let j = 0; j < entryAsString.length; j++) {
// if we haven't seen the digit before, add it to the array
if(!newArr.includes(j) {
newArr.push(j);
}
}
// we know we have all digits when newArr is 10 entries long
if (newArr.length) {
console.log(arr[i]);
// you can also return this value here
}
}
}

One more solution:
const arr1 = [5175, 4538, 2926, 5057, 6401, 4376, 2280, 6137, 8798, 9083];
const arr2 = [4883, 3876, 7769, 9846, 9546, 9634, 9696, 2832, 6822, 6868];
const findAllDigits = (arr) => {
// Declare new Set: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
const digits = new Set();
// return the first item from array that fits the condition,
// find() method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
return arr.find((curr) => (
// String(5175) -> '5175' : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
// [...'5175'] -> ['5','1','7','5'] : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
// .forEach(digits.add, digits) - forEach with callback function and context : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
// comma operator lets get rid of return : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
[...String(curr)].forEach(digits.add, digits),
// condition - is find() method need to return an item
(digits.size === 10)
// if returned value is not undefined or null return finded number oterwise error string
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
)) ?? "Missing digits!";
};
console.log(findAllDigits(arr1)); //5057
console.log(findAllDigits(arr2)); //Missing digits!

Related

How to convert the index of a flatten multidimensional array (any depth) to the original index?

For example, suppose I have a multidimensional array:
const a1=[["a",["a","b"]],"b",[["b"],"c"]];
which the flatten from is
const a2=["a","a","b","b","c","c"];
and accessing a2[2] is equivalent to a1[0][1][1], also access a2[3] is equivalent to a1[1].
However I'm not interested in what the element is in such index, instead I want a function that can convert the index of a2 equivalent to in a1, eg:
magicFunction(2,[["a",["a","b"]],"b",[["b"],"c"]])
returns [0,1,1],
also
magicFunction(3,[["a",["a","b"]],"b",[["b"],"c"]])
returns [1]. And the input multidimensional array can be any depth (more than 3 layers). How do I write such magic function?
Note: I know that may be easy to do if I know the array is 2d and the sub-array have same number of elements:
const a1=[["a","a"],["b","b"],["c","c"]];
const magicFunction=function(target,a){
let count=0;
for(let i=0;i<a.length;i++){
for(let j=0;j<a[i].length;j++){
if(count==target){
return [i,j];
}
count++;
}
}
return [];
};
alert(magicFunction(3,a1));
However now I need the function to accept a with n-dimensional array which d is unknown before testing, which I don't know how many layers of for-loop should I write to handle it, also the length of each sub-array may not be the same.
Here's a recursive function that generates the path by traversing the array and counting values until it reaches the n'th value. The function returns either an array of the path (if we reach the n'th value) or the current count (if we don't).
const magicFunction = (index, array, count=0) => {
for (let idx = 0; idx < array.length; idx++) {
if (Array.isArray(array[idx])) {
let res = magicFunction(index, array[idx], count)
if (Array.isArray(res)) return [idx].concat(res)
else count = res
}
else {
if (count == index) return [idx]
count++
}
}
return count
}
for (i = 0; i < 6; i++) {
res = magicFunction(i, [["a",["b","c"]],"d",[["e"],"f"]])
console.log(`${i} : [${res}]`)
}
You can flatten an array into a list of [element path] pairs and fetch paths from that list:
function* flatWithPaths(a, parent=[]) {
for (let [i, e] of a.entries()) {
if (Array.isArray(e))
yield* flatWithPaths(e, parent.concat(i))
else
yield [e, parent.concat(i)]
}
}
//
const a = [["a",["b","c"]],"d",[["e","f"],[[["g","h"]]]]]
let n = 0
for (let [elem, path] of flatWithPaths(a)) {
console.log(n++, elem, path.join())
}

Finding both characters and putting them in a new array

I've created a function that passes in a string and a character. The string is saturday and the character is a.
I want the result to be an array that contains all the index numbers of where the letter 'a' sits in saturday.
Then array ends up with only [1]. So it finds the first a sitting at the second index. I tested this by changing saturday to soturday and the console prints the array with [6].
I want the result to be [1, 6]
I've tried putting the return result outside the next set of {} braces but no joy.
const subLength = (str, cha) => {
let result = [];
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === cha) {
result.push(str.indexOf(cha));
return result;
}
}
};
console.log(subLength('Saturday', 'a'));
2 small problems with your code
The return statement is in the for loop. The first time the loop hits that your loop will stop and the function will return. This is why you are only getting 1 result. Move the return outside the loop.
Once the above is fixed you will realize that your array will now return [1, 1]. This is because str.indexOf(cha) will always return 1 since it's returning the index of the first a. To fix this, you should be appending the index i to your array instead since it represents the index of the current char.
const subLength = (str, cha) => {
let result = [];
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === cha) {
result.push(i);
}
}
return result;
};
console.log(subLength('Saturday', 'a'));
You are pretty close.
In your code the return is being executed as soon as a match is found. You want to return after you've checked every char - so I've moved it after the foreach.
indexof has a second parm, which defines the char to start looking from. If you omit it, you will get the index of the first match every time - which is not what you want.
const subLength = (str, cha) => {
let result = [];
for(let i = 0; i < str.length; i++){
if(str[i] === cha) {
result.push(str.indexOf(cha, i));
}
}
return result;
};
console.log(subLength('Saturday', 'a'));
Room for improvement
Since you are iterating over every char anyways, you can simply store every i where str[i] matches cha.
So optimized:
const subLength = (str, cha) => {
let result = [];
for(let i = 0; i < str.length; i++){
if(str[i] === cha) {
result.push(i);
}
}
return result;
};
console.log(subLength('Saturday', 'a'));
An even simpler version using regex:
const subLength = (str, cha) => {
return [...str.matchAll(new RegExp(cha, 'g'))].map(e => e.index);
};
console.log(subLength('Saturday', 'a'));
How about putting the return result; outside of the for loop?
Something like this should work, if it is case-sensitive
const subLength = (str, cha) => {
const chaArr = str.split('');
const result = [];
chaArr.forEach((v, i) => {
if (v === cha) result.push(i);
})
return result
};
console.log(subLength('Saturday', 'a'));

String Match challenge in Javascript

if anybody can point or just give a clue what I did wrong, would be very much appreciated. So the task is :
Given 2 strings, a and b, return the number of the positions where
they contain the same length 2 substring. So "xxcaazz" and "xxbaaz"
yields 3, since the "xx" "xx", "aa", and "az" substrings appear in the
same place in both strings.
function('xxcaazz', 'xxbaaz') should return 3
function('abc', 'abc') should return 2
function('abc', 'axc') should return 0
My code:
function stringMatch(a, b){
// convert both strings to arrays with split method
let arrA = a.split("")
let arrB = b.split("")
// create 2 empty arrays to feel in with symbol combinations
let arrOne = [];
let arrTwo = [];
// loop through the first array arrA and push elements to empty arrayOne
for ( let i = 0; i < arrA.length ; i++ ) {
arrOne.push(arrA[i]+arrA[i+1])
}
// loop through the first array arrB and push elements to empty arrayTwo
for ( let i = 0; i < arrB.length ; i++ ) {
arrTwo.push(arrB[i]+arrB[i+1])
}
// create a new array of the matching elements from arrOne and arrTwo
let newArray = arrOne.filter(value => arrTwo.includes(value))
// return the length 0f the newArray - that's supposed to be the answer
return newArray.length
}
Thanks for help!
On the last iteration of your loops, there won't be "next" character, arrB[i+1] will be undefined. The easiest way to solve that is to only loop until the second to last character, or until i < arrB.length - 1.
for ( let i = 0; i < arrB.length - 1; i++ ) {
arrTwo.push(arrB[i]+arrB[i+1])
}
e.g...
console.log(stringMatch('xxcaazz', 'xxbaaz')); //should return 3
console.log(stringMatch('abc', 'abc')); // should return 2
console.log(stringMatch('abc', 'axc')); //should return 0
function stringMatch(a, b){
// convert both strings to arrays with split method
let arrA = a.split("")
let arrB = b.split("")
// create 2 empty arrays to feel in with symbol combinations
let arrOne = [];
let arrTwo = [];
// loop through the first array arrA and push elements to empty arrayOne
for ( let i = 0; i < arrA.length -1 ; i++ ) {
arrOne.push(arrA[i]+arrA[i+1])
}
// loop through the first array arrB and push elements to empty arrayTwo
for ( let i = 0; i < arrB.length - 1; i++ ) {
arrTwo.push(arrB[i]+arrB[i+1])
}
// create a new array of the matching elements from arrOne and arrTwo
let newArray = arrOne.filter(value => arrTwo.includes(value))
// return the length 0f the newArray - that's supposed to be the answer
return newArray.length
}
As a bonus, here's my own solution...
console.log(stringMatch('xxcaazz', 'xxbaaz')); //should return 3
console.log(stringMatch('abc', 'abc')); // should return 2
console.log(stringMatch('abc', 'axc')); //should return 0
function stringMatch(a, b){
var matches = 0;
for(let i=a.length-1; i--;){
let s1 = a.substring(i, i+2);
let s2 = b.substring(i, i+2);
if(s1 == s2) matches++;
}
return matches;
}

Find Missing Character in a Character Array (javascript)

I am trying to write a function that takes in an array of individual characters (eg.['a','b','d']) and returns the first character that is missing (eg. 'c'). I am not sure why my current function doesn't work as described.
const alph = "abcdefghijklmnopqrstuvwxyz";
const findMissingLetter = (arr) => {
arr.forEach((l, i, a) => {
const ltrIdx = alph.indexOf(l); //a's index in the alph is 0
const arrNxtLtr = a[i+1]; //the next ltr after a is c
if(arrNxtLtr !== alph[ltrIdx + 1]) return alph[ltrIdx + 1] //return the letter that is missing from the arr
})
return -1 //return -1 if there is no missing char
}
console.log(findMissingLetter(['a','c']))
ps. I've seen similar approaches to solve this general problem, I am just simply wondering what I've done wrong with my function so I can learn.
Thank you!
If you simply want to find the first mismatch between two strings, then just compare them character by character until you find a mismatch or reach the end of the input string:
const alph = "abcdefghijklmnopqrstuvwxyz";
const findMissingLetter = (arr) => {
for(let i=0; i<arr.length; i++) {
if(arr[i] !== alph[i]) {
return alph[i]; // found the first mismatch
}
}
return -1 // return -1 if there is no missing char
}
console.log(findMissingLetter([]),"-1?");
console.log(findMissingLetter(['a']),"-1?");
console.log(findMissingLetter(['b']),"a?");
console.log(findMissingLetter(['a','b']),"-1?");
console.log(findMissingLetter(['a','c']),"b?");
And avoid forEach() if you want to return from inside a loop as it was commented already.
And if the input string does not have to start at the beginning of the "big" string, locate it's first character and do the comparison from there:
const alph = "abcdefghijklmnopqrstuvwxyz";
const findMissingLetter = (arr) => {
if(arr.length===0)
return -1;
let start = alph.indexOf(arr[0]);
for(let i=0; i<arr.length; i++) {
if(arr[i] !== alph[start+i]) {
return alph[start+i]; // found the first mismatch
}
}
return -1 // return -1 if there is no missing char
}
console.log(findMissingLetter([]),"-1?");
console.log(findMissingLetter(['a']),"-1?");
console.log(findMissingLetter(['b']),"-1?");
console.log(findMissingLetter(['a','b']),"-1?");
console.log(findMissingLetter(['a','c']),"b?");
console.log(findMissingLetter(['b','c','e','f']),"d?");
The reason is that forEach ignores return or any shortcircuit statement. You must instead of trying return the value from the foreach, just save it into another variable and return that variable after the forEach is done.
const alph = "abcdefghijklmnopqrstuvwxyz";
const findMissingLetter = (arr) => {
let missingLetter
arr.forEach((letter, index) => {
if(letter !== alph[index]) missingLetter ??= alph[index]
else missingLetter = -1
})
return missingLetter
}
console.log(findMissingLetter(['a','c']))

Why can I not concat an array to the result of a filter?

I'm a bit curious why the behaviors are different between the two solutions posted below. In the Failing solution, I've concated the zeros array to what I assume by the time of execution, would be the result array of the filter operation. I'm curious why the result is not the updated concated variant (an array with 0s at the end) and instead if simply the initial output of the filter operation.
Passing:
const moveZeros = function (arr) {
let zeros = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zeros.push(0);
}
let filteredArray = arr.filter( element => element !== 0).concat(zeros)
return filteredArray;
//returns [1,2,3,0,0,0]
}
Failing:
const moveZeros = function (arr) {
let zeros = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zeros.push(0);
}
let filteredArray = arr.filter( element => element !== 0);
// shouldnt the line below concat zeros to the filter result?
filteredArray.concat(zeros);
return filteredArray;
//returns [1,2,3]
}
This also passes:
return filteredArray.concat(zeros)
concat() doesn't modify the array in place, it returns a new array. Your failing version doesn't return the new array, it returns the original array.
Array.concat() return the concatenated array instead of the original.
let arr = [1,0,0,2,3,0];
let zeros = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) zeros.push(0);
}
let filteredArray = arr.filter( element => element !== 0);
console.log(filteredArray);
let concatenatedArray = filteredArray.concat(zeros);
console.log(concatenatedArray);
So you need to reassign filteredArray like:
filteredArray = filteredArray.concat(zeros);
From the MDN Web Docs
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
In your passing solution you are assigning the result of a .concat() to a variable, and then returning that variable, whereas in your failing solution, you are returning the original array, because you did not assign the result of filteredArray.concat(zeros) to anything

Categories

Resources