Optimize a function to iterate less into arrays - javascript

As a part of a CodeSignal challange i'm asked to :
Given a sequence of integers as an array, determine whether it is
possible to obtain a strictly increasing sequence by removing no more
than one element from the array.
For sequence = [1, 3, 2, 1], the output should be
function(sequence) = false. There is no one element in this array
that can be removed in order to get a strictly increasing sequence.
For sequence = [1, 3, 2], the output should be function(sequence) =
true. You can remove 3 from the array to get the strictly
increasing sequence [1, 2]. Alternately, you can remove 2 to get
the strictly increasing sequence [1, 3].
Guaranteed constraints:
2 ≤ sequence.length ≤ 105 &
-105 ≤ sequence[i] ≤ 105.
My code works but i'm looking for a more performant solution because the challenge has fixed the execution time limit of 4 seconds.
Here's my code:
const almostIncreasingSequence = seq => {
let i = 0;
while (i < seq.length) {
const filtred = [...seq];
// splice 1 element at index i from array
filtred.splice(i, 1);
// create a `sorted` array with only unique numbers and sort it
const sorted = [...new Set([...filtred].sort((a, b) => a - b))];
if (filtred.join("") === sorted.join("")) {
console.log(`filtred [${filtred}] ✅ sorted [${sorted}]`);
return true;
} else {
console.log(`filtred [${filtred}] 🛑 sorted [${sorted}]`);
}
i++;
}
return false;
};
// array of random numbers for testing
const testArr = Array.from({ length: 100000 }, () =>
Math.floor(Math.random() * 100)
);
// [1,,N] for testing
// const testArr = Array.apply(null, { length: 100001 }).map(Number.call, Number);
console.log(almostIncreasingSequence(testArr));

You could take a found index.
This approach tests either three consecutive elements, like
v
1 2 [3 8 4] 5 6 7 -> found at index 3
or takes for the next loop a check which omits the found value by checking the found index.
v
1 2 3 [8 4 5] 6 7
^ omit value on found index
Then if not in sequence, the adjacent items are checked to prevent this pattern
v
1 2 3 1 2 5 6 7
3 > 2
v
1 2 3 8 2 5 6 7
3 > 2
and if found, more than one element is in wrong position.
function inSequence(array) {
var i,
found;
for (i = 1; i < array.length - 1; i++) {
if ((found === i - 1 || array[i - 1] < array[i]) && array[i] < array[i + 1]) continue;
if (array[i - 1] >= array[i + 1] || found !== undefined) return false;
found = i;
}
return true;
}
console.log(inSequence([2, 1]));
console.log(inSequence([1, 2, 3]));
console.log(inSequence([2, 1, 3]));
console.log(inSequence([1, 2, 4, 3]));
console.log(inSequence([1, 2, 3, 8, 4, 5, 6, 7]));
console.log(inSequence([1, 2, 3, 8, 4, 5, 6, 9, 7]));
console.log(inSequence([2, 1, 3, 4, 5, 2]));
console.log(inSequence([1, 2, 3, 1, 2, 5, 6, 7]));
console.log(inSequence([1, 2, 3, 8, 2, 5, 6, 7]));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Loop through your array once for a worst case performance of O(n). Count the number of times the next number is less than the last (ie out of sequence) and also bomb out as soon as this has occurred more than once to improve performance where possible.
let sequenceTrue = [1, 2, 3, 4, 2, 5, 6, 7, 8, 9];
let sequenceFalse = [1, 3, 4, 2, 5, 6, 7, 8, 2, 9];
function isAlmostIncreasingSequence(sequence) {
let count = 0;
for (let index = 0; index < sequence.length && count <= 1; index++) {
if (
!!sequence[index - 1]
&& sequence[index] <= sequence[index - 1]
) {
count++;
}
}
return count <= 1;
}
console.log("should be true", isAlmostIncreasingSequence(sequenceTrue));
console.log("should be false", isAlmostIncreasingSequence(sequenceFalse));
EDIT:
improve performance of the example above by removing the first check from the for loop and manually evaluate the first item in the array.
function isAlmostIncreasingSequence(sequence) {
let count = 0;
if (sequence[1] < sequence[0]) count++;
for (let index = 1; index < sequence.length && count <= 1; index++) {
if (sequence[index] <= sequence[index - 1]) {
count++;
}
}
return count <= 1;
}

Related

Longest repeated odd number

Consider:
let N = 12
let arr = [1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1]
Your task is to find the maximum number of times an odd number is continuously repeated in the array.
What is the approach for this?
This is the hint:
1 is repeated 4 times from index 0 to index 3 → 4 times
2 is repeated 5 times from index 4 to index 8 → 5 times
1 is repeated 3 times from index 9 to index 11 → 3 times
The odd numbers in array are 1s.
1 occurs 4 times and 3 times continuously, so 4 is the maximum number of times an odd number is continuously repeated in this array.
function longestRepeatedOdd(N, array) {
// Write code here
let count = 0;
for (let i = 0; i <= array.length-1; i++){
if (array[i] % 2 !== 0){
count++
}else if (array[i] % 2 === 0){
break;
}
}
console.log(count)
}
You can use Array.reduce() to keep track of the current run of odd numbers and the maximum.
We use an accumulator value with a max and current field, the current field the current run of odd numbers and the max field is the longest run.
If the current run exceeds the max we set the max to this value.
function longestRepeatedOdd(arr) {
return arr.reduce((acc, n, idx) => {
acc.current = (n === arr[idx - 1] ? acc.current: 0) + (n % 2);
acc.max = (acc.current > acc.max) ? acc.current: acc.max;
return acc;
}, { max: 0, current: 0 }).max;
}
const inputs = [
[1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1],
[7, 7, 4, 4, 1, 1, 1, 1, 1, 1, 4, 3],
[1, 2, 2, 2]
]
inputs.forEach((input, idx) => {
console.log(`Test input #${idx + 1} result:`, longestRepeatedOdd(input))
})
Your answer is definitely valid. Just replace break with continue.
function longestRepeatedOdd(N, array) {
//write code here
let count = 0;
for (let i = 0; i <= array.length-1; i++){
if (array[i] % 2 !== 0){
count++
}else if (array[i] % 2 === 0){
continue;
}
}
return count
}
Try reduce and creating a JavaScript object that reports its value:
const count = arr.reduce((accumulator, value) => {
return {...accumulator, [value]: (accumulator[value] || 0) + 1};
}, {});
Object { 1: 7, 2: 5 }
console.log(count);
Number one repeated 7 times and number two 5 times.

