Finding a non-consecutive number pair in an array - javascript

Given an array with a minimum length of 3 and a maximum length of 5, which always contains uniquely occurring integers from 0 to 4 in ascending order, I need to pick out two non-consecutive numbers from it. Non-consecutive refers to their numeric value, not their position in the array.
To clarify, here are examples of valid arrays:
[ 1, 2, 3 ]
[ 0, 1, 2, 4 ]
[ 0, 3, 4 ]
For the arrays above, valid answers could be, respectively:
[ 1, 3 ]
[ 0, 2 ], [ 0, 4 ] or [ 1, 4 ]
[ 0, 3 ] or [ 0, 4 ]
Furthermore, in those cases where there is more than one valid answer, I need it to be selected at random, if at all possible (for instance I don't want to favor sequences that begin with the lowest number, which is what would occur if I always began checking from left to right and stopped checking as soon as I found one valid solution).
What would be the most efficient way of tackling this problem in Javascript?

You could use two nested iterations and build an new array for choosing as random result.
function getNonConsecutives(array) {
return array.reduce((r, a, i, aa) => r.concat(aa.slice(i + 2).map(b => [a, b])), []);
}
console.log(getNonConsecutives([ 0, 1, 2, 4 ]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
According to Bee157's answer, you could use a random choice with a constraint, like length for the first index and add the needed space for the second index.
The problem is, due to the nature of choosing the first number first, the distribution of the result is not equal.
function getNonConsecutives(array) {
var i = Math.floor(Math.random() * (array.length - 2));
return [
array[i],
array[Math.floor(Math.random() * (array.length - 2 - i)) + 2 + i]
];
}
console.log(getNonConsecutives([ 0, 1, 2, 4 ]));

demoFn(array) {
var i,j, y =[];
for (i=0; i<=array.length;i++) {
for (j = i + 1; j <= array.length; j++) {
if (array[j] && array[i]) {
if (array[j] !== array[i] + 1) {
y.push([array[i], array[j]]);
}
}
}
}
}
Take a random array and check it.

You can create a function using recursion that will pick random number in each iteration and loop all other elements and if condition is met add to array.
function findN(data) {
data = data.slice();
var r = []
function repeat(data) {
if (data.length < 2) return r;
var n = parseInt(Math.random() * data.length);
data.forEach(function(e, i) {
if (i != n) {
var a = data[n];
if (Math.abs(a - e) != 1 && r.length < 2) r.push(n < i ? [a, e] : [e, a])
}
})
data.splice(n, 1);
repeat(data)
return r;
}
return repeat(data)
}
console.log(findN([1, 2, 3]))
console.log(findN([0, 1, 2, 4]))
console.log(findN([0, 3, 4]))

Something like this should do it:
const pick = nums => {
// Pick a random number
const val = nums[Math.floor(Math.random() * nums.length) + 0];
// Filter out any numbers that are numerically consecutive
const pool = nums.filter(n => Math.abs(n - val) > 1);
// Pick another random number from the remainer
const other = pool[Math.floor(Math.random() * pool.length) + 0];
// Sort + return them
return [val, other].sort();
};
console.log(pick([0, 1, 2, 4]));

since you state that the array ellemnts are all unique, and that they are sorted.
It should suffice to take an random element
var index1=Math.floor(Math.random()*arr.length)
now any other element (except maybe the elemnts on position (index1 +/- 1) are not consecutive
So a new random element can be chosen excluding the first index.
var index2=Math.floor(Math.random()*arr.length);
if(index2==index1){
index2+=((index2<arr.length-1)?1:-1);
}
if(Math.abs(arr[index1]-arr[index2])<=1){
if(index2==0 && arr.length<4){
//set index2 to arr.length-1 and do check again, if not ok=> no result
if(!(arr[index1]-arr[arr.length-1]>=-1)){
return [arr[arr.length-1],arr[index1]];
}
}
else if(index2==arr.length-1 && arr.length<4){
//set index2 to 0 and do check again, if not ok=> no result
if(!(arr[index1]-arr[0]<=1)){
return [arr[0],arr[index1]];
}
}
else{
//if index2>index1 index2++
//else index2--
//always OK so no further check needed
index2+=(index2>index1?1:-1);
return [arr[index1],arr[index2]];
}
}
else{
//ok
return [arr[index1,arr[index2]];
}
return false;
if speed is not important, you can use a filter on the array to calculate a new array with all elements differing more then 1 unit of arr[index1]. and randomly select a new number from this new array.
Other attempt
function getNonConsecutive(arr){
var index1,index2,arr2;
index1=Math.floor(Math.random()*arr.length);
arr2=[].concat(arr);
arr2.splice((index1!==0?index1-1:index1),(index!==0?3:2));
if(arr2.length){
index2=Math.floor(Math.random()*arr2.length);
return [arr[index1],arr2[index2]];
}
else{
//original array has length 3 or less
arr2=[].concat(arr);
arr2.splice(index1),1);
for (var j=0,len=arr.length;j<len;j++){
if(Math.abs(arr1[index1]-arr2[j])>1){
return [arr[index1],arr2[j]];
}
}
}
return false
}

Related

Performance of Array.reduce

I was trying out the Codility MaxCounter question:
You are given N counters, initially set to 0, and you have two possible operations on them:
increase(X) − counter X is increased by 1,
max_counter − all counters are set to the maximum value of any counter.
A non-empty zero-indexed array A of M integers is given. This array represents consecutive operations:
if A[K] = X, such that 1 ≤ X ≤ N, then operation K is increase(X),
if A[K] = N + 1 then operation K is max_counter.
For example, given integer N = 5 and array A such that:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the values of the counters after each consecutive operation will be:
(0, 0, 1, 0, 0)
(0, 0, 1, 1, 0)
(0, 0, 1, 2, 0)
(2, 2, 2, 2, 2)
(3, 2, 2, 2, 2)
(3, 2, 2, 3, 2)
(3, 2, 2, 4, 2)
The goal is to calculate the value of every counter after all operations.
For example, given:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the function should return [3, 2, 2, 4, 2].
Assume that:
N and M are integers within the range [1..100,000];
each element of array A is an integer within the range [1..N + 1].
Complexity:
expected worst-case time complexity is O(N+M);
expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
Here is my solution, for which i used the reduce method. It scores 40% on performance.
Can anyone see where the performance issue is here?
I'm assuming perhaps it is the reduce speed itself thats the problem and that in order to increase this score i would need to convert this to for loops, but this just feels like a really ugly way to use modern javascript in this context.
Hopefully one of you will point out something non JS related about this solution that will not indicate that reduce is the issue and instead indicate that im an idiot (I will deal with the ramifications of this over a cold beer)
function maxCounter(N, A) {
let maxCounter = 0
const NArray = new Array(N).fill(0)
const results = A.reduce((acc, current) => {
if (current === N + 1) return new Array(N).fill(maxCounter)
const out = acc.map((element, index) => {
if (index + 1 === current){
const newValue = element + 1
if (newValue > maxCounter) maxCounter = newValue
return newValue
} else {
return element
}
})
return out
}
, NArray)
return results
}
const results = maxCounter(5, [1,4,2,5,2,6,2])
console.log({results})
You could introduce a min value, which is set if all values have to be set to the max value, but this does happen only if the value is incremented, then the min value is used for update or at the end to give all items at least the min value.
function maxCounter(n, a) {
var min = 0,
max = 0,
result = [],
i;
for (i of a) {
if (--i === n) {
min = max;
continue;
}
if (!result[i] || result[i] < min) result[i] = min;
if (++result[i] > max) max = result[i];
}
for (i = 0; i < n; i++) {
if (!result[i] || result[i] < min) result[i] = min;
}
return result;
}
console.log(...maxCounter(5, [3, 4, 4, 6, 1, 4, 4]));
console.log(...maxCounter(5, [1, 4, 2, 5, 2, 6, 2]));
The time complexity of your solution is O(NM), which exceeds the required complexity of O(N + M). The reason is that your solution builds a new array of length N for each of the M queries.
This isn't because you used reduce, but rather because you used reduce on an array of length M with a reduction operation which takes O(N) time. If your reduction operation took O(1) time instead, then you would be fine.
It is possible to achieve O(1) time per query by updating the counters array in-place; the hard part is answering the "set-all" query in O(1) time, i.e. without updating all N elements of the counter array. One solution is to record a kind of "timestamp" for the last time a counter was modified, and another "timestamp" for the last time "set-all" was done; this allows you to test if a counter's value is older than the most-recent "set-all" query, and get the correct counter value either way. Here's a class which answers both queries in O(1) using this technique:
class Counters {
constructor(n) {
this.setallVal = 0;
this.setallTimestamp = 0;
this.maxCounter = 0;
this.counts = new Array(n).fill(0);
this.timestamps = new Array(n).fill(0);
}
getCounter(i) {
if (this.timestamps[i] >= this.setallTimestamp) {
return this.counts[i];
} else {
return this.setallVal;
}
}
incCounter(i) {
let c = this.getCounter(i) + 1;
this.counts[i] = c;
this.timestamps[i] = this.setallTimestamp;
if (c > this.maxCounter) {
this.maxCounter = c;
}
}
setAllToMax() {
this.setallVal = this.maxCounter;
this.setallTimestamp++;
}
}
#kaya3 Thanks!
I tried this too which gets 60% but i assume falls over with the same issue of setting the max across all elements
function solution(N, A) {
let maxCounter = 0
const NArray = new Array(N).fill(0)
for (i=0; i<A.length; i++){
if (A[i]>N){
NArray.fill(maxCounter)
} else {
const currentVal = NArray[A[i] -1] + 1
if (currentVal > maxCounter) maxCounter = currentVal
NArray[A[i] -1] = currentVal
}
}
return NArray
}

Splitting of array

Given a non-empty array, if there is a place to split the array so that the sum of the numbers on one side is equal to the sum of the numbers on the other side return the length of the two arrays as an array but if there is no place to split the array, return -1
canBalance([1, 1, 1, 2, 1]) → [3,2]
canBalance([2, 1, 1, 2, 1]) → -1
canBalance([10, 10]) → [1,1]
function canBalance(array) {
//Type your solutions here
}
module.exports = canBalance;
Make two variables, and add and subtract each item in the array until they are equal.
function canBalance(array) {
let start = 0, end = array.reduce((a, c) => a + c, 0);
for (let i = 0; i < array.length; i++) {
start += array[i];
end -= array[i];
if (start == end) {
return [i + 1, array.length - (i + 1)];
}
}
return -1;
}
console.log(canBalance([1, 1, 1, 2, 1]));
console.log(canBalance([2, 1, 1, 2, 1]));
console.log(canBalance([10, 10]));
loop through the array from the first (index 0) to the one before the last (length -1) since you want to check only until the second last to compare against the last one.
you can use slice to get the array minus the one element being iterated each time and use a reducer to get the sum
const reducer = (a, c) => a+c;
function canBalance(array) {
var result = [];
var arr = [];
for(var i=0; i<array.length - 1; i++){
arr.push(array[i]);
var leftover = array.slice(i+1,array.length);
if(arr.reduce(reducer) === leftover.reduce(reducer)){
result.push(arr.length);
result.push(leftover.length);
}
}
return result.length > 0 ? result : -1;
}

How to get the sum of all duplicates in an array?

I am trying to calculate the sum of all duplicates in an array. For example:
duplicate([1,1,2,3,3]) --> should return 8.
I have written the following function to calculate the sum of duplicates in an array using JavaScript. Currently it is returning an array with duplicates one less than what they are present in the array.
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
stack.push(arr[i])
}
}
return stack;
}
console.log(duplicate([1,2,1,2,2,3,3]))
This is returning [ 1, 2, 2, 3 ]
How do I get the correct array and calculate the correct sum? I have to use Object for that?
To make the logic easier, you might filter out the non-duplicates by checking whether their indexOf in the array is equal to their lastIndexOf in the array:
function duplicate(arr) {
const duplicates = arr.filter(elm => arr.indexOf(elm) !== arr.lastIndexOf(elm));
return duplicates.reduce((a, b) => a + b);
}
console.log(duplicate([1,1,2,3,3])); // --> should return 8.
console.log(duplicate([1,2,1,2,2,3,3]));
Initially create an object where the keys will be the integer and their value will be the number of occurrence. Then if the number of occurrence is more than 1 , multiply the number with number of occurrence.
function duplicate(arr) {
let dupVal = 0;
let k = arr.reduce((acc, curr, index) => {
if (acc[curr] === undefined) {
acc[curr] = 1
} else {
acc[curr] += 1;
}
return acc
}, {});
for (let keys in k) {
if (k[keys] > 1) {
dupVal += parseInt(keys, 10) * k[keys]
}
}
return dupVal;
}
console.log(duplicate([1, 2, 1, 2, 2, 3, 3]))
Try This one
const arr = [1,1,2,3,3]
let dup = arr.filter((value, index)=>{
// creating a copy of main array
let copyarr = [].concat(arr)
// removing present value
copyarr.splice(index,1)
// after removing present value, if you still
// get the value in copied array that means
// it has duplicates
if(copyarr.indexOf(value)>-1){
return true
}
return false
})
// now add it using reduce
let sum = dup.reduce((acc, value)=> acc+value,0)
console.log(sum)
Copy above code and paste into chrome devTool. You will get the answer.
The problem is that you are matching value with immediate next value in array, in array that is sorted already it will work, but not on unsorted one. So try to sort the array first and then run your code.
Edit :
Looks like sorting is added in code,
But another condition => if there is number that is repeated more than twice it should be handled and only appear once in stack, if that is required.
This will : console.log(duplicate([1,2,1,2,2,3,3]))
Result this : [1,2,3]
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
if(stack.length == 0 || (arr[i] != stack[stack.length-1])){
stack.push(arr[i])
}
}
}
return stack;
}
you can use JS Array.reduce method to accomplish your requirement in a shorter way
function sumDplicates(arr) {
return arr.reduce(function(tot, val, index, _arr) {
if (_arr.lastIndexOf(val) > index || _arr.indexOf(val) != index)
return tot + val;
return tot
}, 0)
}
console.log(sumDplicates([1, 1, 2, 3, 3]));
console.log(sumDplicates([1, 2, 1, 2, 2, 3, 3]));
You can pursue your original sorting approach with a slight modification:
if (arr[i] === arr[i + 1] || arr[i] === arr[i - 1])
That is, check if the previous or the next element in the sorted array is equal to the current element for it to qualify as a duplicate.
The following solution accomplishes this with filter and reduce:
function duplicate(array) {
return array
.sort((a, b) => a - b)
.filter((a, i, arr) => (arr[i] === arr[i + 1] || arr[i] === arr[i - 1]))
.reduce((a, b) => a + b, 0);
}
console.log(duplicate([1, 1, 2, 3, 3]));
console.log(duplicate([1, 2, 1, 2, 3, 3]));
Array.reduce() and Array.lastIndexOf() will simply solve your problem.
function sum(arr)
{
return arr.reduce(function(sum, item){
return arr.lastIndexOf(item)!==arr.indexOf(item) ? sum+=item : sum;
},0)
}
console.log(sum([1,1,2,3,3]));
console.log(sum([1,2,3,4]));
console.log(sum([1,2,2,3,4]));
console.log(sum([1,1,2,2,3,3,4,4]));
Though I don't know much about JavaScript, If I were you, I would have simply kept a temporary array, which copies all the duplicate variables and then use that array for sum.
Also, if you want to add the particular number as many times as it appears, I will suggest creating a table like the one in sparse matrices
and then referring to it during addition.
This logic, though not space efficient, is very easy to implement.
Here is an approach with a single Array.reduce and nothing else. No Array.indexOf or Array.lastIndexOf. Although it might not be as concise it does not traverse the array looking for indexes multiple times nor does any Array.filter:
const sumDubs = arr => arr.reduce((r,c) => {
if(r[c]) {
r[c] += 1
r.sum += r[c] > 2 ? (r[c]*c) - ((r[c]-1)*c) : r[c]*c
} else r[c] = 1
return r
}, { sum: 0 }).sum
console.log(sumDubs([1, 1, 2, 3, 3])) // 8
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3])) // 14
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3, 3, 1, 2, 4, 4])) // 28
The idea is to keep track of the on-going sum via a property in the accumulator of the Array.reduce and simply keep calculating the sum based on which number is duplicated and more importantly how many times.

