Find a missing number in a sequence using .reduce - javascript

var o = [1,2,3,5,6,7,8]
var res = o.reduce(function(x,y){
return !((y-x)===1)?y-1:'Nothing'
})
console.log(res)//7
Output should be 4, want to know if it is possible using reduce or functionally (Not through loops)? it only works if the missing value is before the last value of an array.

You can use reduce to compute the actual sum of all elements and then subtract it from the target sum (n(a0+an)/2). This gives you the missing number.
var o = [1,2,3,5,6,7,8];
var len = o.length;
var sum = (len + 1) * (o[0] + o[len - 1]) / 2;
var res = sum - o.reduce((x,y) => x + y);
console.log(res);
Note that this works with any starting value and any step, e.g. for [3,5,7,11] it will correctly print 9. The only requirement is that o should be an arithmetic progression.

You could use a start value and check the previous element and the actual element.
var o = [1, 2, 3, 5, 6, 7, 8],
res = o.reduce(function(r, a, i, aa) {
return !i || r !== undefined || aa[i - 1] + 1 === a ? r : aa[i - 1] + 1;
}, undefined);
console.log(res);

Instead of reduce you could use find, which will not look any further once it finds a missing value:
const o = [1,2,3,5,6,7,8];
const res = o.find( (x,i) => o[i+1]-x > 1 ) + 1;
console.log(res)//4

It's always good to generalize these jobs. So you should provide a series descriptor function for the algorithm to find which item is missing in the series. Let's do it;
function findMissingItem(a,s){
return s(a[a.findIndex((f,i,a) => i ? f !== s(a[i-1]) : false)-1]);
}
var data1 = [1,2,3,5,6,7,8],
data2 = [1,4,9,16,36,49,64,81],
series1 = n => n+1,
series2 = n => Math.pow(Math.sqrt(n)+1,2);
res1 = findMissingItem(data1,series1),
res2 = findMissingItem(data2,series2);
console.log(res1);
console.log(res2);

Here's a simple solution using Array.reduce and the ES6 arrow function for brevity.
const array = [1, 2, 3, 5, 6, 7, 8];
const result = array.reduce((result, x) => x > result ? result : x + 1, 1)
console.log(result); // 4
With a little refactoring, we can start to make the solution more generic.
const sequence = [1, 2, 3, 5, 6, 7, 8];
const result = sequence.reduce(missingLinkReducer, sequence[0])
function missingLinkReducer(expected, actual) {
return expected === actual ? nextValue(expected) : expected;
}
function nextValue(value) {
return value + 1;
}
console.log(result);
Going a little bit further, we can make it so that different functions can be plugged in for calculating the next value.
const sequence = [1, 2, 3, 5, 6, 7, 8];
const result = sequence.reduce(createMissingLinkReducer(increment), sequence[0]);
console.log(result + ' is missing from ' + sequence);
const sequenceB = [1, 2, 4, 8, 16, 64, 128];
const resultB = sequenceB.reduce(createMissingLinkReducer(double), sequenceB[0]);
console.log(resultB + ' is missing from ' + sequenceB);
function createMissingLinkReducer(nextValue) {
return function missingLinkReducer(expected, actual) {
return expected === actual ? nextValue(expected) : expected;
}
}
function increment(value) {
return value + 1;
}
function double(value) {
return value * 2;
}

You can use every for this, below will work for any sequence interval you specify - it will return -1 if all elements are in sequence, or the element that doesn't fit:
var o = [1, 4, 7, 10, 11]
var seqInterval = 3;
function getMissing(arr, interval) {
var hit = -1;
var res = arr.every(function(e, i) {
hit = i === 0 ? hit : ((e - interval) === arr[i - 1] ? -1 : e);
return hit === -1;
});
return hit;
}
console.log(getMissing(o, seqInterval));
var o1 = [1,2,3,5,6,7,8];
var seqInterval1 = 1;
console.log(getMissing(o1, seqInterval1));

Related

How can I return "1-5" if the array is [1,2,3,4,5] in JavaScript?

