I have an array with numbers in the range of 0 - 100. I need to find all the same numbers and add 1 to them.
my code worked well with arrays like [100, 2, 1, 1, 0]
const findAndChangeDuplicates = (arr: any) => {
for (let i = arr.length - 1; i >= 0; i--) {
if (arr[i + 1] === arr[i] && arr[i] <= 5) {
arr[i] += 1;
} else if (arr[i - 1] === arr[i] && arr[i] >= 5) {
arr[i] -= 1;
findAndChangeDuplicates(arr);
}
}
return arr;
};
but when I came across this
[100, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 3, 3, 2, 2, 2, 2, 1, 1, 0, 0]
my code let me down.
Expected Result:
[100, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Have any ideas?
An approach by using at least one loop from the end to adjust the values and if necessary another loop from the beginning to set the largest value to 100.
Both loops feature a value variable v. In the first loop, it starts with the last value of the array and increments its value and check is the item is smaller than this value.
If smaller, then the value is assigned, otherwise the actual value is taken for the next item.
if necessary, the other loop works in opposite direction and with a start value of 100 and checks if the item is greater than wanted and takes the smaller value, or the value is taken from the item.
The result is an array which has a gereatest value of 100 at start and goes until zero or greater to the end of the array.
function update(array) {
var i = array.length,
v = array[--i];
while (i--) if (array[i] < ++v) array[i] = v; else v = array[i];
if (array[0] > 100) {
v = 100;
for (i = 0; i < array.length; i++) {
if (array[i] > v) array[i] = v; else v = array[i];
v--;
}
}
return array;
}
console.log(update([100, 2, 1, 1, 0]));
console.log(update( [100, 100, 99, 86, 6, 5, 5, 5, 5, 5, 4, 4, 4, 3, 3, 2, 2, 2, 2, 1, 1, 0, 0]))
.as-console-wrapper { max-height: 100% !important; top: 0; }
The following assumes you want them ordered from highest to lowest, if not this might ba as well as useless to you.
The idea is to first create an Object to keep track of how many of each number exist. We then map each value by first checking whether it's unique and if not increasing it until we can't find any value inside the Object anymore. This will not neatly order the numbers by itself so we will have to sort afterwards.
let arr1 = [100, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 3, 3, 2, 2, 2, 2, 1, 1, 0, 0],
arr2 = [100, 2, 1, 1, 0];
const f = (arr) => arr.reduce((a,c) => (a[c] = (a[c] || 0) + 1, a),{}),
g = (arr, obj) => arr.map(v => {
if (obj[v] > 1) {
let i = 1;
obj[v] = obj[v] - 1;
while (obj[v + i]) {
i++;
}
obj[v + i] = (obj[v + i] || 0) + 1;
return v + i;
} else {
return v;
}
}).sort((a,b) => +b - +a);
console.log(g(arr1, f(arr1)))
console.log(g(arr2, f(arr2)))
Here is a verbose solution that will work with unordered arrays as well.
It's not efficient, neither brilliant, but it takes care of unordered arrays as well.
Basically, it takes advantage of reduce to collect all the occurrences of each element. Each time it finds more than one, it increases all the occurrences by 1 except the last one.
Next, it checks whether there still are duplicates. If there are, it repeats the process until none is found. Of course, it's not the cleverest approach, but it works.
// Increases all duplicates until there are no more duplicates.
const increaseDuplicates = (arr, index) => {
// Repeat the code until no duplicate is found
while (!noDuplicates(arr)) {
// Acquire all the occurrences of each item, keeping track of the index.
Object.entries(arr.reduce((acc, next, i) => {
acc[next] = acc[next] || [];
return acc[next].push(i), acc;
}, {})).forEach(([n, indexes]) => {
// for each value found, check whether it appears at least twice.
if (indexes.length > 1) {
// if it does, increase the value of every item but the last one.
for (var i = 0; i < indexes.length - 1; i++) {
arr[indexes[i]]++;
}
}
});
}
return arr;
};
// Asserts an array has no duplicates.
const noDuplicates = (arr) => [...new Set(arr)].length === arr.length;
const input = [100, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 3, 3, 2, 2, 2, 2, 1, 1, 0, 0];
console.log(increaseDuplicates(input));
const unorderedInput = [6,4,5,6,6,6,6,5,6,3,1,2,3,99,403,100, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 3, 3, 2, 2, 2, 2, 1, 1, 0, 0];
console.log(increaseDuplicates(unorderedInput));
You can use a forEach on your array to do this, using the 3rd parameter of the callback, the array itself, and a bit of recursivity
const increment_to_unicity = (value, index, self) => {
if (self.indexOf(value) !== index) {
self[index]++
increment_to_unicity(self[index], index, self)
}
return self[index];
}
arr = arr.map(increment_to_unicity).sort((a, b) => b - a);
Related
How do I find the smallest missing positive integer from an array of integers in js? I didn't find an answer to my question, all I found was in other languages.
Here's an example array:
[-2, 6, 4, 5, 7, -1, 1, 3, 6, -2, 9, 10, 2, 2]
The result should be 8.
You could take an object of seen values and a min variable for keeping track of the next minimum value.
const
data = [-2, 6, 4, 5, 7, -1, 1, 3, 6, -2, 9, 10, 2, 2],
ref = {};
let min = 1;
for (const value of data) {
if (value < min) continue;
ref[value] = true;
while (ref[min]) min++;
}
console.log(min);
You could create an array of positive integer (in this example integers has values from 0 to 10), then use Math.min on integers array filtered with initial array (that was filtered taking only positive numbers):
let integers = Array.from(Array(11).keys());
let arr = [-2, 6, 4, 5, 7, -1, 1, 3, 6, -2, 9, 10, 2, 2];
console.log(Math.min(...integers.filter(x => x > 0 && !arr.filter(x => x > 0).includes(x))));
You can do like below to avoid multiple loops.
Simplest solution is when numbers from 1-10, sum of all number will 55 using this formula (n * (n + 1)) / 2;.
the missing number will be 55-(sum of remaining numbers).
const list = [-2, 6, 4, 5, 7, -1, 1, 3, 6, -2, 9, 10, 2, 2];
const missing = (list) => {
let sum = 0;
let max = 0;
let ref = {};
for (let i = 0; i < list.length; i++) {
const ele = list[i];
if (ele > 0 && !ref[ele]) {
ref[ele] = true;
max = max < ele ? ele : max;
sum += ele;
}
}
const total = (max * (max + 1)) / 2;
return total - sum; // will work if only one missing number
// if multiple missing numbers and find smallest one
// let result = 0;
// for (let i = 1; i <= total - sum; i++) {
// if (!ref[i]) {
// result = i;
// break;
// }
// }
// return result;
};
console.log(missing(list));
I create function for finding the smallest positive.
arr = [-2, 6, 4, 5, 7, -1, 1, 3, 6, -2, 9, 10, 2, 2]
function getSmallestPos(arr) {
pi = [...new Set(
arr.filter(n => n > 0)
.sort((a, b) => a - b ))
];
for (i = 0; i < pi.length; i++) {
if ( pi[i] != (i+1)) {
return (i+1);
}
}
}
console.log(getSmallestPos(arr));
Your questions title contradicts the body of your answer.
To get the smallest positive integer you might try this:
const array = [-2, 6, 4, 5, 7, -1, 1, 3, 6, -2, 9, 10, 2, 2];
// filter array to get just positive values and return the minimum value
const min = Math.min(...array.filter(a => Math.sign(a) !== -1));
console.log(min);
For getting the missing value check this out:
const array = [-2, 6, 4, 5, 7, -1, 1, 3, 6, -2, 9, 10, 2, 2];
const getMissingPositiveInt = (array) => {
// filter array to get just positive values and sort from min to max (0, 1, 4, 5 ...)
const min = array.filter(a => Math.sign(a) !== -1).sort((a,b) => a-b);
for (let i=min[0]; i<array.length; i++) // loop from min over whole array
if (!min.includes(i)) // if array doesnt include index ...
return i; // ... you got your missing value and can return it
}
console.log(getMissingPositiveInt(array));
I would like to write a function that checks whether or not an array would be incremental if we were to remove one element from the array. So for example an array like
[1,3,4,2,8]
Would return true because if we remove 2, the array would be incremental ie [1,3,4,8]
To solve this I created a function that iterates through the array, checks whether n is less than n+1 creating an array of true and false values depending on whether the element we are iterating through meets the condition stated.
In cases where n+1 is less than or equal to n; I then have another condition that checks whether n+1 is less than n-1, returning 'very false' in this instance.
I then want to return false if my new Array indicates that removing one element will not leave me with an array of incremental values. I check for by checking whether;
i.) my new array contains more than one false value
ii.) my new array contains the value 'very false' only in instances if very false is not the last element in my array
iii.) my array contains both very false and false
Struggling to get this logic to work. I added a few test cases I am struggling to get right
function solution(sequence) {
let newArr = [];
for(let i = 0; i < sequence.length - 1; i++) {
sequence[i] < sequence[i+1] ? newArr.push(true)
: sequence[i+1] <= sequence[i-1] ? newArr.push('very false')
:newArr.push(false)
}
return (newArr.filter(i => i === false).length) > 1 || ( (newArr.includes('very false')) && (newArr[newArr.length - 1] !== 'very false')) || ( newArr.includes(false) && newArr.includes('very false')) ? false : true;
}
//TEST CASES
let one = [1, 2, 3, 4, 5, 3, 5, 6] //I get this right
let two = [40, 50, 60, 10, 20, 30] //I get this right
let three = [1, 2, 3, 4, 3, 6] //I get this wrong
let four = [1, 4, 10, 4, 2] // I get this wrong
//EXPECTED OUTCOMES
console.log(solution(one)) => false;
console.log(solution(two)) => false;
console.log(solution(three)) => true;
console.log(solution(four)) => false;
FYI: This is not for an assignment or test
The next provided solution uses an every based approach where one would count the amount of violations of the OP's criteria / requirements for a possibly incremental array. Depending on whether a violation has taken place one would compare the current values against the most recent value which caused the violation or one would just simply compare two consecutive array items.
As soon as the maximum allowed violation count gets exceeded, every immediately exits / breaks its iteration with a false boolean return value which also becomes the return value of the below implemented canBeMadeIncremental function; otherwise the return value of both processes is true.
function canBeMadeIncremental(arr) {
let violationCount = 0;
let comparisonValue = null;
return arr.every((value, idx, arr) => {
let isAchievable = true;
if (idx >= 1) {
comparisonValue = comparisonValue ?? arr[idx - 1];
if (comparisonValue >= value) {
++violationCount;
} else {
comparisonValue = null;
}
isAchievable = (violationCount <= 1);
}
return isAchievable;
});
}
console.log(
"canBeMadeIncremental([1, 2, 3, 4, 5, 3, 5, 6]) ?..",
canBeMadeIncremental([1, 2, 3, 4, 5, 3, 5, 6]) // false
);
console.log(
"canBeMadeIncremental([40, 50, 60, 10, 20, 30]) ?..",
canBeMadeIncremental([40, 50, 60, 10, 20, 30]) // false
);
console.log(
"canBeMadeIncremental([1, 2, 3, 4, 3, 6]) ?..",
canBeMadeIncremental([1, 2, 3, 4, 3, 6]) // true
);
console.log(
"canBeMadeIncremental([1, 4, 10, 4, 2]) ?..",
canBeMadeIncremental([1, 4, 10, 4, 2]) // false
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
The above code example could be easily refactored into a more generic function which provides data about whether an incremental array is possible and if yes, how to achieve it (e.g. which index / indices need/s to be removed) ...
function howToMakeIncremental(arr) {
let isAchievable = true;
let comparisonValue = null;
let violationCount = 0;
const violatingIndexList = [];
arr.every((value, idx, arr) => {
if (idx >= 1) {
comparisonValue = comparisonValue ?? arr[idx - 1];
if (comparisonValue >= value) {
violatingIndexList.push(idx);
++violationCount;
} else {
comparisonValue = null;
}
isAchievable = (violationCount <= 1);
}
return isAchievable;
});
const howTo = { isAchievable };
if (isAchievable) {
howTo.violatingIndexList = violatingIndexList;
}
return howTo;
}
console.log(
"howToMakeIncremental([1, 2, 3, 4, 5, 3, 5, 6]) ?..",
howToMakeIncremental([1, 2, 3, 4, 5, 3, 5, 6]) // { isAchievable: false }
);
console.log(
"howToMakeIncremental([40, 50, 60, 10, 20, 30]) ?..",
howToMakeIncremental([40, 50, 60, 10, 20, 30]) // { isAchievable: false }
);
console.log(
"howToMakeIncremental([1, 2, 3, 4, 3, 6]) ?..",
howToMakeIncremental([1, 2, 3, 4, 3, 6]) // { isAchievable: true, violatingIndexList: [4] }
);
console.log(
"howToMakeIncremental([1, 4, 10, 4, 2]) ?..",
howToMakeIncremental([1, 4, 10, 4, 2]) // { isAchievable: false }
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
Creating a new array seems overkill. I would first define a function that verifies at which index a given array violates being incremental.
Then check if there is a second violation after that. If so, return false.
With just one violation, return true when it occurs between the first two or last two values in the array.
In all other cases, verify that removing either of the two involved values resolves the violation.
Code:
// Return the index where the value is not greater than its predecessor
function violation(a, start=0) {
for (let i = start + 1; i < a.length; i++) {
if (a[i - 1] >= a[i]) return i;
}
return 0;
}
function solution(a) {
let i = violation(a);
return !i || !violation(a, i) && (i==1 || i==a.length-1
|| a[i-2] < a[i] || a[i-1] < a[i+1]);
}
//TEST CASES
let tests = [
[1, 2, 3, 4, 5, 3, 5, 6],
[40, 50, 60, 10, 20, 30],
[1, 2, 3, 4, 3, 6],
[1, 4, 10, 4, 2]
];
for (let test of tests) {
console.log(solution(test));
}
There is nothing with your logic just remove the first return
function solution(sequence) {
let newArr = [];
for(let i = 0; i < sequence.length - 1; i++) {
sequence[i] < sequence[i+1] ? newArr.push(true)
: sequence[i+1] < sequence[i-1] ? newArr.push('very false')
:newArr.push(false)
}
// return newArr
return (newArr.filter(i => i === false).length) > 1 || ( (newArr.includes('very
false')) && (newArr[newArr.length - 1] !== 'very false')) || (
newArr.includes(false) && newArr.includes('very false')) ? false : true;
}
//TEST CASES
let one = [1, 2, 3, 4, 5, 3, 5, 6]
let two = [40, 50, 60, 10, 20, 30]
let three = [1, 2, 3, 4, 3, 6]
let four = [1, 4, 10, 4, 2]
console.log(solution(one));
console.log(solution(two));
console.log(solution(three));
console.log(solution(four));
I'm solving the following kata:
Given an input of an array of digits, return the array with each digit incremented by its position in the array: the first digit will be incremented by 1, the second digit by 2, etc. Make sure to start counting your positions from 1 (and not 0).
Your result can only contain single digit numbers, so if adding a digit with it's position gives you a multiple-digit number, only the last digit of the number should be returned.
Notes:
return an empty array if your array is empty
arrays will only contain numbers so don't worry about checking that
Examples
[1, 2, 3] --> [2, 4, 6] # [1+1, 2+2, 3+3]
[4, 6, 9, 1, 3] --> [5, 8, 2, 5, 8] # [4+1, 6+2, 9+3, 1+4, 3+5]
# 9+3 = 12 --> 2
My code:
const incrementer = (arr) => {
if (arr === []) {
return []
}
let newArr = []
for (let i = 0; i <= arr.length; i++) {
let result = arr[i] + (i + 1)
newArr.push(result)
if (newArr[i] > 9 ) {
let singleDigit = Number(newArr[i].toString().split('')[1])
newArr.push(singleDigit)
}
}
const filteredArr = newArr.filter(el => el >= 0 && el <= 9)
return filteredArr
}
I can't seem to pass the latest test case, which is the following:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 8]), [2, 4, 6, 8, 0, 2, 4, 6, 8, 9, 0, 1, 2, 2]
I keep getting back the whole correct array up until the second 0, after which the other numbers, 1,2,2 are missing from the solution. What am I doing wrong?
The problem in your code is that the filter only runs at the end, and so when you have done a double push in one iteration (once with the value that has more than one digit, and once with just the last digit), the next iteration will no longer have a correct index for the next value that is being pushed: newArr[i] will not be that value.
It is better to correct the value to one digit before pushing it to your new array.
Moreover, you can make better use of the power of JavaScript:
It has a nice map method for arrays, which is ideal for this purpose
Use modulo arithmetic to get the last digit without having to create a string first
Here is the proposed function:
const incrementer = (arr) => arr.map((val, i) => (val + i + 1) % 10);
console.log(incrementer([1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 8]));
... so if adding a digit with it's position gives you a multiple-digit number, only the last digit of the number should be returned.
So if the number is 12, it expects only 2 to be added to the array.
So your code should be:
if (newArr[i] > 9)
{
newArr[i] = newArr[i] % 10; // remainder of newArr[i] / 10
}
const incrementer = (arr) => {
if (arr.length === 0) { // CHANGE HERE
return [];
}
let newArr = []
for (let i = 0; i <= arr.length; i++) {
let result = arr[i] + (i + 1)
newArr.push(result)
if (newArr[i] > 9 ) {
newArr[i] = newArr[i] % 10; // CHANGE HERE
}
}
const filteredArr = newArr.filter(el => el >= 0 && el <= 9)
return filteredArr
}
console.log(incrementer([2, 4, 6, 8, 0, 2, 4, 6, 8, 9, 0, 1, 2, 2]));
console.log(incrementer([1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 8]));
Please see below code.
const incrementer = arr => {
if (arr === []) {
return [];
}
let newArr = [];
for (let i = 0; i < arr.length; i++) {
let result = arr[i] + (i + 1);
// newArr.push(result);
if (result > 9) {
let singleDigit = Number(result.toString().split("")[1]);
newArr.push(singleDigit);
} else {
newArr.push(result);
}
}
// const filteredArr = newArr.filter(el => el >= 0 && el <= 9);
return newArr;
};
console.log(incrementer([1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 8]))
const incrementer = (arr) => {
if (arr === []) {
return []
}
return arr.map((number, index) => (number + index + 1) % 10);
}
Doing the needed additions in (number + index + 1) and % 10 operation will get the last digit.
I have two arrays (X and Y) and I need to create array Z that contains all elements from array X except those, that are present in array Y p times where p is a prime number.
I am trying to write this in JS.
For Example:
Array X:
[2, 3, 9, 2, 5, 1, 3, 7, 10]
Array Y:
[2, 1, 3, 4, 3, 10, 6, 6, 1, 7, 10, 10, 10]
Array Z:
[2, 9, 2, 5, 7, 10]
So far I have this:
const arrX = [2, 3, 9, 2, 5, 1, 3, 7, 10]
const arrY = [2, 1, 3, 4, 3, 10, 6, 6, 1, 7, 10, 10, 10]
const arrZ = []
const counts = [];
// count number occurrences in arrY
for (const num of arrY) {
counts[num] = counts[num] ? counts[num] + 1 : 1;
}
// check if number is prime
const checkPrime = num => {
for (let i = 2; i < num; i++) if (num % i === 0) return false
return true
}
console.log(counts[10]);
// returns 4
Any hint or help appreciated. Thanks!
You're on the right track. counts should be an object mapping elements in arrY to their number of occurrences. It's easily gotten with reduce.
The prime check needs a minor edit, and the last step is to filter arrX. The filter predicate is just a prime check on the count for that element.
// produce an object who's keys are elements in the array
// and whose values are the number of times each value appears
const count = arr => {
return arr.reduce((acc, n) => {
acc[n] = acc[n] ? acc[n]+1 : 1;
return acc;
}, {})
}
// OP prime check is fine, but should handle the 0,1 and negative cases:
const checkPrime = num => {
for (let i = 2; i < num; i++) if (num % i === 0) return false
return num > 1;
}
// Now just filter with the tools you built...
const arrX = [2, 3, 9, 2, 5, 1, 3, 7, 10]
const arrY = [2, 1, 3, 4, 3, 10, 6, 6, 1, 7, 10, 10, 10]
const counts = count(arrY);
const arrZ = arrX.filter(n => checkPrime(counts[n]));
console.log(arrZ)
If I have an array like
const arr = [1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 5, 7];
What would be the best way of finding that the array starts to repeat itself? In this instance that the first three numbers and the last three numbers are in a repeating pattern.
This is a random array, the repeating could easily start at index 365 and not necessarily from the first index.
Any ideas?
Thanks in advance
This does what you're looking for...
const arr1 = [1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 5, 7];
const arr2 = [1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 4, 7];
function patternFound(arr) {
var newArray = arr.map(function(o, i) {
if (i < arr.length - 1) {
return arr[i] + "|" + arr[i + 1];
}
})
.sort();
newArray = newArray.filter(function(o, i) {
if (i < arr.length - 1) {
return (o == newArray[i + 1]);
}
});
return newArray.length > 0;
}
console.log(patternFound(arr1));
console.log(patternFound(arr2));
Basically, it creates an array of paired elements from the first array, with a pipe delimiter (["1|5", "5|7", "7|5" etc.]), sorts it and then looks for duplicates by comparing each element to the next.
There's probably a much smaller way of doing this, but I didn't want to spend time making something that was unreadable. This does what you want and does it simply and clearly.
The first array is the one you supplied, and the second has been changed so there's no matching pattern.
You could use a single loop approach with short circuit and a hash table for found pairs like
{
"1|5": true,
"5|7": true,
"7|5": true,
"5|13": true,
"13|8": true,
"8|1": true,
"1|7": true,
"7|3": true,
"3|8": true,
"8|5": true,
"5|2": true,
"2|1": true
}
The iteration stops immediately on index 12 with the other found pair 1|5.
function check(array) {
var hash = Object.create(null);
return array.some(function (v, i, a) {
var pair = [v, a[i + 1]].join('|');
return hash[pair] || !(hash[pair] = true);
});
}
console.log(check([1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 5, 7])); // true
console.log(check([1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 3, 7])); // false
A nested loop seems the simplest approach.
Make sure to offset the nested loop to save calculations:
/**
* Takes array and returns either boolean FALSE or the first index of a pattern
*
* #param {any[]} arr
* #returns {(false | number)}
*/
function findArrayPattern(arr) {
if (arr.length < 2) {
return false;
}
for (var point1 = 0; point1 < arr.length - 2; point1++) {
var p1 = arr[point1];
var p2 = arr[point1 + 1];
for (var point2 = point1 + 2; point2 < arr.length - 1; point2++) {
var p3 = arr[point2];
var p4 = arr[point2 + 1];
if (p1 == p3 && p2 == p4) {
return point1;
}
}
}
return false;
}
//TEST
var arr = [1, 5, 7, 5, 13, 8, 1, 7, 3, 8, 5, 2, 1, 5, 7];
var pattern = findArrayPattern(arr);
if (pattern !== false) {
console.log("a pattern was found at " + pattern);
} else {
console.log("no pattern was found");
}