Can't seem to identify the issue (splitting an array in chunks)

So this function is supposed to split the array into chunks the size of the second argument, e.g. [1, 2, 3, 4] with the second argument 2 would return [[1, 2], [3, 4]], if the number is 3 it would return [[1, 2, 3], [4]] and so on. My function seems to be behaving in a similar way but it only returns the first chunk, and the subsequent chunks are just empty arrays. I increase the index by by the second argument after each iteration, so I'm not sure why it's not working. Can someone please explain what exactly is happening here and where is the error in the logic?
let arr = [1, 2, 3, 4, 5, 6]
function test(arr, num) {
let idx = 0;
let newArr = []
while (idx < arr.length) {
newArr.push(arr.slice(idx, num))
idx = idx + num
}
return newArr
}
console.log(test(arr, 2))
You need an index as second parameter of Array#slice, instead of the length of slice.
For example if you take the second array, index = 2 and the second parameter has to be 4, the index of the end for slicing.
chunks
values 1 2 3 4 5 6
indices 0 1 2 3 4 5
slice 0 2 1 2
2 4 3 4
4 6 5 6
function test(arr, num) {
let idx = 0;
let newArr = [];
while (idx < arr.length) {
newArr.push(arr.slice(idx, idx += num));
}
return newArr;
}
let arr = [1, 2, 3, 4, 5, 6];
console.log(test(arr, 2));

latest elements in array not found? codewars kata

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.

Need help finding logic flaw in array of integers to find pairs of K different pairs

Trying to solve the below:
Given an array of integers and an integer k, you need to find the number of unique k-diff pairs in the array. Here a k-diff pair is defined as an integer pair (i, j), where i and j are both numbers in the array and their absolute difference is k.
Example 1:
Input: [3, 1, 4, 1, 5], k = 2
Output: 2
Explanation: There are two 2-diff pairs in the array, (1, 3) and (3, 5).
Although we have two 1s in the input, we should only return the number of unique pairs.
Example 2:
Input:[1, 2, 3, 4, 5], k = 1
Output: 4
Explanation: There are four 1-diff pairs in the array, (1, 2), (2, 3), (3, 4) and (4, 5).
Example 3:
Input: [1, 3, 1, 5, 4], k = 0
Output: 1
Explanation: There is one 0-diff pair in the array, (1, 1).
I have written the following code:
const findPairs = (nums, k) => {
let count = 0
if (k < 0) return 0
if (k === 0) {
const hash = {}
for (let num of nums) {
hash[num] = (hash[num] || 0) + 1
}
nums = Object.keys(hash)
for (let num of nums) {
if (hash[num] > 1) count++
}
} else {
let numSet = new Set(nums)
nums = Array.from(numSet)
for (let num of nums) {
if (numSet.has(Math.abs(num + k))) {
count++
numSet.delete(Math.abs(num + k))
}
}
}
return count
}
The test cases all pass except the last one:
console.log(findPairs([3, 1, 4, 1, 5], 2)) //2
console.log(findPairs([1, 2, 3, 4, 5], 1)) //4
console.log(findPairs([1, 3, 1, 5, 4], 0)) //1
console.log(findPairs([1, 2, 3, 4, 5], -1)) //0
console.log(findPairs([1, 1, 1, 1, 1], 0)) //1
console.log(findPairs([1, 3, 1, 5, 4], 0)) //1
console.log(findPairs([-1, -2, -3], 1)) //2 my code returns 0 incorrectly
Need help figuring out logic flaw.
Your Math.abs call will mean that count only gets incremented when a matching element is found which is positive or zero. In the last example, you have negative elements. If you remove the Math.abs, your code works:
const findPairs = (nums, k) => {
let count = 0
if (k < 0) return 0
if (k === 0) {
const hash = {}
for (let num of nums) {
hash[num] = (hash[num] || 0) + 1
}
nums = Object.keys(hash)
for (let num of nums) {
if (hash[num] > 1) count++
}
} else {
let numSet = new Set(nums)
nums = Array.from(numSet)
for (let num of nums) {
if (numSet.has(num + k)) {
count++
numSet.delete(num + k)
}
}
}
return count
}
console.log(findPairs([3, 1, 4, 1, 5], 2)) //2
console.log(findPairs([1, 2, 3, 4, 5], 1)) //4
console.log(findPairs([1, 3, 1, 5, 4], 0)) //1
console.log(findPairs([1, 2, 3, 4, 5], -1)) //0
console.log(findPairs([1, 1, 1, 1, 1], 0)) //1
console.log(findPairs([1, 3, 1, 5, 4], 0)) //1
console.log(findPairs([-1, -2, -3], 1)) //2

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

Categories

Resources