I am trying to make a program that returns ["1-5"] if I give [1,2,3,4,5].
I have made it but I can't filter it. So I want a code that will filter my output code. Or any code that is better than mine.
let array = [1,2,3,5,6,7,8,10,11, 34, 56,57,];
let x = [];
for(let i = 0; i < array.length; i++){
for(let j = 0; j < array.length; j++){
if(array[i] + j == array[j]){
x.push(array[i] + "-" + array[j]);
}
if(array[j] > array[i] + j && array[j + 1]){
let y = array.slice(j, array.length)
array = y;
i, j = 0;
}
if(array[i] - array[i + 1] != -1 && array[i + 1] - array[i] != 1 && array[i + 1] != undefined){
x.push(array[i]);
}
}
}
console.log(x);
The phrasing of the question makes this somewhat difficult to answer, but based on your code snippet I can gather that you are either:
Attempting to find the range of the entire array OR
Attempting to find contiguous ranges within the array
Based on these interpretations, you could answer this question as follows:
function detectRange(a) {
// clone a
const b = [...a]
// remove first value
const min = max = b.splice(0, 1)[0]
// compute range
const range = b.reduce(({min, max}, i) => {
if(i < min) min = i
if(i > max) max = i
return { min, max }
}, {min, max})
return range
}
function detectRanges(a) {
// clone a
const b = [...a]
// remove first value
const min = max = b.splice(0, 1)[0]
// init ranges array
const ranges = [ ]
// compute ranges
const range = b.reduce(({min, max}, i) => {
if(i === max + 1) {
return {min , max: i}
} else {
ranges.push({min, max})
return {min: i, max: i}
}
}, {min, max})
// push the remaining range onto the array
ranges.push(range)
return ranges
}
function printRange(r) {
console.log(`["${r.min}-${r.max}"]`)
}
function printRanges(r) {
r.forEach(i => {
printRange(i)
})
}
// detect and print range of whole array
printRange(detectRange([1, 2, 3, 5, 6, 7, 8, 10, 11, 34, 56, 57]))
// detect and print only contiguous ranges within array
printRanges(detectRanges([1, 2, 3, 5, 6, 7, 8, 10, 11, 34, 56, 57]))
If you assume that the list is sorted, we only need to traverse the list sequentially. There's no need to have double-nested loops. If you maintain sufficient states, you can determine whether you are in a group and you merely manage the start versus the last element in the group.
To simplify things I made use of ES6 string interpolation ${start}-${last}.
let array = [1,2,3,5,6,7,8,10,11, 34, 56,57];
let result = [ ];
let hasStart = false;
let start = 0;
let last = 0;
for (let num of array) {
if (!hasStart) {
hasStart = true;
last = start = num;
continue;
}
if (num === last + 1) {
last = num;
continue;
}
result.push( start === last ? start : `${start}-${last}` );
last = start = num;
}
if (hasStart) {
result.push( start === last ? start : `${start}-${last}` );
}
console.log(result);
Input: [1,2,3,4,5]
Output: ["1-5"]
So I assume you want to get string in format:
["smallestelement-largestelement"]
var input1 = [1,2,3,4,5]
console.log( "["+'"'+Math.min(...input1)+"-"+Math.max(...input1)+'"'+"]")
If what you want is string in format:
["firstelement-lastelement"]
var input1 = [1,2,3,4,5]
console.log( "["+'"'+input1[0]+"-"+input1.pop()+'"'+"]")
If you have an integer array, and if you want to output the range, you could natively sort() it (you can also provide rules for sorting) and use shift() for the first element and slice(-1) for the last:
let arr = [4,1,5,3].sort();
console.log(arr.shift()+'-'+arr.slice(-1));
As said in the comments, you should clarify if you wish "1-57" for the snippet array, or describe your use case more broadly.
const array = [1, 2, 3, 5, 6, 7, 8, 10, 11, 34, 56, 57];
let s = null;
const result = array.sort((a, b) => a - b).reduce((p, c, i, arr) => {
if (!s) s = c;
if (c + 1 !== arr[i + 1]) {
p.push(s === c ? s : `${s}-${c}`);
s = null;
}
return p
}, [])
console.log(result);

What is the efficient way to find an array inside an array?

