Related
Example: I have an array like this: [0,22,56,74,89] and I want to find the closest number downward to a different number. Let's say that the number is 72, and in this case, the closest number down in the array is 56, so we return that. If the number is 100, then it's bigger than the biggest number in the array, so we return the biggest number. If the number is 22, then it's an exact match, just return that. The given number can never go under 0, and the array is always sorted.
I did see this question but it returns the closest number to whichever is closer either upward or downward. I must have the closest one downward returned, no matter what.
How do I start? What logic should I use?
Preferably without too much looping, since my code is run every second, and it's CPU intensive enough already.
You can use a binary search for that value. Adapted from this answer:
function index(arr, compare) { // binary search, with custom compare function
var l = 0,
r = arr.length - 1;
while (l <= r) {
var m = l + ((r - l) >> 1);
var comp = compare(arr[m]);
if (comp < 0) // arr[m] comes before the element
l = m + 1;
else if (comp > 0) // arr[m] comes after the element
r = m - 1;
else // arr[m] equals the element
return m;
}
return l-1; // return the index of the next left item
// usually you would just return -1 in case nothing is found
}
var arr = [0,22,56,74,89];
var i=index(arr, function(x){return x-72;}); // compare against 72
console.log(arr[i]);
Btw: Here is a quick performance test (adapting the one from #Simon) which clearly shows the advantages of binary search.
var theArray = [0,22,56,74,89];
var goal = 56;
var closest = null;
$.each(theArray, function(){
if (this <= goal && (closest == null || (goal - this) < (goal - closest))) {
closest = this;
}
});
alert(closest);
jsFiddle http://jsfiddle.net/UCUJY/1/
Array.prototype.getClosestDown = function(find) {
function getMedian(low, high) {
return (low + ((high - low) >> 1));
}
var low = 0, high = this.length - 1, i;
while (low <= high) {
i = getMedian(low,high);
if (this[i] == find) {
return this[i];
}
if (this[i] > find) {
high = i - 1;
}
else {
low = i + 1;
}
}
return this[Math.max(0, low-1)];
}
alert([0,22,56,74,89].getClosestDown(75));
Here's a solution without jQuery for more effiency. Works if the array is always sorted, which can easily be covered anyway:
var test = 72,
arr = [0,56,22,89,74].sort(); // just sort it generally if not sure about input, not really time consuming
function getClosestDown(test, arr) {
var num = result = 0;
for(var i = 0; i < arr.length; i++) {
num = arr[i];
if(num <= test) { result = num; }
}
return result;
}
Logic: Start from the smallest number and just set result as long as the current number is smaller than or equal the testing unit.
Note: Just made a little performance test out of curiosity :). Trimmed my code down to the essential part without declaring a function.
Here's an ES6 version using reduce, which OP references. Inspired by this answer get closest number out of array
lookup array is always sorted so this works.
const nearestBelow = (input, lookup) => lookup.reduce((prev, curr) => input >= curr ? curr : prev);
const counts = [0,22,56,74,89];
const goal = 72;
nearestBelow(goal, counts); // result is 56.
Not as fast as binary search (by a long way) but better than both loop and jQuery grep https://jsperf.com/test-a-closest-number-function/7
As we know the array is sorted, I'd push everything that asserts as less than our given value into a temporary array then return a pop of that.
var getClosest = function (num, array) {
var temp = [],
count = 0,
length = a.length;
for (count; count < length; count += 1) {
if (a[count] <= num) {
temp.push(a[count]);
} else {
break;
}
}
return temp.pop();
}
getClosest(23, [0,22,56,74,89]);
Here is edited from #Simon.
it compare closest number before and after it.
var test = 24,
arr = [76,56,22,89,74].sort(); // just sort it generally if not sure about input, not really time consuming
function getClosest(test, arr) {
var num = result = 0;
var flag = 0;
for(var i = 0; i < arr.length; i++) {
num = arr[i];
if(num < test) {
result = num;
flag = 1;
}else if (num == test) {
result = num;
break;
}else if (flag == 1) {
if ((num - test) < (Math.abs(arr[i-1] - test))){
result = num;
}
break;
}else{
break;
}
}
return result;
}
I have an issue with a recursive algorithm, that solves the problem of finding the happy numbers.
Here is the code:
function TestingFunction(number){
sumNumberContainer = new Array(0);
CheckIfNumberIsHappy(number);
}
function CheckIfNumberIsHappy(number){
var sumOfTheNumbers = 0;
for (var i = 0; i < number.length; i++) {
sumOfTheNumbers += Math.pow(parseInt(number[i]), 2);
}
console.log(sumOfTheNumbers);
if(sumOfTheNumbers == 1){
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
//return true;
} else {
sumNumberContainer.push(sumOfTheNumbers);
if(sumNumberContainer.length > 1){
for (var i = 0; i < sumNumberContainer.length - 1; i++) {
for (var j = i + 1; j < sumNumberContainer.length; j++) {
if(sumNumberContainer[i] == sumNumberContainer[j]){
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
//return false;
}
}
}
}
CheckIfNumberIsHappy(sumOfTheNumbers.toString());
}
}
Algorithm is working ALMOST fine. I've tested it out by calling function with different numbers, and console was displaying correct results. The problem is that I almost can't get any value from the function. There are only few cases in which I can get any value: If the number is build out of ,,0", and ,,1", for example 1000.
Because of that, I figured out, that I have problem with returning any value when the function is calling itself again.
Now I ended up with 2 results:
Returning the
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
which is giving an infinity looped number. For example when the number is happy, the function is printing in the console number one again and again...
Returning the
//return true
or
//return false
which gives me an undefined value
I'm a little bit in check by this problem, and I'm begging you guys for help.
I would take a step back and reexamine your problem with recursion in mind. The first thing you should think about with recursion is your edge cases — when can you just return a value without recursing. For happy numbers, that's the easy case where the sum of squares === 1 and the harder case where there's a cycle. So test for those and return appropriately. Only after that do you need to recurse. It can then be pretty simple:
function sumSq(num) {
/* simple helper for sums of squares */
return num.toString().split('').reduce((a, c) => c * c + a, 0)
}
function isHappy(n, seen = []) {
/* seen array keeps track of previous values so we can detect cycle */
let ss = sumSq(n)
// two edge cases -- just return
if (ss === 1) return true
if (seen.includes(ss)) return false
// not an edge case, save the value to seen, and recurse.
seen.push(ss)
return isHappy(ss, seen)
}
console.log(isHappy(23))
console.log(isHappy(22))
console.log(isHappy(7839))
Here's a simplified approach to the problem
const digits = x =>
x < 10
? [ x ]
: [ ...digits (x / 10 >> 0), x % 10 ]
const sumSquares = xs =>
xs.reduce ((acc, x) => acc + x * x, 0)
const isHappy = (x, seen = new Set) =>
x === 1
? true
: seen.has (x)
? false
: isHappy ( sumSquares (digits (x))
, seen.add (x)
)
for (let n = 1; n < 100; n = n + 1)
if (isHappy (n))
console.log ("happy", n)
// happy 1
// happy 7
// happy 10
// ...
// happy 97
The program above could be improved by using a technique called memoization
Your code is almost correct. You just forgot to return the result of the recursive call:
function TestingFunction(number){
sumNumberContainer = new Array(0);
if (CheckIfNumberIsHappy(number))
console.log(number);
}
function CheckIfNumberIsHappy(number){
var sumOfTheNumbers = 0;
for (var i = 0; i < number.length; i++) {
sumOfTheNumbers += Math.pow(parseInt(number[i]), 2);
}
if(sumOfTheNumbers == 1){
return true;
} else {
sumNumberContainer.push(sumOfTheNumbers);
if(sumNumberContainer.length > 1){
for (var i = 0; i < sumNumberContainer.length - 1; i++) {
for (var j = i + 1; j < sumNumberContainer.length; j++) {
if(sumNumberContainer[i] == sumNumberContainer[j]){
return false;
}
}
}
}
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
}
}
for (let i=0; i<100; ++i)
TestingFunction(i.toString()); // 1 7 10 13 ... 91 94 97
I've got the solution, which was given to me in the comments, by the user: Mark_M.
I just had to use my previous
return true / return false
also I had to return the recursive statement in the function, and return the value of the CheckIfTheNumberIsHappy function, which was called in TestingFunction.
The working code:
function TestingFunction(number){
sumNumberContainer = new Array(0);
return CheckIfNumberIsHappy(number);
}
function CheckIfNumberIsHappy(number){
var sumOfTheNumbers = 0;
for (var i = 0; i < number.length; i++) {
sumOfTheNumbers += Math.pow(parseInt(number[i]), 2);
}
console.log(sumOfTheNumbers);
if(sumOfTheNumbers == 1){
return true;
} else {
sumNumberContainer.push(sumOfTheNumbers);
if(sumNumberContainer.length > 1){
for (var i = 0; i < sumNumberContainer.length - 1; i++) {
for (var j = i + 1; j < sumNumberContainer.length; j++) {
if(sumNumberContainer[i] == sumNumberContainer[j]){
return false;
}
}
}
}
return CheckIfNumberIsHappy(sumOfTheNumbers.toString());
}
}
Thanks for the great support :)
I have a JavaScript code like so:
var myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
for (var i = 0, di = 1; i >= 0; i += di) {
if (i == myArray.length - 1) { di = -1; }
document.writeln(myArray[i]);
}
I need it to stop right in the middle like 10 and from 10 starts counting down to 0 back.
So far, I've managed to make it work from 0 to 20 and from 20 - 0.
How can I stop it in a middle and start it from there back?
Please help anyone!
Here is an example using a function which accepts the array and the number of items you want to display forwards and backwards:
var myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
if(myArray.length === 1){
ShowXElementsForwardsAndBackwards(myArray, 1);
}
else if(myArray.length === 0) {
//Do nothing as there are no elements in array and dividing 0 by 2 would be undefined
}
else {
ShowXElementsForwardsAndBackwards(myArray, (myArray.length / 2));
}
function ShowXElementsForwardsAndBackwards(mYarray, numberOfItems){
if (numberOfItems >= mYarray.length) {
throw "More Numbers requested than length of array!";
}
for(let x = 0; x < numberOfItems; x++){
document.writeln(mYarray[x]);
}
for(let y = numberOfItems - 1; y >= 0; y--){
document.writeln(mYarray[y]);
}
}
Just divide your array length by 2
var myArray = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
for (var i = 0, di = 1; i >= 0; i += di) {
if (i == ((myArray.length / 2) -1 )) { di = -1; }
document.writeln(myArray[i]);
}
Could Array.reverse() help you in this matter?
const array = [0,1,3,4,5,6,7,8,9,10,11,12,13,14,15]
const getArrayOfAmount = (array, amount) => array.filter((item, index) => index < amount)
let arraySection = getArrayOfAmount(array, 10)
let reversed = [...arraySection].reverse()
console.log(arraySection)
console.log(reversed)
And then you can "do stuff" with each array with watever array manipulation you desire.
Couldn’t you just check if you’ve made it halfway and then subtract your current spot from the length?
for(i = 0; i <= myArray.length; i++){
if( Math.round(i/myArray.length) == 1 ){
document.writeln( myArray[ myArray.length - i] );
} else {
document.writeln( myArray[i] );
}
}
Unless I’m missing something?
You could move the checking into the condition block of the for loop.
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
for (
var i = 0, l = (myArray.length >> 1) - 1, di = 1;
i === l && (di = -1), i >= 0;
i += di
) {
document.writeln(myArray[i]);
}
If you capture the midpoint ( half the length of the array ), just start working your step in the opposite direction.
const N = 20;
let myArray = [...Array(N).keys()];
let midpoint = Math.round(myArray.length/2)
for ( let i=1, step=1; i; i+=step) {
if (i === midpoint)
step *= -1
document.writeln(myArray[i])
}
To make things clearer, I've:
Started the loop iterator variable (i) at 1; this also meant the array has an unused 0 value at 0 index; in other words, myArray[0]==0 that's never shown
Set the the loop terminating condition to i, which means when i==0 the loop will stop because it is falsy
Renamed the di to step, which is more consistent with other terminology
The midpoint uses a Math.round() to ensure it's the highest integer (midpoint) (e.g., 15/2 == 7.5 but you want it to be 8 )
The midpoint is a variable for performance reasons; calculating the midpoint in the loop body is redundant and less efficient since it only needs to be calculated once
For practical purpose, made sizing the array dynamic using N
Updated to ES6/ES7 -- this is now non-Internet Explorer-friendly [it won't work in IE ;)] primarily due to the use of the spread operator (...) ... but that's easily avoidable
I have to create a function that takes 3 numbers. The function should return an array containing the numbers from least to greatest. So far I have this.I know it isn't correct but it's a start.I'm not using native functions as well. Can anyone give me some tips? Appreciate any help.
function leastToGreatest (num) {
var array = [];
var num1 = 0;
var num2 = 0;
var num3 = 0;
for (var i = 0; i < num.length; i++) {
if(num[i] < num[i] && num[i] < num[i]) {
num[i] = num1;
array.push(num1);
}
else if(num[i] > num[i] && num[i] < num[i]) {
num[i] = num2;
array.push(num2);
}
else if(num[i] > num[i] && num[i] > num[i])
num[i] = num3;
array.push(num3);
}
return array;
}
leastToGreatest(2,1,3);
I would suggest using two for loops to solve this problem. For example,
function sortArray(array) {
var temp = 0;
for (var i = 0; i < array.length; i++) {
for (var j = i; j < array.length; j++) {
if (array[j] < array[i]) {
temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}
return array;
}
console.log(sortArray([3,1,2]));
With this function, no matter what size the array is it will always sort it.
The reason your function is not working (as is stated by #WashingtonGuedes) is that you are comparing the same value each time. As they said, you will reach the last statement and receive two false's, which causes you to test false for all three if statements. Your returned array, then, will be empty.
One suggestion is to not hard-code for three values, as you have done, but instead assume nothing and let the program do the hard work. As put in my code snippet, you can enter an array of any length and it will be sorted, not just where length is 3.
Your code as it is now currently does not work because you are comparing a number with itself, so it will always be equal (causing your sorting to do nothing). To get a working sort you could fix this or use array.prototype.sort.
Here is a fun little variation on this:
var sortingFunction = function(){
console.log([].slice.apply(arguments).sort(function(a, b) { return a - b; }));
}
sortingFunction(3,2,5,1);
You can pass in as many numbers as you want, not just three. If you want to limit it to three you can test it in the function:
var sortingFunction = function(){
var values = [].slice.apply(arguments);
if(values.length === 3) {
console.log(values.sort(function(a, b) { return a - b; }));
}
else
{
console.log('you didn\'t pass in three values');
}
}
sortingFunction(3,2,5,1);
sortingFunction(3,31,1);
If you just want an array with numbers arranged from least to greatest, you can use the sort() method with the following parameter:
array.sort(function(a,b){return(a-b)});
var array = [12,7,18,1];
array.sort(function(a,b){return (a-b)});
console.log(array); //Array should be arranged from least to greatest
If it's just three items you want to sort, you can do it quite easily with three comparisons and swaps:
if (num[0] > num[1])
{
// swap num[0] and num[1]
temp = num[0]; num[0] = num[1]; num[1] = temp;
}
if (num[0] > num[2])
{
// swap num[0] and num[2]
temp = num[0]; num[0] = num[2]; num[2] = temp;
}
// at this point, num[0] contains the smallest of the three numbers
if (num[1] > num[2])
{
// swap num[1] and num[2]
temp = num[1]; num[1] = num[2]; num[1] = temp;
}
// your three items are sorted
This is easy to prove correct by hand. Write the numbers 1, 2, and 3 on small pieces of paper, lay them out in random order, and then perform those steps above. No matter what order you start with, this will sort those three items.
Understand, the above only works for three items. If you want a way to sort any number of items, then you'll want to use the built-in sorting method.
var sort = function ([x, y, z]) {
var k = [x, y, z];
k[0] = Math.min(x, y, z);
if ((x < y && x > z) || (x < z && x > y)) {
k[1] = x;
}
else if ((y < x && y > z) || (y < z && y > x)) {
k[1] = y;
}
else {
k[1] = z;
}
k[2] = Math.max(x, y, z);
return k;
};
I can't find how to determine to which interval an element belongs based on an Array for JavaScript. I want the behavior of bisect.bisect_left from Python. Here is some sample code:
import bisect
a = [10,20,30,40]
print(bisect.bisect_left(a,0)) #0 because 0 <= 10
print(bisect.bisect_left(a,10)) #0 because 10 <= 10
print(bisect.bisect_left(a,15)) #1 because 10 < 15 < 20
print(bisect.bisect_left(a,25)) #2 ...
print(bisect.bisect_left(a,35)) #3 ...
print(bisect.bisect_left(a,45)) #4
I know this would be easy to implement, but why re-invent the wheel?
In case anyone else lands here, here's an implementation of bisect_left that actually runs in O(log N), and should work regardless of the interval between list elements. NB that is does not sort the input list, and, as-is, will likely blow the stack if you pass it an unsorted list. It's also only set up to work with numbers, but it should be easy enough to adapt it to accept a comparison function. Take this as a starting point, not necessarily your destination. Improvements are certainly welcome!
Run it in a REPL
function bisect(sortedList, el){
if(!sortedList.length) return 0;
if(sortedList.length == 1) {
return el > sortedList[0] ? 1 : 0;
}
let lbound = 0;
let rbound = sortedList.length - 1;
return bisect(lbound, rbound);
// note that this function depends on closure over lbound and rbound
// to work correctly
function bisect(lb, rb){
if(rb - lb == 1){
if(sortedList[lb] < el && sortedList[rb] >= el){
return lb + 1;
}
if(sortedList[lb] == el){
return lb;
}
}
if(sortedList[lb] > el){
return 0;
}
if(sortedList[rb] < el){
return sortedList.length
}
let midPoint = lb + (Math.floor((rb - lb) / 2));
let midValue = sortedList[midPoint];
if(el <= midValue){
rbound = midPoint
}
else if(el > midValue){
lbound = midPoint
}
return bisect(lbound, rbound);
}
}
console.log(bisect([1,2,4,5,6], 3)) // => 2
console.log(bisect([1,2,4,5,6], 7)) // => 5
console.log(bisect([0,1,1,1,1,2], 1)) // => 1
console.log(bisect([0,1], 0)) // => 0
console.log(bisect([1,1,1,1,1], 1)) // => 0
console.log(bisect([1], 2)); // => 1
console.log(bisect([1], 1)); // => 0
Speaking of re-inventing the wheel, I'd like to join the conversation:
function bisectLeft(arr, value, lo=0, hi=arr.length) {
while (lo < hi) {
const mid = (lo + hi) >> 1;
if (arr[mid] < value) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}
I believe that is the schoolbook implementation of bisection. Actually, you'll find something pretty much the same inside the d3-array package mentioned before.
using the D3-array npm.
const d3 = require('d3-array');
var a = [10,20,30,40];
console.log(d3.bisectLeft(a,0));
console.log(d3.bisectLeft(a,10));
console.log(d3.bisectLeft(a,15));
console.log(d3.bisectLeft(a,25));
console.log(d3.bisectLeft(a,35));
console.log(d3.bisectLeft(a,45));
output:
0
0
1
2
3
4
A faster way than the previously accepted answer that works for same size intervals is:
var array = [5, 20, 35, 50]
//Intervals:
// <5: 0
// [5-20): 1
// [20-35): 2
// [35-50): 3
// >=50: 4
var getPosition = function(array, x) {
if (array.length == 0) return;
if (array.length == 1) return (x < array[0]) ? 0 : 1;
return Math.floor((x - array[0]) / (array[1] - array[0])) + 1
}
console.log(getPosition(array, 2)); //0
console.log(getPosition(array, 5)); //1
console.log(getPosition(array, 15));//1
console.log(getPosition(array, 20));//2
console.log(getPosition(array, 48));//3
console.log(getPosition(array, 50));//4
console.log(getPosition(array, 53));//4
console.log("WHEN SIZE: 1")
array = [5];
//Intervals:
// <5: 0
// >=5: 1
console.log(getPosition(array, 3));
console.log(getPosition(array, 5));
console.log(getPosition(array, 6));
There are no built-in bisection functions in JavaScript, so you will have to roll your own. Here is my personal reinvention of the wheel:
var array = [10, 20, 30, 40]
function bisectLeft (array, x) {
for (var i = 0; i < array.length; i++) {
if (array[i] >= x) return i
}
return array.length
}
console.log(bisectLeft(array, 5))
console.log(bisectLeft(array, 15))
console.log(bisectLeft(array, 25))
console.log(bisectLeft(array, 35))
console.log(bisectLeft(array, 45))
function bisectRight (array, x) {
for (var i = 0; i < array.length; i++) {
if (array[i] > x) return i
}
return array.length
}