Finding Greatest value on array of indexes

I have to find the greatest value of array and return its index position.
This is my snippets of code:
function findGreaterNumbers(array) {
for(var i = 1; i < array.length; i++) {
if(array[i].length !== 0) {
var result = Math.max.apply(null, [i]);
} else {
return 0;
}
}
return result;
}
console.log(findGreaterNumbers([1, 2, 3]); // 2: I want 3
console.log(findGreaterNumbers([6, 1, 2, 7])); // 3: I want 4
console.log(findGreaterNumbers([5, 4, 3, 2, 1])); // 4: I want 0
console.log(findGreaterNumbers([])); // undefined: I want 0
You can do the following:
const findMax = (arr) => {
const max = Math.max.apply(Math, arr);
return arr.indexOf(max);
}
First you create a function that receives an array arr then inside this function you find the array element with the highest value by using the JS built in Math.max method. If you return this value will show you the maximum value of the numbers in the array you've supplied.
In order to return the index you can use the indexOf array method to find its index. You return this value and you have the index of the maximum number in an array.
const input = [1,5,3,4,0,-1];
function getMaxIndex() {
const max = Math.max(...input);
return input.findIndex((item) => item === max);
}
console.log(getMaxIndex())
try this way!
function findGreaterNumbers(arr) {
let count = 0;
for (let i = 0; i < arr.length - 1; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] < arr[j]) {
count ++;
}
}
}
return count;
}
findGreaterNumbers([1,2,3]) // 3
findGreaterNumbers([6,1,2,7]) // 4
findGreaterNumbers([5,4,3,2,1]) // 0
findGreaterNumbers([]) // 0
Another way you can find max number by Math.max and find index of max from Array.indexOf
var numbers = [ 0, 1, 3, 2, 5, 10 , 4 ,6];
var max = Math.max(...numbers)
var index = numbers.indexOf(max)
console.log('Index of max', index)
I would go with the built-in map and reduce methods for arrays which are extremely useful. map transforms an array into another array of the same length, whereas reduce can be used to do any aggregation on the array itself. And finding lowest/highest values is just a special type of aggregation.
That said, the following method will give you all indices that correspond to the largest value:
function iMax(array) {
return array.reduce((m, d, i) => {
return (m.length === 0 || d > m[0].d)
? [{d: d, i: i}]
: (d === m[0].d ? m.concat({d: d, i: i}) : m)
}, [])
.map(d => d.i);
}
// Run tests
console.log(JSON.stringify([1, 2, 3]) + " => " + JSON.stringify(iMax([1, 2, 3])));
console.log(JSON.stringify([6, 1, 2, 7]) + " => " + JSON.stringify(iMax([6, 1, 2, 7])));
console.log(JSON.stringify([5, 4, 3, 2, 1]) + " => " + JSON.stringify(iMax([5, 4, 3, 2, 1])));
console.log(JSON.stringify([5, 4, 3, 5, 2, 1]) + " => " + JSON.stringify(iMax([5, 4, 3, 5, 2, 1])));
console.log(JSON.stringify([]) + " => " + JSON.stringify(iMax([])));
I also added an example with multiple max values. Note that indices in Javascript start with 0, if you need the indices you mentioned in your example, you can add 1 to the results (but I would not recommend it). Finally, if you need any other value than an empty array if there is no max value in the input array, you can have an if before returning the result.
Try and go for something simpler like:
function findGreaterNumbers(array) {
let max = array[0]; // initialize the maximum to the first element
array.forEach((value) => { // fancier for loop that iterates through the
// array, **value** is the placeholder for the
// members
if (value > max) {
max = value;
}
}); //after the forEach, max will have the largest value
return array.indexOf(max); // returns the index of max
}
Some more explanations: forEach