Find an Array of Values Inside an Array
Lets say I have an array [1,2,3,8,2,3,4,5,6,7,8] and I want to find the first occurrence of the values [3,4,5,6] together, how might I do that? I can use Array.prototype.findIndex, but when I am looking for a large amount of values in a large array, it doesn't feel like the proper way to do it.
What fails:
let largeArray = [1,2,3,8,2,3,4,5,6,7,8];
let smallArray = [3,4,5,6];
//Problem: an array isn't a function
largeArray.findIndex(smallArray);
/*
Problem: always returns -1 because it checks each element
rather than looking for a group of elements.
*/
largeArray.indexOf(smallArray);
//Problem: same as using indexOf
largeArray.findIndex(item=>smallArray);
What works:
let largeArray = [1,2,3,8,2,3,4,5,6,7,8];
let smallArray = [3,4,5,6];
//Here is what works, but isn't ideal
largeArray.findIndex((item, index, arr) => {
let isTheOne = item == smallArray[0] &&
arr[index + 1] == smallArray[1] &&
arr[index + 2] == smallArray[2] &&
arr[index + 3] == smallArray[3];
return isTheOne;
});
//It returns 5, which is correct.
To Be Continued
I am currently using what works, but what if largeArray had the length of a million and smallArray had the length of 300. That would be 1 line of item == smallArray[0] &&, 298 lines of arr[index + x] == smallArray[x] &&, and 1 line of arr[index + x] == smallArray[x];. I don't want to use Array.prototype.map, Array.prototype.filter, Array.prototype.forEach, a for loop, or a while loop. This is because Array.prototype.map, Array.prototype.forEach, and the loops take a very long time to complete. I don't want to use Array.prototype.filter because that doesn't give me the index.
You were on the right track, you just want to use every() to look over the small index to check that each index matches
const largeArray = [1, 2, 3, 8, 2, 3, 4, 5, 6, 7, 8];
let smallArray = [3, 4, 5, 6];
const index = largeArray.findIndex(
(item, index, arr) =>
smallArray.every(
(n, sIndex) => n === arr[index + sIndex]
)
);
console.log(index);
You could add a check beforehand to not have to go in every... not sure what that would improve.
const index = largeArray.findIndex(
(item, index, arr) =>
item === smallArray[0] &&
smallArray.every(
(n, sIndex) => n === arr[index + sIndex]
)
);
Other approach is using strings
const largeArray = [1, 2, 3, 8, 2, 3, 4, 5, 6, 7, 8];
const smallArray = [3, 4, 5, 6];
const largeStr = largeArray.join(",");
const smallStr = smallArray.join(",");
const strIndex = largeStr.indexOf(smallStr);
const index = strIndex > -1 ? largeStr.substr(0,strIndex-1).split(",").length : -1;
console.log(index)
To figure out what is better is really based on your use case.
You can use .join to convert the arrays to strings, and use .indexOf to get the index given that you will remove the additional commas:
const getIndexOfSubArray = (arr=[], sub=[]) => {
const str = arr.join();
const subStr = sub.join();
const index = str.indexOf(subStr);
return index < 0 ? -1 : str.substr(0, index-1).split(',').length;
}
console.log( getIndexOfSubArray([1,2,3,8,2,3,4,5,6,7,8], [3,4,5,6]) );
You could iterate by hand and check the items with indexOf.
function getIndex(array, subarray) {
let p = -1,
first = subarray[0];
while ((p = array.indexOf(first, p + 1)) !== -1) {
let i = p,
complete = true;
for (const s of subarray) {
if (s !== array[i++]) {
complete = false;
break;
}
}
if (complete) return p;
}
return -1;
}
console.log(getIndex([1, 2, 3, 8, 2, 3, 4, 5, 6, 7, 8], [3, 4, 5, 6])); // 5
Here is a simple approach to this problem:
let largeArray = [1, 2, 3, 8, 2, 3, 4, 5, 6, 7, 8];
let smallArray = [3, 4, 5, 6];
let s = 0,
i = 0,
j = 0;
let SLen = smallArray.length,
LLen = largeArray.length;
while (i < LLen && j < SLen && SLen - j <= LLen - i) {
if (j == 0) {
s = i;
}
if (largeArray[i] == smallArray[j]) {
j++;
} else {
j = 0;
i = s;
}
i++;
}
let index = i - j;
if (j == SLen) {
console.log(`found at index ${index}`);
} else {
console.log('not found');
}

