I have an array of numbers, for example [300, 500, 700, 1000, 2000, 3000] and I want to find the closest number, without going under the number given.
For instance, searching for 2200 would return 3000 (NOT 2000).
However, if I search for 3200 as there is nothing higher in the array, it should return 3000 as there are no other choices.
I can get the closest number that is under the value using:
if (sizeToUse == null || Math.abs(this - monitorWidth) < Math.abs(sizeToUse - monitorWidth)) {
sizeToUse = this;
}
However, I can't get the whole thing to work. My full code is:
$(function() {
var monitorWidth = window.screen.availWidth,
sizeToUse = null,
upscaleImages = false;
$('.responsive-img').each(function(){
var sizeData = $(this).attr('data-available-sizes');
sizeData = sizeData.replace(' ', '');
var sizesAvailable = sizeData.split(',');
sizesAvailable.sort(function(a, b){return b-a});
$.each(sizesAvailable, function(){
if(upscaleImages){
if (sizeToUse == null || Math.abs(this - monitorWidth) < Math.abs(sizeToUse - monitorWidth)) {
sizeToUse = this;
}
}
else{
//We don't want to upscale images so we need to find the next highest image available
}
});
console.log('Size to use ' + sizeToUse + ' monitor width ' + monitorWidth);
});
});
You can use this code :
function closest(arr, closestTo){
var closest = Math.max.apply(null, arr); //Get the highest number in arr in case it match nothing.
for(var i = 0; i < arr.length; i++){ //Loop the array
if(arr[i] >= closestTo && arr[i] < closest) closest = arr[i]; //Check if it's higher than your number, but lower than your closest value
}
return closest; // return the value
}
var x = closest(yourArr, 2200);
Fiddle : http://jsfiddle.net/ngZ32/
Another approach is to find the first candidate greater than or equal to the one you want, and take the result, returning the last element if there are no matches:
function closestNumberOver(x, arr) {
return arr.find(d => d >= x) || arr[arr.length - 1]
}
var list = [300, 500, 700, 1000, 2000, 3000];
function findBestMatch(toMatch) {
// Assumes the array is sorted.
var bestMatch = null;
var max = Number.MIN_VALUE;
var item;
for (var i = 0; i < list.length; i++) {
item = list[i];
if (item > toMatch) {
bestMatch = item;
break;
}
max = Math.max(max, item);
}
// Compare to null, just in case bestMatch is 0 itself.
if (bestMatch !== null) {
return bestMatch;
}
return max;
}
alert(findBestMatch(2200));
alert(findBestMatch(3200));
sizesAvailable.sort(function(a, b){return a-b}); // DESCENDING sort
if(upscaleImages) // do th eif once, not every time through the loop
{
$.each(sizesAvailable, function()
{
if (this > monitorWidth)
sizeToUse = this;
}
if (sizeToUse == null) sizeToUse = sizesAvailable[0];
}
else
{
$.each(sizesAvailable, function()
{
//We don't want to upscale images so....
}
}
});
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 a list of positive integers e.g. 15, 29, 110, and a target e.g. 44. I'm trying to find all possible combinations which sum to the target but importantly, the numbers in the set can be used multiple times e.g.
Target = 44
Result = 1x15, 1x29
Target = 307
Result = 2x110, 3x29
I found a dynamic programming solution which works when the combination is no more than one of each number. So Target 44 works but not my 307 example (returns Not Found).
How can the multiples or number reuse be done?
function subset(people, min, max)
{
var subsets = [];
subsets[0] = '';
for (var person in people)
{
for (var s = min-1; s >= 0; --s)
{
if (s in subsets)
{
var sum = s + people[person];
if (!(sum in subsets))
{
subsets[sum] = subsets[s] + ' ' + person;
if (sum >= min && sum <= max)
{
return subsets[sum];
}
}
}
}
}
return 'Not found';
}
var p = {
optionA:15,
optionB:29,
optionC:110
};
var qty = 307;
console.log(subset(p, qty, qty));
Try this recursive solution:
function subset(people, min, max) {
const pairs = Object.entries(people),
results = [],
getSum = multiplications => multiplications.reduce((sum, multiplicator, position) =>
sum + pairs[position][1] * multiplicator, 0),
formatResult = result => result.map(multiplications =>
multiplications.reduce((res, multiplicator, position) =>
(multiplicator > 0 ? res.push(`${multiplicator}x${pairs[position][1]}`) :
res, res), []));
function findSums(multiplications, position) {
let s;
while((s = getSum(multiplications)) <= max) {
if (s >= min) {
results.push([...multiplications]);
}
if (position < pairs.length - 1) {
const m = [...multiplications],
nextPosition = position + 1;
m[nextPosition]++;
findSums(m, nextPosition);
}
multiplications[position]++;
}
}
findSums(pairs.map(_ => 0), 0);
return results.length > 0 ? formatResult(results) : "Not found";
}
var p = {
optionA:15,
optionB:29,
optionC:110
};
var qty = 307;
console.log(subset(p, qty, qty));
Change the second loop in such way:
for (var s = 0; s <= wantedSum - people[person] ; s++)
Using this approach you fill all entries of subsets[] array\list where index is multiple of people[person] (instead of single entry). For example, with value 3 you fill 3,6,9,12... entries.
I have an array with 3 types of values in which I wish to sort them.
If I only use one variable it goes all pretty well, but it all goes wrong when i want to use 3 types of variables to determine sorting order.
First variable condition: if obj.sortorder == 999999 send it to the end of the line(there is only one of this one);
if not true then
if (date < other date) put it in front of the one before
if(date > other date) put it behind the other one
if dates are equal look to sortorder to determine the order in which to apppear.
Then loop over array, reset all sortorder variables to be properly ascending in the new order.
Then splice in a new value(15th), but then it doesn't appear next to the other one, even though they have duplicate values.
My head hurts from trying to figure this one out, I just can't seem to get them in the right order.
Basically the way they are created here they should come out in the first sort. But yet the first sort is messed up with values all over the place except where I want them.
The second sort puts them miraculously in the right order but puts the 16 between the two fifteens whilst the fifteens should be next to eachother. and somehow the 24th ends up as the 9999 after the reassignment whilst the 29th should have remained as the last one.
Who can help me with this?
If you press run code snippet you get the garbled output I currently get.
The first set is what all the others should look like, except for the last one where the 15's should be hugging eachother.f
elements = [];
for (var c = 0; c < 30; c++) {
elements[c] = {
sortorder: c,
getDate: function () {
return new Date(2015, 06, this.sortorder)
}
};
}
function log(what) {
var elem = document.getElementById('sortorder');
elem.appendChild(document.createTextNode(what + "\n"));
}
for (c = 1; c < elements.length; c++) {
log(c + " = " + elements[c].getDate() + " - " + elements[c].sortorder)
}
log('--------------------------------');
elements[elements.length - 1].sortorder = 9999999;
this.elements.sort(function (one, two) {
/**
* Failsafe to prevent the last element to be placed in the middle
*/
if (one.sortorder >= 9999998) {
return 1;
}
if (two.sortorder >= 9999998) {
return -1;
}
if (one.getDate() < two.getDate()) {
return -1
}
if (two.getDate() > one.getDate()) {
return 1;
}
if (one.sortorder < two.sortorder) {
return -1;
}
if (one.sortOrder > two.sortorder) {
return 1;
}
return 0;
});
function log(what) {
var elem = document.getElementById('sortorder');
elem.appendChild(document.createTextNode(what + "\n"));
}
for (c = 1; c < elements.length; c++) {
log(c + " = " + elements[c].getDate() + " - " + elements[c].sortorder)
}
log('--------------------------------------------------------------');
elements.splice(17, 0, {
sortorder: 15,
getDate: function () {
return new Date(2015, 06, 15)
}
});
for (var c = 1; c < this.elements.length; c++) {
if (c < this.elements.length - 1) {
this.elements[c].sortorder = c;
} else {
this.elements[c].sortorder = 9999999;
}
}
for (c = 1; c < elements.length; c++) {
log(c + " = " + elements[c].getDate() + " - " + elements[c].sortorder)
}
<pre id="sortorder">
</pre>
After some experimenting, fiddling, swearing, googling, the normal process in a situation like this I finally got the solution.
I guess I was overthinking things once again.
Thanks for helping out and thinking along.
elements.sort(function(one,two)
{
var ret = 0;
/**
* Failsafe to prevent the last element to be placed in the middle
*/
if(one.sortorder >= 9999998) {
ret = 1;
}
else {
if(two.sortorder >= 9999998) {
ret = -1;
}
else {
if(one.getDate() - two.getDate() !== 0) {
ret = one.getDate() - two.getDate();
}
else {
ret = one.sortorder - two.sortorder;
}
}
}
console.log(ret);
return ret;
});
Not quite sure what you mean by "Then splice in a new value(15th), but then it doesn't appear next to the other one, even though they have duplicate values."
This does everything but that.
var last;
var newArray = [];
for(var obj in array){
obj = array[obj];
if(obj.sortorder === 999999)
last = obj;
else {
for(var item in newArray){
if(obj.date.getTime() < newArray[item].date.getTime()){
newArray.splice(item, 0, obj);
break;
}
if(obj.date.getTime() === newArray[item].date.getTime()){
if(obj.sortorder > newArray[item].sortorder){
newArray.splice(item, 0, obj);
break;
}
else{
newArray.splice(item + 1, 0, obj);
break;
}
}
}
}
}
newArray.push(last);
var sortorder = 1;
for(var item in newArray){
if(newArray[item].date.getTime() > newArray[item - 1].date.getTime())
newArray[item].sortorder = ++sortorder;
}
I am learning js now..
I am trying to write a simple js programme..
what I am trying to do is to print all valid combinations of n-pair
of parenthesis(properly opened and closed)
eg (), (()()),(())
i have written the logic can you tell me whether its correct or not
https://jsfiddle.net/e7mcp6xb/
module.exports = Parentheses = (function() {
var _isParenthesesMatch = function(str) {
var parentheses = str.length;
var rightParentheses = '(';
var leftParentheses = ')';
var rightCount = 0;
var leftCount = 0;
for(i=0;i<=str.length;i++){
if(rightParentheses == str.charAt(i))
{
rightCount++;
}
else if(leftParentheses == str.charAt(i))
{
leftCount++;
}
}
if(rightCount == leftCount){
return true;
}
else(rightCount != leftCount){
return false;
}
}
}());
The check is wrong, but You can fix it easily: In each step of the for loop the number of opening parenthesis cannot be smaller than the number of closing ones:
if (rightCount < leftCount)
return false;
The whole function should look like this:
function(str) {
var rightParentheses = '(';
var leftParentheses = ')';
var rightCount = 0;
var leftCount = 0;
for (var i = 0; i <= str.length; i++) {
if (rightParentheses == str.charAt(i))
rightCount++;
else if (leftParentheses == str.charAt(i))
leftCount++;
if (rightCount < leftCount)
return false;
}
return rightCount == leftCount;
}
If You'd like to generate all valid strings, you can use this function:
function nPair(n) {
if (n == 0)
return [""];
var result = [];
for (var i = 0; i < n; ++i) {
var lefts = nPair(i);
var rights = nPair(n - i - 1);
for (var l = 0; l < lefts.length; ++l)
for (var r = 0; r < rights.length; ++r)
result.push("(" + lefts[l] + ")" + rights[r]);
}
return result;
}
// result of nPair(3):
// ["()()()", "()(())", "(())()", "(()())", "((()))"]
Try this, i have modified your code a little bit. Modification and its explanation is marked in comments.
module.exports = Parentheses = (function() {
var _isParenthesesMatch = function(str) {
var parentheses = str.length;
var rightParentheses = '(';
var leftParentheses = ')';
var count=0;
for(i=0;i<str.length;i++){
//this is to check valid combination start always from ( and end with )
if(str.charAt(0)==rightParentheses && str.length-1==leftParentheses)
{
if(rightParentheses == str.charAt(i))
{
count++; //this will calculate how many times rightParentheses is present & increment count by 1
}
else if(leftParentheses == str.charAt(i))
{
count--; //this will simply decrement count to match valid sequence
}
}
if(count==0){
return true;
}
}
}());
Your function is wrong, try checking if left and right parenthesis and balanced:
function isValid(str){
var stripedStr = str.replace(/[^\(\)]+/g, '');
return stripedStr.split('').reduce(function(a, b){
return a > -1 ? b === '(' ? a + 1 : a - 1 : -1;
}, 0) === 0;
}
stripedStr - use replace() to remove any characters that are not ( or ).
split('') - returns an array so we can use reduce.
reduce() - applies a function against an accumulator and each value of the array (from left-to-right) has to reduce it to a single value.
The reduce starts with 0 as initial value and in the reduce function we count parenthesis
(+1 for (, -1 for ) )
Our string is valid if our counter never goes below 0 and we end up with 0.
You can write the reduce function like this too:
function(previousValue, currentValue){
if (previousValue > -1){
if (currentValue === '('){
return previousValue + 1;
} else {
return previousValue - 1;
}
}
return -1;
}
This is equivalent to:
function(a, b){
return a > -1 ? b === '(' ? a + 1 : a - 1 : -1;
}
It is wrong, because your function will return true for this example ))(( or this ())(()
I tried to rewrite this indexOf MDN example to practice recursion
var str = 'To be, or not to be, that is the question.';
var count = 0;
var pos = str.indexOf('e');
while (pos !== -1) {
count++;
pos = str.indexOf('e', pos + 1);
}
console.log(count); // displays 4
This was my solution:
var count = 0;
function countLetters(str, p) {
var pos = str.indexOf(p);
if (pos == -1) {
return count;
}
else {
count ++;
return countLetters(str.substr(pos + 1), p)
}
}
console.log(countLetters('To be, or not to be, that is the question.', 'e'));
It works, but is there anyway to get the count variable inside the function itself? Is it not really a true recursion if I have a count variable outside the function?
In a recursive function, if you want to keep a variable around from one "iteration" to the next, then you need to pass it as an argument:
function countLetters(str, p, count) {
count = count || 0;
var pos = str.indexOf(p);
if (pos == -1) {
return count;
}
else {
return countLetters(str.substr(pos + 1), p, count + 1);
}
}
console.log(countLetters('To be, or not to be, that is the question.', 'e'));
// => 4
However, this is not always necessary, as Arun P Johny's answer illustrates.
What you can do is to return the count value form the method, so if the item is not found you return 0, else you return 1 + value-of-recursive-call
function countLetters(str, p) {
var pos = str.indexOf(p);
if (pos == -1) {
return 0;
} else {
return 1 + countLetters(str.substr(pos + 1), p)
}
}
console.log(countLetters('To be, or not to be, that is the question.', 'e'));
Demo: Fiddle