I'm modifying the jquery ui slider. I have certain "stops" I want the user to be able to slide to, expressed as a percentage of the slider's overall width. So, for example, if I have 3 stops, they will be distributed evenly at 0, 50, and 100 (%). I store these in an array [0,50,100].
When the user drags the slider and releases, I capture the slider's current value. So if he scrolled 56% of the way across the bar, his stopVal is 56.
How do I write a function that will then determine which number in the array this stopVal is closest to? Here's my code:
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
//NOW NEED TO FIND CLOSEST ARRAY VALUE TO stopVal
}
This function will let you do that:
Array.prototype.closest = function(value) {
var i;
function diff(n) {
var diff = n - value;
return diff < 0 ? -diff : diff;
}
var found = this[0],
mindiff = diff(found);
for (i = 1 ; i < this.length ; i++) {
var currentdiff = diff(this[i]);
if (currentdiff < mindiff) {
found = this[i];
mindiff = diff(found);
}
}
return found;
}
Now you can do this:
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
//NOW NEED TO FIND CLOSEST ARRAY VALUE TO stopVal
stopVal = optValArr.closest(stopVal);
}
NOTE: Some people consider defining prototypes for native types as dangerous as it can cause conflicts if two libraries do the same (just like global variable). If you are writing a public library you should therefore avoid adding to the prototypes of native types.
Try This:
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
var diff=101;
var val =0;
for(var i =0; i < optValArr.length; i++){
var tmpDiff = Math.abs(stopVal - optValArr[i]);
if(tmpDiff < diff){
diff=tmpDiff;
val = optValArr[i]
}
}
}
slideStop("something", {"value":20});
Demo: http://jsfiddle.net/ggzZj/
var optValArr = [0,50,100];
function slideStop( event, ui ) {
var stopVal = ui.value;
var closestVal = optValArr.reduce(function (memo, curr) {
var currDiff = Math.abs(curr - stopVal),
memoDiff = Math.abs(memo - stopVal)
return memoDiff < currDiff ? memoDiff : currDif
})
}
Another common definition of "closer" is based on the square of the difference. But, you could do that by simply adding the number that you want in your original array like this:
[10,40,50, my_number]
Then, sort your array and then you choice if you want the closest position from the right or the left.
What do you think?
Related
Wondering if this function is reliable to always be unique, also wondering if I should do this locally or on the server?
function IDGenerator() {
this.length = 8;
this.timestamp = +new Date;
var _getRandomInt = function( min, max ) {
return Math.floor( Math.random() * ( max - min + 1 ) ) + min;
}
this.generate = function() {
var ts = this.timestamp.toString();
var parts = ts.split( "" ).reverse();
var id = "";
for( var i = 0; i < this.length; ++i ) {
var index = _getRandomInt( 0, parts.length - 1 );
id += parts[index];
}
return id;
}
}
Since no two time-stamps can be generated within the same time on the same machine.
Generated by, and within the same machine - a Time-stamp alone - is guaranteed to be Unique.
The problem with just using Math.random is that it is not truly random. On the other hand, the problem with just using a date-time stamp is that, depending on how quickly the method is executed, the value may duplicate.
I have used this to great effect in many applications. It is simple, and I have never come across a conflict due to duplicate values:
function uid() {
return (Date.now().toString(36) + Math.random().toString(36).substr(2, 9));
}
I'm building a little module in javascript to act like a pack of cards. My first method works but was quite simple, and so i wanted to create some shuffle methods that mimic the idea behind real world card shuffling.
Amongst some other useful functions I've create riffle, overhand and cut functions, that all seem to do there job, but when calling them repeatedly in sequence the returned pack amount is inconsistent, from running it over and over again it appears to be some sort of race condition, but can't seem to get my head around how to avoid it.
The relevant private methods are :
riffle : function riffle() {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log('top is '+top.length+" and bottom is "+bottom.length);
var hand = 'right';
var result = [];
var i = 52;
while (i > 0) {
var drop = Math.floor(Math.random()*3)+1;
var cards;
if (hand === 'right' ) {
if (drop >= top.length) {
cards = top;
} else {
cards = top.splice(0, drop);
}
hand = 'left';
} else {
if (drop >= bottom.length) {
cards = bottom;
} else {
cards = bottom.splice(0, drop);
}
hand = 'right';
}
result = result.concat(cards);
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
cut : function cut(fn) {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log(top);
Pack = bottom.concat(top);
console.log(Pack.length+" after cut");
if (fn && typeof(fn) === 'function') { fn(); }
return this;
}
Later on I have a privileged method called shuffle that calls them :
shuffle : function shuffle(cb) {
State.cardsOut = [];
Internal.generatePack().cut().riffle().riffle()
.riffle().riffle().riffle();
if (cb && typeof(cb) === 'function') { cb(); }
}
Note : I start with a generate function that creates an arrray of objects representing a full pack of 52 cards. The results I get when I console log the pack at different times after shuffles and cuts vary and I can't seem to figure out why.
you can see what i'km working on here
https://gist.github.com/Pushplaybang/66bc7a1fa5d84eee2236
Any help would be awesome.
The drop variable stores the number of cards you are supposed to be riffling from either the left or right hand. However, there are two instances:
if (drop >= top.length) {
cards = top;
}
and
if (drop >= bottom.length) {
cards = bottom;
}
where drop can be greater than the number of remaining cards in the half of the pack so more cards will be subtracted from i than you have actually riffled. You can fix this by:
if (drop >= top.length) {
drop = top.length;
cards = top;
top = [];
}
and
if (drop >= bottom.length) {
drop = top.length;
cards = bottom;
bottom = [];
}
(You need to empty the arrays or you may end up adding the same cards twice).
Other issues
You have magic numbers in the code (26 and 52) these could be constants defined in the class and given appropriate names (i.e. PACK_SIZE = 52) which would mean that if you create a sub-class representing a different number of cards then it would still work.
hand has two possible values which could be represented as a boolean but you assign it strings (again you could use constants LEFT_HAND = true, RIGHT_HAND = !LEFT_HAND).
Pack appears to be a global variable - I would have thought it ought to be a member of the class.
You do not need to name the functions as this is just polluting the global namespace: riffle : function riffle() { can just be an anonymous function riffle : function() {.
Performance - you create additional arrays with each iteration and the cards are moved multiple times. This could be more efficient.
Something like this:
PACK_SIZE: 52,
riffle : function() {
var index_of_cards_riffled_from_top = 0;
var index_of_cards_riffled_from_bottom = this.PACK_SIZE / 2;
var riffled_cards = [];
while ( index_of_cards_riffled_from_top < this.PACK_SIZE / 2
|| index_of_cards_riffled_from_bottom < this.PACK_SIZE ) {
var num_cards_to_riffle_top = Math.min( this.PACK_SIZE / 2 - index_of_cards_riffled_from_top, Math.floor( Math.random() * 3 ) + 1 );
var num_cards_to_riffle_bottom = Math.min( this.PACK_SIZE - index_of_cards_riffled_from_bottom, Math.floor( Math.random() * 3 ) + 1 );
while ( num_cards_to_riffle_top > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_top++ ] );
num_cards_to_riffle_top--;
}
while ( num_cards_to_riffle_bottom > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_bottom++ ] );
num_cards_to_riffle_bottom--;
}
}
this.Pack = riffled_cards;
}
while #MTO 's answer did solve my problem, I'd like to shed some light on how I've chosen to begin refactoring this function.
riffle : function riffle() {
var cutPos = Math.floor(Math.random()*rv)+( (cardCount-rv) / 2 );
var splitPack = {
left : Pack.splice(0, cutPos),
right : Pack.splice(0, Pack.length)
};
var hand = 'right',result = [], i = 52, cards;
while(i > 0) {
drop = Math.floor(Math.random()*3)+1;
if (drop >= splitPack[ hand ].length) {
drop = splitPack[ hand ].length;
}
cards = splitPack[ hand ].splice(0, drop);
hand = (hand === 'left') ? 'right' : 'left';
result = result.concat(cards);
cards = [];
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
a few things :
the elements that seem global are not really, as this is all wrapped within a function that creates a new "deck" object, and some elements need to be private, such as the cards remaining in the pack once dealing has begin.
While booleans would work well for the hands, I wanted to boil this down somewhat and so use the strings to select obj properties.
everything MTO said about using constants is absolutely valid.
by now splicing each time, we're removing the elements from the array.
I prefer this approach as it only uses one while loop.
lastly, this type of shuffle is meant to emulate hand shuffling, and must be combined with other hand shuffling methods, ideally in a repetitive sequence, to produce something useful,
if you want something consistently random and efficient use fischer-yates algorithm.
I want to cycle through an array and display each element individually, and then remove it. Sort of like this fiddle, but I don't want it to go forever.
I tried using jQuery because I thought it would be easier, but I am clearly missing something. Can anyone help?
Here is my attempt, but it just goes straight to the last element in the array.
var list = [1,2,3,4,5,6];
var length = list.length;
for(i = 0; i < length; i++) {
$('#nums').html(list[i]).delay(750);
}
Oh, and I don't care if it's jQuery or vanilla JavaScript. Either is fine by me.
$(document).ready(function(){
var list = [1,2,3,4,5,6];
var length = list.length;
var i = 0;
var ivl = setInterval( function () {
if (i < length) {
$('#nums').html(list[i]).delay(750);
i++;
}
else {
clearInterval(ivl);
}
}, 750);
});
The (pretty clever) example uses the fact that the modulus operator (%) gives you remainder, which is periodic, so you can use it to cycle through your array by taking the remainder of an infinitely increasing number divided by the length of your list.
If you want it to stop, however, you can just do a check to see if you've finished cycling through the list:
var list = [1, 2, 3, 4, 5, 6];
var length = list.length;
var i = 0;
var finished = false;
function repeat() {
if (!finished) {
document.getElementById('nums').innerHTML = list[i % length];
i++;
if (i === length) {
finished = true;
}
} else {
clearInterval(interval);
}
}
var interval = setInterval(repeat, 750);
<div id="nums"></div>
Late to the party but wouldn't it be better to use setTimeout rather than setInterval just in case the code executed on each iteration takes longer than the interval duration? I mean, I know it's not an issue in this instance but it's just a better/safer way to do this sort of thing.
$(document).ready(function(){
var list = [1,2,3,4,5,6];
var length = list.length;
var i = 0;
(function next(){
if (i < length){
$('#nums').html(list[i++]);
setTimeout(next,750);
}
})();
});
http://jsfiddle.net/zLexhdfp/3/
Let's say I have a simple array like this:
var myArr = [0,1,2,3,4,5,6,7,8,9]
I'd like to extract a number of elements, starting from a specific index, like this:
myArr.getElementsFromIndex(index, numberOfElements)
where, unlike .slice(), if we hit the last index, elements from the start of the array should be returned instead (so that the total number of elements returned will always be respected). Either pure javascript or a library like underscore/lodash can be used.
Examples:
myArr.getElementsFromIndex(3, 5)
should return[3,4,5,6,7]
and
myArr.getElementsFromIndex(8, 5)
should return [8,9,0,1,2]
Use the below code
var myArr = [0,1,2,3,4,5,6,7,8,9];
function getElementsFromIndex(startIndex, num) {
var elems = [];
for(var iter = 0; iter<num; iter++) {
if(startIndex >= myArr.length) {
while(startIndex >= myArr.length) {
startIndex -= myArr.length;
}
}
elems.push(myArr[startIndex]);
startIndex++;
}
return(elems);
}
Array#slice takes a start and end index (not a start index and a number of elements).
Array#splice does what you want, except for the wrapping around (but also modifies the original array).
You can write a wrapper function using slice (which will not modify the original array):
function getElementsFromIndex(arr, start, numElements) {
if(start + numElements > arr.length) {
var endOfArr = arr.slice(start, arr.length);
var elementsFound = arr.length - start;
var restElements = getElementsFromIndex(arr, 0, numElements - elementsFound);
return endOfArr.concat(restElements);
}
return arr.slice(start, start + numElements);
}
This function returns what you require (see example), and even wraps around multiple times, if needed.
If you want to tie the function to arrays, in order to use it as you propose (ie. myArr.getElementsFromIndex(start, numElements)), you can add it to Array's prototype. You might want to look up arguments for/against modifying prototypes of built-in types, though.
Array.prototype.getElementsFromIndex = function(start, numElements) {
if(start + numElements > this.length) {
var endOfArr = this.slice(start, this.length);
var elementsFound = this.length - start;
return endOfArr.concat(this.getElementsFromIndex(0, numElements - elementsFound));
}
return this.slice(start, start + numElements);
};
See example of the last one here.
Add this to your js code:
Array.prototype.getElementsFromIndex = function (start, len) {
var newArray = [],
origArray = this,
i = start;
while (newArray.length < len) {
newArray.push(origArray[i++]);
if (i >= origArray.length)
i = 0;
}
return newArray;
}
You can use it exactly the way you wanted:
var myArr = [0,1,2,3,4,5,6,7,8,9];
alert(myArr.getElementsFromIndex(8, 5));
JSFIDDLE DEMO: http://jsfiddle.net/x6oy0krL/1
Maybe some people will say that it is not right to extend objects like Array, documentElement and so on, but the result here is as the OP wanted.
I want to say that the original array will not be modified, too.
Just concatenate the array to itself, then use slice:
function sliceWrap(arr, start, num) {
return arr.concat(arr).slice(start, start+num);
}
Another approach, which wraps around:
function sliceWrap2(arr, start, num) {
var result = [], i, end = start+num, len = arr.length;
for (i=start; i<end; i++) {
result.push(arr[i % len]);
}
return result;
}
Here is my function for determining if a given timecode in the buffer of an html5 video element (learned about this here).
I think there must be a faster way. Maybe a binary search over the start times?
I considered an interval tree, but the cost to maintain that data structure seems excessive given a system level data structure is provided.
isTimecodeInBuffer = function( _tc ) {
var r = $(html5VideoEl).buffered;
var i;
var iMax = r.length;
var within = false;
//todo: better seek here
for (i=0; i<iMax; ++i) {
if (_tc >= r.start(i) && _tc < r.end(i)) {
within = true;
break;
}
}
return within;
};
You can do this with a standard binary search that is slightly modified to test for matching a time range, rather than an exact match. It's not worth storing any kind of data structure, since the data will change quite frequently as additional data is buffered.
function bufferedGreater(haystack, index, value) {
return haystack.end(index) <= value;
}
function bufferedLess(haystack, index, value) {
return haystack.start(index) > value;
}
function binarySearch(haystack, needle, greaterThan, lessThan) {
var minIndex = 0,
maxIndex = haystack.length - 1,
currentIndex;
while (minIndex <= maxIndex) {
currentIndex = Math.floor((minIndex + maxIndex) / 2);
if (greaterThan(haystack, currentIndex, needle)) {
minIndex = currentIndex + 1;
} else if (lessThan(haystack, currentIndex, needle)) {
maxIndex = currentIndex - 1;
} else {
return currentIndex;
}
}
return -1;
}
var buffered = binarySearch(video.buffered, 10, bufferedGreater, bufferedLess) >= 0;
There is a working demo at http://jsbin.com/vifedogi/1/edit?html,js,console,output
Note: you'll want to access the buffered object directly on the video element, not on the jQuery object, like var r = html5VideoEl.buffered;