Reduce over a single array summing slices

I have an array of values
let a = [1,2,3,4,5,6];
I want to sum specific slices, for example a.[0] + a.[1] giving a new array:
[1 + 2, 3 + 4, 5 + 6]
Is there a recommended way to do this with reduce() or other method? Such as some kind of stepping/range parameter?
Because I want #T.J. Crowder to be right :)
const a = [1, 2, 3, 4, 5, 6];
// Loop over all values of the array
const res = a.reduce((tmp, x, xi) => {
// Use Math.floor and xi (the index of the value we are treating)
// to store the values on the returned array at the correct position
tmp[Math.floor(xi / 2)] = (tmp[Math.floor(xi / 2)] || 0) + x;
return tmp;
}, []);
console.log(res);
Will also work if the number of element is not pair
const a = [1, 2, 3, 4, 5];
const res = a.reduce((tmp, x, xi) => {
tmp[Math.floor(xi / 2)] = (tmp[Math.floor(xi / 2)] || 0) + x;
return tmp;
}, []);
console.log(res);
Alternative solution :
const a = [1, 2, 3, 4, 5, 6];
const res = [];
do {
res.push(a.splice(0, 2).reduce((tmp, x) => tmp +x, 0));
} while (a.length);
console.log(res);
You can do this with reduce, but it's not the right tool for the job. Here's how, keying off index and passing an array around:
let array = [1,2,3,4,5,6];
let result = array.reduce((a, v, i) => {
if (i % 2 == 1) {
// It's an odd entry, so sum it with the
// previous entry and push to the result array
a.push(v + array[i - 1]);
}
return a;
}, []);
console.log(result);
You can squash that into a concise arrow function, at the expense of clarity:
let array = [1,2,3,4,5,6];
let result = array.reduce((a, v, i) => ((i % 2 === 1 ? a.push(v + array[i - 1]) : 0), a), []);
console.log(result);
A simple for loop would probably be more appropriate, though:
let array = [1,2,3,4,5,6];
let result = [];
for (let n = 0; n < array.length; n += 2) {
result.push(array[n] + array[n + 1]);
}
console.log(result);
Another approach with Array#flatMap and taking only the odd indices for a value.
var array = [1, 2, 3, 4, 5, 6],
result = array.flatMap((v, i, { [i + 1]: w = 0 }) => i % 2 ? [] : v + w);
console.log(result);
A simple and quick solution with [Array.prototype.reduce] can look like this:
const array = [1,2,3,4,5,6];
const range = 2;
const result = array.reduce((all, item, i) => {
const idx = Math.floor(i/range);
if (!all[idx]) all[idx] = 0;
all[idx] += item;
return all;
},[]);
console.log(result);

How to write the code with less time complexity for finding the missing element in given array range?