JavaScript - Special case of subset sum algorithm

From a given array of positive integers, I want to know if the sum of E elements from the array is equal to a given number N.
For example, given the array arr = [1, 2, 3, 4] , e = 3 and n = 9. It means if the sum of 3 elements in arr equals to 9. The result is true since 2 + 3 + 4 is equal to 9.
Another example with arr = [1, 2, 3, 4] , e = 2 and n = 7. It is true since 3 + 4 is equal to 7.
I'm trying to resolve it with recursion, but I'm stuck. My idea is to nest loops dynamically to walk through the elements to the array and compare them.
My attempt is this:
function subsetsum(arr, elements, n) {
loop(arr, elements, n, [], 0);
}
function loop(arr, elements, n, aux, index) {
if(aux.length != elements) {
aux[index] = arr.length - 1;
loop(arr, elements, n, aux, index + 1);
} else {
if ((elements - index + 1) < 0) {
return 0;
} else {
if (aux[elements - index + 1] > 0) {
aux[elements - index + 1]--;
loop(arr, elements, n, aux, index);
}
}
}
}
subsetsum([1, 2, 3, 4], 3, 9));
A related question is at Find the highest subset of an integer array whose sums add up to a given target. That can be modified to restrict the number of elements in the subset as follows:
// Find subset of a, of length e, that sums to n
function subset_sum(a, e, n) {
if (n < 0) return null; // Nothing adds up to a negative number
if (e === 0) return n === 0 ? [] : null; // Empty list is the solution for a target of 0
a = a.slice();
while (a.length) { // Try remaining values
var v = a.shift(); // Take next value
var s = subset_sum(a, e - 1, n - v); // Find solution recursively
if (s) return s.concat(v); // If solution, return
}
}
I've been playing around with this for a while and decided to use a short-cut, mainly the permutation code from this previous SO question.
My code uses basically uses the permutation code to create an array of all the possible permutations from the input array, then for each array (using map) grabs a slice corresponding to the number specified as amount, sums that slice and if it is the same as total returns true.
some then returns the final result as to whether there are any permutations that equals the total.
function checker(arr, amount, total) {
var add = function (a, b) { return a + b; }
return permutator(arr).map(function(arr) {
var ns = arr.slice(0, amount);
var sum = ns.reduce(add);
return sum === total;
}).some(Boolean);
}
checker([1, 2, 3, 4], 3, 9); // true
I've included two demos - 1) a demo showing this code, and 2) code that provides a more detailed breakdown: basically map returns an object containing the slice info, the sum totals and whether the condition has been met.
This is probably not what you're looking for because it's a bit long-winded, but it was certainly useful for me to investigate :)
Edit - alternatively here's a hacked version of that permutation code from the previous question that delivers the results and an array of matches:
function permutator(inputArr, amount, total) {
var results = [], out = [];
function permute(arr, memo) {
var cur, memo = memo || [];
var add = function (a, b) { return a + b; }
for (var i = 0; i < arr.length; i++) {
cur = arr.splice(i, 1);
if (arr.length === 0) {
results.push(memo.concat(cur));
}
var a = arr.slice();
// this is the change
var sum = memo.concat(cur).reduce(add);
if (memo.concat(cur).length === amount && sum === total) {
out.push(memo.concat(cur))
}
permute(a, memo.concat(cur));
arr.splice(i, 0, cur[0]);
}
return [results, out];
}
return permute(inputArr);
}
permutator([1,2,3,4], 3, 9);
DEMO
If I understand you correctly, the solution of this task must be simple like this:
function subsetsum(arr, countElements, sum) {
var length = arr.length-1;
var temp = 0;
var lastElement = length-countElements;
console.log(lastElement);
for (var i = length; i > lastElement; i--) {
temp = temp+arr[i];
console.log('Sum: '+temp);
}
if (temp === sum) {
console.log('True!');
} else {console.log('False!')}
};
subsetsum([1, 2, 3, 4], 2, 7);

Categories

Resources