Checking whether array contains incremental elements if one is removed - javascript

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));

Related

Find the smallest missing positive int in array in js

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));

Count repeated numbers in array and return true (Cognitive Complexity)

I need to check if a number repeats itself at least three times in an array. How can I refactor it to decrease the Cognitive Complexity that Lint keeps complaining about.
Heres my code:
let array11 = [1, 3, 2, 3, 5, 6, 7, 8, 9, 0, 1];
function checkDuplicateNumber (array11) {
for (let i = 0; i < array11.length; i += 1) {
let sameNumberLoop = 0;
for (let i2 = i; i2 < array11.length; i2 += 1) {
if (array11[i] === array11[i2]) {
sameNumberLoop += 1;
if (sameNumberLoop >= 3) {
return true;
}
}
}
}
}
Instead of iterating multiple times, iterate just once, while counting up the number of occurrences in an object or Map:
let array11 = [1, 3, 2, 3, 5, 6, 7, 8, 9, 0, 1];
function checkDuplicateNumber (array) {
const counts = {};
for (const num of array) {
counts[num] = (counts[num] || 0) + 1;
if (counts[num] === 3) return true;
}
return false;
};
console.log(checkDuplicateNumber(array11));
console.log(checkDuplicateNumber([3, 1, 3, 5, 3]));
let array11 = [1, 3, 2, 3, 5, 6, 7, 8, 9, 0, 1]
let array22 = [1, 3, 2, 3, 5, 6, 7, 1, 9, 0, 1]
function checkDuplicateNumber(arr) {
const map = new Map()
return arr.some((v) => (map.has(v) ? (++map.get(v).count === 3) : (map.set(v, { count: 1 }), false)))
}
console.log(checkDuplicateNumber(array11))
console.log(checkDuplicateNumber(array22))

Find all the same numbers in the array

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);

Loop through 2d-array diagonally

I've been searching on this website for an valid answer for my question so far i haven't find one so i'm posting a question
I have to loop trough an 2d-array diagonally to check if the value === 0. Whenever i try this it only goes trough the array of the 2d-array and then it stops
Basically i need to get the indices 0,0 1,1 2,2...
function isValidDistanceMatrix(nss) {
let row_count = nss.length;
let row_sizes = nss.map(ns => ns.length);
let maxRow = Math.max(...row_sizes);
let min = Math.min(...row_sizes);
if (maxRow === row_count && min === maxRow){
for (let x = 0; x < row_count; x++){
if (nss[x][x] === 0){
return true;
}
}
}
return false;
}
[[0, 5, 6, 1], [5, 0, 1, 1], [6, 1, 0, 1], [1, 1, 1, 0]] returns true
[[0, 5, 6, 1], [5, 4, 1, 1], [6, 1, 5, 7], [1, 1, 1, 0]] returns false
You are returning true after checking the first value instead of checking all of the values.
Change it to return false for any non-zero value, and return true at the end :
function isValidDistanceMatrix(nss) {
let row_count = nss.length;
let row_sizes = nss.map(ns => ns.length);
let maxRow = Math.max(...row_sizes);
let min = Math.min(...row_sizes);
if (maxRow !== row_count || min !== row_count) return false; // change to return false
for (let x = 0; x < row_count; x++) {
if (nss[x][x] !== 0) { // change to not equal
return false; // change to false on the first non-zero
}
}
return true; // change to return true after all values are checked
}
Alternatively, it can be simplified with the Array.prototype.every() method (not tested) :
const isValidDistanceMatrix = nss =>
nss.every((row, index) => row.length === nss.length && row[index] === 0);

How to recognize a repeating pattern in an array in javascript

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");
}

Categories

Resources