I am trying to get 5 closest elements to given element, including that element, in array. For example, if we have:
const arr = [1, 2, 3, 4, 7, 11, 12, 13, 15, 17]
and I want to get 5 closest elements to 11, it should return [4, 7, 11, 12, 13]. If i pass 1 it should return [1, 2, 3, 4, 7]. If I pass 15 it should return [11, 12, 13, 15, 17].
I'm not sure what you meant;
You might've meant a code to find the element and return the five nearest elements to it by place in the array;
Or you might've meant a code to find the 5 numbers closest to a number you say.
IF you meant the first case
There are two ways to do so,
A value as a parameter
Use this code:
function closestNByValue(arr, value, n) {
let ind = arr.indexOf(value);
let finalArr = [];
if (n > arr.length) {
finalArr = Array.from(arr);
} else if (ind == -1) {
finalArr = [];
} else if (ind <= n/2 - 0.5) {
finalArr = arr.slice(0, n);
} else if (ind >= (arr.length - n/2) - 0.5) {
finalArr = arr.slice(-n);
} else if (n%2 == 0) {
finalArr = arr.slice(ind-(n/2), ind+(n/2));
} else {
finalArr = arr.slice(ind-(n/2 - 0.5), ind+(n/2 + 0.5));
}
return finalArr;
}
console.log(closestNByValue([1, 2, 3, 4, 7, 11, 12, 13, 15, 17], 11, 5))
How does it do the job?
Okay first we need to find the index of the value and save it in ind (short form of 'index') and we check multiple different situations for what the ind is so we'd be able to output the best answer as finalArr.
There are two exceptions; what if there was no such value in our array? then ind = -1 and we'd return an empty array; or what if the number of elements nearby that we seek is larger than or equal to the arr.length? then we'd return all of the arr.
But if there were no exceptions, there are three different situations for the ind; first, ind is a number that makes us have all of the finalArr values from the first of arr, second, ind be a number that makes us have all of the finalArr values from the last of arr, and third, ind being a number that we have to select half from the indexes smaller than ind and half, larger.
If it is the third way, the way we select we'd be different depending on the oddity of the numbers we want to select.
And we'll have a conditional statement for each situation and return the finalArr.
An index as a parameter
function closestNByIndex(arr, ind, n) {
let finalArr = [];
if (n > arr.length) {
finalArr = Array.from(arr);
} else if (ind == -1) {
finalArr = [];
} else if (ind <= n/2 - 0.5) {
finalArr = arr.slice(0, n);
} else if (ind >= (arr.length - n/2) - 0.5) {
finalArr = arr.slice(-n);
} else if (n%2 == 0) {
finalArr = arr.slice(ind-(n/2), ind+(n/2));
} else {
finalArr = arr.slice(ind-(n/2 - 0.5), ind+(n/2 + 0.5));
}
return finalArr;
}
console.log(closestNByIndex([1, 2, 3, 4, 7, 11, 12, 13, 15, 17], 5, 5))
Similar to the first code it works, though we have the index and we don't search for it.
The point is, if you use the function with value, it'd do the nearest 5 elements of the first value that equals the entry but such confusion is not being tolerated in the second code.
IF you meant the second case
This is a code I coded:
const arr = [1, 2, 3, 4, 7, 11, 12, 13, 15, 17];
function allDiff(arr, num1, num2) {
const finalArr = [];
const x = Math.abs(num2 - num1);
for (let y = 0; y < arr.length; y++) {
if (Math.abs(arr[y] - num1) == x) {
finalArr.push(arr[y]);
}
}
return finalArr;
}
function deleteArr(arr, delet) {
for (let x = 0; x < arr.length; x++) {
if (delet.includes(arr[x])) {
delete arr[x];
}
}
return arr;
}
function closest(arr, num) {
const map = new Map()
arr2 = Array.from(arr);
let key, value;
for (let x = 0; x < arr2.length; x++) {
key = Math.abs(arr2[x] - num);
value = allDiff(arr2, num, arr2[x]);
arr2 = deleteArr(arr2, value);
map.set(key, value);
}
return map;
}
function closestN(arr, num, n) {
const map = closest(arr, num);
const mapKeys = Array.from(map.keys());
const mapKeysSorted = mapKeys.sort(function(a, b) {
return a - b
});
let finalArr = [];
let y;
for (let i = 0; i < mapKeysSorted.length; i++) {
if (n <= 0) {
break;
}
y = map.get(mapKeysSorted[i]);
if (n < y.length) {
finalArr = finalArr.concat(y.slice(0, n + 1));
break;
}
finalArr = finalArr.concat(y);
n -= y.length;
}
return finalArr;
}
console.log(closestN(arr, 11, 5));
It might be a little too long, but I have programmed it as you can give it any array (arr) with integer values, an integer (num) that you'd like it to be the base and another integer (n) for the number of the size of the output array, 5 in this case.
Explaining the code
The function closest would return a map of (the difference between the numbers, a list of the numbers in the arr that differs the number equal to their key).
The main function, closestN, calls the closest function and saves the map in the map variable.
Then it sorts the keys of the map in mapKeysSorted.
Now, a for loop loops through the mapKeySorted array and pushes new elements to the finalArr until the size of the finalArr reaches the number of elements we seek.
The main function is the closestN.
Here's a way to get to your goal:
To start, first thing to do is finding the index of the wanted number in the array. Example index of 1 in your array arr is 0. The index will help in extracting the numbers later on. The method findIndex will help us in finding the index.
Then, we need to find the position at which will start extaracting the closest numbers (in terms of position not value). As seen from the desired output you have provided, usually you want the returned array to be in the following structure:
output: [
2 nearest numbers (from N left),
the wanted number,
2 nearest numbers (from N right)
]
This can get tricky so we should make sure to deal with some edge case like when the wanted element is sitting at position 0.
Extract the numbers and return them as an array as described by your desired output. The use of slice method will come in handy here which allow us to extract the numbers just as we need.
Here's a live demo demonstrating solution:
const arr = [1, 2, 3, 4, 7, 11, 12, 13, 15, 17],
/** a function that returns an array containing the "5" (depending on "arr" length that could be less) nearest numbers (in terms of position) in "arr" array to the supplied number "n" */
findClosestNumbers = n => {
/** make sure we don't exceed the array length */
const toTake = 5 > arr.length ? arr.length : 5,
/** find the index of the wanted nulber "n", if "-1" is returned then "n" cannot be found ion the array "arr" */
idx = arr.findIndex(el => n == el),
/**
* from where we should start returning the nearest numbers (the position of the first number to extract from "arr"
* the below condition help deal with some edge cases like when "n" is the last element in "arr"
*/
startIdx = idx + toTake / 2 > arr.length ?
arr.length - 5 :
(idx - 2 >= 0 ?
idx - 2 :
0);
/** return the nearest numbers or return an empty array "[]" if the number "n" is not found on the array "arr" */
return idx == -1 ? [] : arr.slice(startIdx, startIdx + 5);
};
/** run for various scenarios */
console.log('For 1 =>', findClosestNumbers(1));
console.log('For 11 =>', findClosestNumbers(11));
console.log('For 15 =>', findClosestNumbers(15));
console.log('For 17 =>', findClosestNumbers(17));
.as-console-wrapper {
max-height: 100%!important;
}
The demo above is meant to help you understand how things could work and it is not the only way to get to your goal. Also, because I kept it as simple as possible, the above demo is wide open for improvements.
I am trying to solve this.
Nominal Case:
For the array[1,2,3,5,2,4,7,54], and the number 6. The sequences[1,2,3] and [4,2] will be removed because the add up to 6. function will return [5,7,54]. If two sequences overlap, remove the first sequence.
Overlapping Case:
For the array [1,2,3,9,4,1,4,6,7] and the number 5, the sequence [2,3,] and [4,1] are removed because they add up to 5. For the [4,1] case. you see that [4,1,4] represents two overlapping sequences. because [4,1] adds up to 5 first is removed and the 4 is not removed even through [1,4] adds up to 5. We say that [4,1] and [1,4] overlap to give [4,1,4] and in those cases the first of the overlapping sequences is removed . functin will return [1,9,4,6,7]
function consecutive(arr, len, num) {
var newarr = [];
for (let i = 1; i < len; i++) {
var sum = arr[i] + arr[i + 1];
if (sum == num) {
newarr.push(arr[i]);
newarr.push(arr[i + 1]);
}
}
return newarr;
}
let arr = [1, 2, 3, 5, 2, 4, 7, 54];
let len = arr.length;
let num = 6;
console.log(consecutive(arr, len, num));
Get Wrong Output
[2,4]
You could store the target index of wrong items and if no one to filter out check the next elements if they sum up to the wanted value.
function consecutive(array, num) {
return array.filter(
(wrong => (v, i, a) => {
if (i <= wrong) return false;
let sum = 0, j = i;
while (j < a.length) {
if ((sum += a[j]) === num) {
wrong = j;
return false;
}
j++;
}
return true;
})
(-1)
);
}
console.log(consecutive([1, 2, 3, 5, 2, 4, 7, 54], 6));
I am attempting to write a "binary search" which I've never done before. The code below does not work when the value searched for is 6 or 2 and I want to know what I am doing wrong and how to remedy it.
EDIT
To explain what it is suppose to do (based on my understanding) a binary search requires that an array is already sorted, it then looks for the mid-point index of an array. For example, if an array had nine indexes (0-8)the the mid point would be index 4.
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
The algorithm then determines if that mid point has a higher or lower value than the number you are searching for. All elements on the side of the array that does not contain the searched for number and that exist before the midpoint value simply get removed. If the search for value is 8 then the result would be:
[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
array midpoint value: 5
[ 5, 6, 7, 8, 9 ]
array midpoint value: 7
[ 7, 8, 9 ]
array midpoint value: 8
Code
//_________________________________________________BEGIN notes
// Step 1. Get length of array
// Step 2. Find mid point
// Step 3. Compare if mid point is lower or higher than searched number
// Step 4. lop off unneeded side
// Step 5. go to step 1
//_________________________________________________END notes
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 44, 55];
function getMidPoint(arr, searchNumb) {
var length = arr.length;
var midPoint = Math.floor(length / 2);
var newArr = arr;
console.log(arr);
console.log("array midpoint value: " + arr[midPoint]);
if (arr[midPoint] > searchNumb) {
var newArr = arr.slice(0, arr[midPoint]);
return getMidPoint(newArr, searchNumb);
} else if (arr[midPoint] < searchNumb) {
var newArr = arr.slice(midPoint, arr.length);
return getMidPoint(newArr, searchNumb);
} else {
return arr
}
}
Language agnostic, here is the simplified flow of a recursive binary search implementation, assuming we have an (initially non-empty) array [ARR] and a target [T], where we refer to the middle element of ARR as M:
// 1. If M == T, return true
// 2. If length of ARR is 0, return false (note: step 1 short circuits, ensuring we only hit step 2 if step 1 evaluates to false)
// 3. If T < M, return the result of the recursion on the lower half of ARR
// 4. If T > M, return the result of the recursion on the the latter half of ARR
Following is solution that executes the control flow outlined above. This is similar to solutions already presented in this post, with a few noteworthy differences:
function binarySearch(arr, target, start=0, stop=(arr.length-1)) {
let midPoint = Math.floor(((stop-start)/2) + start)
switch (true) {
case arr[midPoint] === target:
return true
case stop - start === 0:
return false
case arr[midPoint] < target:
return binarySearch(arr, target, midPoint+1, stop)
case arr[midPoint] > target:
return binarySearch(arr, target, start, midPoint)
}
}
Let's unpack the main differences of this implementation:
Slice is no longer used:
We are eschewing the use of Array.prototype.slice because it is a relatively expensive operation (copying half of the current array with each recursive call!) and it is not required for the algorithm to function properly.
In place of slice, we are passing the start and stop indexes of the range of the array that we have narrowed the search down to. This keeps our heap happy by not cluttering it with (potentially many) partial, impermanent copies of the same (potentially massive) array.
We are passing two additional arguments, and they have defaults:
These arguments (start and stop) serve to keep track of the range of the array we are currently recurring on. They are our alternative to slice!
The default arguments enable us to call this recursive function exactly the same as we would when using slice (should the user not provide an explicit range when it is first called).
We are using a switch statement:
The speed of a switch statement vs. an if-else chain depends on several factors, most notably the programming language and the amount of conditionals in each. A switch statement was used here primarily for readability. It is a control flow that matches what we are concerned with handling in this recursive function: 4 discrete cases, each requiring different action. Additionally, a few individuals have a rare allergy to if-else statements that exceed 3 logical tests.
For more information on JavaScript's switch statement and its performance vs. if-else, please take a look at this post: Javascript switch vs. if...else if...else, which links to this more informative page http://archive.oreilly.com/pub/a/server-administration/excerpts/even-faster-websites/writing-efficient-javascript.html
You are slicing it wrong.
Use this code:
//_________________________________________________BEGIN notes
// Step 1. Get length of array
// Step 2. Find mid point
// Step 3. Compare if mid point is lower or higher than searched number
// Step 4. lop off unneeded side
// Step 5. go to step 1
//_________________________________________________END notes
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 44, 55];
function getMidPoint(arr, searchNumb) {
var length = arr.length;
var midPoint = Math.floor(length / 2);
var newArr = arr;
console.log(arr);
console.log("array midpoint value: " + arr[midPoint]);
if (arr[midPoint] > searchNumb) {
var newArr = arr.slice(0, midPoint);
return getMidPoint(newArr, searchNumb);
} else if (arr[midPoint] < searchNumb) {
var newArr = arr.slice(midPoint + 1, arr.length);
return getMidPoint(newArr, searchNumb);
} else {
return midPoint;
}
}
Also, if the search element is not in array, this will go on infinitely. Add a base case for that too.
I think that this line:
var newArr = arr.slice(0, arr[midPoint]);
should probably be:
var newArr = arr.slice(0, midPoint);
But I don't know if that's the only issue with your code. (It's not clear to me what the code is supposed to actually do. Right now "getMidPoint" appears to returns a smaller array containing the searched-for value.)
Probably You are already a master with Binary search. However I would like to indicate that is not necessary to create a sliding window for resolving a binary search.
function binarySearch(arr, value){
if(!arr.length) return -1;
let average = Math.floor(arr.length-1/2);
if (value === arr[average]) return average;
if (value > arr[average]) return binarySearch(arr.slice(average+1),value);
if (value < arr[average]) return binarySearch(arr.slice(0,average),value);
}
binarySearch([1,2,3,4,5],6) //-1
binarySearch([1,2,3,4,5],3) //2
Follow this steps to create the Binary search with recursion:
function binarySearch(arr, value){
1 ) implement a base case
if(!arr.length) return -1;
2 ) create a middle point
let average = Math.floor(arr.length-1/2);
3 ) if the middle point is equal to the searched valued, you found it! return the value
if (value === arr[average]) return average;
4) if the value is greater than the middle point run a new process with only the sub array starting from the middle + 1 till the end
if (value > arr[average]) return binarySearch(arr.slice(average+1),value);
5) if the value is lower than the middle point run a new process with only the sub array starting from 0 to the middle
if (value < arr[average]) return binarySearch(arr.slice(0,average),value);
}
I hope it helps!
Note: you can use a switch statement in order to not repeat if,if,if but I like it more this way, more readable.
For solving the question in recursion please find the answer and explanation below.
const BinarySearchRec = (arr, el) => {
// finding the middle index
const mid = Math.floor(arr.length / 2);
if (arr[mid] === el) {
// if the element is found then return the element.
return mid;
}
if (arr[mid] < el && mid < arr.length) {
/** here we are having the value returned from recursion as
the value can be -1 as well as a value which is in second half of the original array.**/
const retVal = BinarySearchRec(arr.slice(mid + 1, arr.length), el);
/** if value is greater than or equal to 0 then only add that value with mid
and also one as mid represents the index.
Since index starts from 0 we have to compensate it as we require the length here.**/
return retVal >= 0 ? mid + 1 + retVal : -1;
}
if (arr[mid] > el) {
// here we need not do any manipulation
return BinarySearchRec(arr.slice(0, mid), el);
}
return -1;
};
The above solutions which have been added and the one accepted fails in scenarios when the element to be found is in the second half.
There is solution with while loop which works correctly but since the question was to solve it recursively I have given a comprehensive recursive version.
There are 2 issues in your code :-
1) You are slicing it incorrectly
2) You have not put any base condition
This code should work hopefully :-
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 44, 55];
function getMidPoint(arr, searchNumb) {
var length = arr.length;
var midPoint = Math.floor(length / 2);
var newArr = arr;
console.log(arr);
console.log("array midpoint value: " + arr[midPoint]);
if (arr[midPoint] > searchNumb) {
var newArr = arr.slice(0, midPoint);
return getMidPoint(newArr, searchNumb);
} else if (arr[midPoint] < searchNumb) {
var newArr = arr.slice(midPoint+1, arr.length);
return getMidPoint(newArr, searchNumb);
} else {
return arr[midPoint];
}
}
This function would return undefined if element is not found in array.
This is fully rewritten code to achieve your goal (commented, linted).
This example doesn't have any checks for params.
Main error:
wrong slicing
Disadvantages of this approach:
recursion is slower and takes up more of the stack
slice() also there is no needed (because of the stack again)
/**
* Searches recursively number from the list
* #param {Array} list
* #param {number} item Search item
* #param {number} low Lower limit of search in the list
* #param {number} high Highest limit of search in the list
* #param {number} arrLength Length of the list
* #return {(number | null)} Number if the value is found or NULL otherwise
*/
const binarySearch = ( list, item, low, high, arrLength ) => {
while ( low <= high ) {
let mid = Math.floor((low + high) / 2);
let guess = list[mid];
if ( guess === item ) {
return mid;
} else if ( guess > item ) {
high = mid - 1;
list = list.slice( 0, mid );
return binarySearch( list, item, low, high );
} else {
low = mid + 1;
list = list.slice( low, arrLength );
return binarySearch( list, item, low, high );
}
}
return null;
};
/**
* Creates the array that contains numbers 1...N
* #param {number} n - number N
* #return {Array}
*/
const createArr = ( n ) => Array.from({length: n}, (v, k) => k + 1);
const myList = createArr( 100 );
const arrLength = myList.length;
let low = 0;
let high = arrLength - 1;
console.log( '3 ' + binarySearch( myList, 3, low, high, arrLength ) ); // 2
console.log( '-1 ' + binarySearch( myList, -1, low, high, arrLength ) ); // null
I think it's more elegant solution for binary search:
const binarySearch = ( list, item ) => {
let low = 0;
let high = list.length - 1;
while ( low <= high ) {
let mid = Math.floor((low + high) / 2);
let guess = list[mid];
if ( guess === item ) {
return mid;
} else if ( guess > item ) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return null;
};
const myList = [1, 3, 5, 7, 9];
console.log( binarySearch( myList, 3 ) );
console.log( binarySearch( myList, -1 ) );
Here's my recursive binary search solution:
// arr = sorted array, val = search value
// left and right are the index pointers enclosing the search value
// e.g. binarySearch([1,5,7,9,14,17,24,29,33,38,49,52,61,62,70,80,90,95,104,107,109],70)
binarySearch = (arr,val,left=0,right=arr.length) => {
position = (left,right) => {
let pos = (left + right)/2
return Math.floor(pos)
}
let i = position(left,right)
if (arr[i] === val) {
return i
}
// Base Case: if left and midpoint index coincide then there are no more possible solutions
else if (i === left) {
return -1
}
// For this case we shift the left index pointer
else if (arr[i] < val) {
return binarySearch(arr,val,i,right)
}
// For this case we shift the right index pointer
else if (arr[i] > val) {
return binarySearch(arr,val,left,i)
}
}
Here is my approach for binary search recursively.
We don't slice the array because it is not needed if we can just pass down the indexes.
I think that will save some time.
Function will return index if the element is found and -1 if not.
l is standing for left, r is standing for right.
function binarySearch(arr, searchNumber) {
return _binarySearch(0, arr.length -1, arr, searchNumber);
function _binarySearch(l, r, arr, searchNumber) {
const mid = Math.floor((l + r) / 2);
const guess = arr[mid];
if (guess === searchNumber) { // base case
return mid;
} else if (l === r) { // end-case the element is not in the array
return -1;
} else if (guess < searchNumber) {
return _binarySearch(mid + 1, arr.length - 1, arr, searchNumber);
} else if (guess > searchNumber) {
return _binarySearch(l, mid - 1, arr, searchNumber);
}
}
}
const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(binarySearch(list, 4));
Simple and Easy
let arr = [1,2,3,4,5];
function BinarySearch(arr, start, end, key) {
if(start > end) return -1;
let mid = Math.floor((start + end) / 2);
if(arr[mid] === key) return mid;
if(key > arr[mid]) {
return BinarySearch(arr, mid + 1, end, key);
} else if(key < arr[mid]) {
return BinarySearch(arr, start, mid -1, key);
}
}
BinarySearch([1,3,4,5], 0, arr.length - 1, 1); // it will return 0;
BinarySearch recursion Returning search element index.
Below code worked for me
function binerySearchRecursive(arr, num, start=0 end=arr.length-1){
let mid = Math.floor((start+end/2));
if(start> end){
return -1; // edge case if array has 1 element or 0
}
if(num === arr[mid])
return mid;
else if(num < arr[mid])
return binerySearchRecursive(arr, num, start, mid-1 );
else
return binerySearchRecursive(arr, num, mid+1 , end);
}
binerySearchRecursive([1,2,3,4,5], 5)
function binarySearch(arr, n) {
let mid = Math.floor(arr.length / 2);
// Base case
if (n === arr[mid]) {
return mid;
}
//Recursion
if (n > arr[mid]) {
return mid + binarySearch(arr.slice(mid, arr.length), n)
} else {
return binarySearch(arr.slice(0, mid), n)
} }
Simple solution to recursive binary search
For a recursive binary search you can try this :
function recursiveBinarySearch(lst, target, start=0, end=(lst.length-1)){
let midPoint = (Math.floor((start+end)/2));
if (start > end){
return false;
}
if (lst[midPoint] === target){
return true;
}
else{
if(lst[midPoint] < target){
return recursiveBinarySearch(lst, target, midPoint+1, end);
}
else{
return recursiveBinarySearch(lst, target, start, midPoint-1);
}
}
}
this too late but i hope this well be useful for some one :)
const items = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
let target = 30;
function binarySearch(L,R){
if(L == R){
return false;
}
let mid = Math.floor((L + R)/2);
if(mid == target){
return target;
}
if(mid > target){
binarySearch(L,mid);
}
if(mid < target){
binarySearch(mid+1,R);
}
}
binarySearch(1,items.length);
This is the most comprehensive version of binary recursive search for JavaScript. In my opinion, this is O(log n).
function binaryRecursion(arr, val) {
if (arr.length === 0) return -1
let middle = Math.floor(arr.length - 1 / 2)
if (arr[middle] === val) return middle;
if (val > arr[middle]) {
return binaryRecursion(arr.slice(middle + 1), val)
}
if (val < arr[middle]) {
return binaryRecursion(arr.slice(0, middle), val)
}
}
This returns the index of the element, not whether it exists or not.