My function should return the missing element in a given array range.
So i first sorted the array and checked if the difference between i and i+1 is not equal to 1, i'm returning the missing element.
// Given an array A such that:
// A[0] = 2
// A[1] = 3
// A[2] = 1
// A[3] = 5
// the function should return 4, as it is the missing element.
function solution(A) {
A.sort((a,b) => {
return b<a;
})
var len = A.length;
var missing;
for( var i = 0; i< len; i++){
if( A[i+1] - A[i] >1){
missing = A[i]+1;
}
}
return missing;
}
I did like above, but how to write it more efficiently??
You could use a single loop approach by using a set for missing values.
In the loop, delete each number from the missing set.
If a new minimum value is found, all numbers who are missing are added to the set of missing numbers, except the minimum, as well as for a new maximum numbers.
The missing numbers set contains at the end the result.
function getMissing(array) {
var min = array[0],
max = array[0],
missing = new Set;
array.forEach(v => {
if (missing.delete(v)) return; // if value found for delete return
if (v < min) while (v < --min) missing.add(min); // add missing min values
if (v > max) while (v > ++max) missing.add(max); // add missing max values
});
return missing.values().next().value; // take the first missing value
}
console.log(getMissing([2, 3, 1, 5]));
console.log(getMissing([2, 3, 1, 5, 4, 6, 7, 9, 10]));
console.log(getMissing([3, 4, 5, 6, 8]));
Well, from the question (as it's supposed to return a single number) and all the existing solution (examples at least), it looks like list is unique. For that case I think we can sumthe entire array and then subtracting with the expected sum between those numbers will generate the output.
sum of the N natural numbers
1 + 2 + ....... + i + ... + n we can evaluate by n * (n+1) / 2
now assume, in our array min is i and max is n
so to evaluate i + (i+1) + ..... + n we can
A = 1 + 2 + ..... + (i-1) + i + (i+1) + .... n (i.e. n*(n+1)/2)
B = 1 + 2 + ..... + (i-1)
and
C = A - B will give us the sum of (i + (i+1) + ... + n)
Now, we can iterate the array once and evaluate the actual sum (assume D), and C - D will give us the missing number.
Let's create the same with each step at first (not optimal for performance, but more readable) then we will try to do in a single iteration
let input1 = [2, 3, 1, 5],
input2 = [2, 3, 1, 5, 4, 6, 7, 9, 10],
input3 = [3, 4, 5, 6, 8];
let sumNatural = n => n * (n + 1) / 2;
function findMissing(array) {
let min = Math.min(...array),
max = Math.max(...array),
sum = array.reduce((a,b) => a+b),
expectedSum = sumNatural(max) - sumNatural(min - 1);
return expectedSum - sum;
}
console.log('Missing in Input1: ', findMissing(input1));
console.log('Missing in Input2: ', findMissing(input2));
console.log('Missing in Input3: ', findMissing(input3));
Now, lets try doing all in a single iteration (as we were iterating 3 times for max, min and sum)
let input1 = [2, 3, 1, 5],
input2 = [2, 3, 1, 5, 4, 6, 7, 9, 10],
input3 = [3, 4, 5, 6, 8];
let sumNatural = n => n * (n + 1) / 2;
function findMissing(array) {
let min = array[0],
max = min,
sum = min,
expectedSum;
// assuming the array length will be minimum 2
// in order to have a missing number
for(let idx = 1;idx < array.length; idx++) {
let each = array[idx];
min = Math.min(each, min); // or each < min ? each : min;
max = Math.max(each, max); // or each > max ? each : max;
sum+=each;
}
expectedSum = sumNatural(max) - sumNatural(min - 1);
return expectedSum - sum;
}
console.log('Missing in Input1: ', findMissing(input1));
console.log('Missing in Input2: ', findMissing(input2));
console.log('Missing in Input3: ', findMissing(input3));
Instead of sorting, you could put each value into a Set, find the minimum, and then iterate starting from the minimum, checking if the set has the number in question, O(N). (Sets have guaranteed O(1) lookup time)
const input1 = [2, 3, 1, 5];
const input2 = [2, 3, 1, 5, 4, 6, 7, 9, 10];
const input3 = [3, 4, 5, 6, 8];
function findMissing(arr) {
const min = Math.min(...arr);
const set = new Set(arr);
return Array.from(
{ length: set.size },
(_, i) => i + min
).find(numToFind => !set.has(numToFind));
}
console.log(findMissing(input1));
console.log(findMissing(input2));
console.log(findMissing(input3));
If the array is items and the difference between missing and present diff is 1:
const missingItem = items => [Math.min(...items)].map(min => items.filter(x =>
items.indexOf(x-diff) === -1 && x !== min)[0]-diff)[0]
would give complexity of O(n^2).
It translates to: find the minimum value and check if there isn't a n-diff value member for every value n in the array, which is also not the minimum value. It should be true for any missing items of size diff.
To find more than 1 missing element:
([Math.min(...items)].map(min => items.filter(x =>
items.indexOf(x-diff) === -1 && x !== min))[0]).map(x => x-diff)
would give O((m^2)(n^2)) where m is the number of missing members.
Found this old question and wanted to take a stab at it. I had a similar thought to https://stackoverflow.com/users/2398764/koushik-chatterjee in that I think you can optimize this by knowing that there's always only going to be one missing element. Using similar methodology but not using a max will result in this:
function getMissing(arr) {
var sum = arr.reduce((a, b) => a + b, 0);
var lowest = Math.min(...arr);
var realSum = (arr.length) * (arr.length + 1) / 2 + lowest * arr.length;
return realSum - sum + lowest;
}
With the same inputs as above. I ran it in jsperf on a few browsers and it is faster then the other answers.
https://jsperf.com/do-calculation-instead-of-adding-or-removing.
First sum everything, then calculate the lowest and calculate what the sum would be for integers if that happened to be the lowest. So for instance if we have 2,3,4,5 and want to sum them that's the same as summing 0,1,2,3 and then adding the lowest number + the amount of numbers in this case 2 * 4 since (0+2),(1+2),(2+2),(3+2) turns it back into the original. After that we can calculate the difference but then have to increase it once again by the lowest. To offset the shift we did.
You can use while loop as well, like below -
function getMissing(array) {
var tempMin = Math.min(...array);
var tempMax = tempMin + array.length;
var missingNumber = 0;
while(tempMin <= tempMax){
if(array.indexOf(tempMin) === -1) {
missingNumber = tempMin;
}
tempMin++;
}
return missingNumber;
}
console.log(getMissing([2, 3, 1, 5]));
console.log(getMissing([2, 3, 1, 5, 4, 6, 7, 9, 10]));
console.log(getMissing([3, 4, 5, 6, 8]));
My approach is based on in place sorting of the array which is O(N) and without using any other data structure.
Find the min element in the array.
Sort in place.
Again loop the array and check if any element is misplaced, that is the answer!
function getMissing(ar) {
var mn = Math.min(...ar);
var size = ar.length;
for(let i=0;i<size;i++){
let cur = ar[i];
// this ensures each element is in its right place
while(cur != mn +i && (cur - mn < size) && cur != ar[cur- mn]) {
// swap
var tmp = cur;
ar[i] = ar[cur-mn];
ar[cur-mn] = tmp;
}
}
for(let i=0; i<size; i++) {
if(ar[i] != i+mn) return i+mn;
}
}
console.log(getMissing([2, 3, 1, 5]));
console.log(getMissing([2, 3, 1, 5, 4, 6, 7, 9, 10]));
console.log(getMissing([3, 4, 5, 6, 8]));

JS: Replace null value in array with an average of the previous and next value

If I have the following array
[5,2,null,5,9,4]
I need to replace null with the average of the previous and next values (2 and 5). Like so:
[5,2,3.5,5,9,4]
Additionally, if I had an array with consecutive null values:
[5,2,null,null,9,4]
I would like to be able to replace them with the step values between the 2 and 9. Like so:
[5,2,4.33,6.63,9,4]
Edit: Guys, totally not a homework assignment. I'm trying to do some stuff with the Chartist-js library.
Here's what I've got that works to replace null with the values before and after.
var seriesData = [5,2,null,5,null,4]
seriesData.forEach(function(value, index){
if ( value === null ) {
var prev = seriesData[index - 1];
var next = seriesData[index + 1];
seriesData[index] = (prev + next) / 2;
}
});
=> [5, 2, 3.5, 5, 4.5, 4]
This does sound like a Chart problem, so here's a crude logic, you can easily improve the code:
function test(){
var arr = [5,3,2,null,null,9,4, null, null, null, 10, null, 12],
ind = -1,
prevIndex,
nJumps, valJump;
for( var currIndex = 0; currIndex< arr.length; currIndex++ ){
if( arr[ currIndex] == null ){
continue;
}else if( prevIndex === undefined || (nJumps = (currIndex- prevIndex)) === 1 ){
prevIndex = currIndex;
continue;
}
valJump = (( arr[ currIndex ] - arr[ prevIndex ]) / nJumps);
ind = (+prevIndex);
while( ++ind < (+currIndex) )
arr[ ind ] = (arr[ ind-1 ]) + valJump;
prevIndex = currIndex;
}
return arr;
}
This solves your first problem:
var arr = [5, 2, null, 5, 9, 4];
var result = arr
.map(function (x) {
if (x == null) {
return (arr[arr.indexOf(x) - 1] + arr[arr.indexOf(x) + 1]) / 2 ;
}
return x;
});
console.log(result); // [5, 2, 3.5, 5, 9, 4]
For the second problem I don't quite understand what you mean with the two steps stuff.

Categories

Resources