I know it is a noob question, but still thought of asking out of curiosity.
I have a byte array of 3 bytes for example [00,00,00]. everytime I iterate a method I have increment the last by by 0x02 times. Now once the last byte reaches the threshold value FF I need to increment the last but one byte to +1 along with incrementing last byte. For example for byte array values [00,00,FF] incrementing it with 0x02 should become [00,01,02] and finally it should reach [FF,FF,FF] what should be the ideal way of doing it rather than using the normal if conditions.
Did you try with a for loop and a if condition ?
You cycle through your elements. If the element is smaller than 0xFF, you increment it, and you return. Otherwise you set it to 0x00 and you let the for loop cycle further.
Pseudo-code:
for element in array (in reverse order):
if(element == 0xFF):
element = 0
else:
element += 0x02
return array
you should work with only one variable
var i=0; // from 0 to 4096
//every time you can increment i++;
where you want result just convert i to array like :
function convert_array(val){
var val2 = parseInt(val/4096, 16);
var val1 = parseInt(val2/256, 16);
var val0 = parseInt(val2%256, 16);
return [val2 , val1, val0];
}
var result = convert_array(i);
the advanges :
to increment number no problem just i++;
you have no loops (for) :)
EDIT (dumped the nested loops)
For a more generic approach (obviously not with colors), you should try a cascading increment like this:
// non-destructive increment of the arr
// [0xFF, 0x00] -> [0x00, 0x02]
function incremented(arr) {
var value,
index = 0,
increment = 0x02,
threshold = 0xFF,
re = [],
flip = true;
for (var i = 0; i < arr.length; i++) {
value = arr[i];
if (flip === false) {
// don't touch, just add it
re.push(value);
} else if (value + increment > threshold) {
// add zero value, but keep on flipping
re.push(0);
} else {
// add incremented value
re.push(value + increment);
}
}
if (flip) {
// if even the last number did not stop flipping add another
re.push(increment);
}
return re;
}
Related
I have an array with values
const range = [1,10,100,500,1000,2000,4000,8000]
let input = 1580
The expected output is 1000 because 1580 is between 1000 and 2000
But my code is giving wrong results and also giving incorrect for large values.
for(i=0;i<range.length;i++)
{
if(input > range[i])
break;
}
console.log(range[i])
You need to compare input < range to find the first one that's larger than the input.
Then, if you can expect the array to be sorted, you'd need to use the value before the index at which you break, because that's after the limit has been exceeded.
const range = [1, 10, 100, 500, 1000, 2000, 4000, 8000]
const input = 1580
let i;
for (i = 0; i < range.length; i++) {
if (input < range[i])
break;
}
console.log(i);
console.log(range[i - 1])
But a better approach that doesn't depend on the array being sorted would be to .reduce to keep the largest number found so far fulfilling the condition as the accumulator.
const range = [1,1560,10,100,500,1000,1500,2000,4000,8000,1550];
const input = 1580;
const output = range.reduce(
(bestSoFar, num) => num > input ? bestSoFar : Math.max(bestSoFar, num),
0
);
console.log(output);
const range = [1,10,100,500,1000,2000,4000,8000]
let input = 1580
console.log(range.reduce((a,c)=>c<=input?c:a))
for(i=0;i<range.length;i++)
{
if(input > range[i])
break;
}
console.log(range[i]);
On line no:3 , the boolean condition is wrong, what you are effectively trying to do is check whether the input is greater than the each value of range.
Your loop will break itself on the first value of the range array, (ie. range[0]) , thus you won't get the desired output.
You need to tweek the code a little bit.
I'm building an app and in one of my functions I need to generate random & unique 4 digit codes. Obviously there is a finite range from 0000 to 9999 but each day the entire list will be wiped and each day I will not need more than the available amount of codes which means it's possible to have unique codes for each day. Realistically I will probably only need a few hundred codes a day.
The way I've coded it for now is the simple brute force way which would be to generate a random 4 digit number, check if the number exists in an array and if it does, generate another number while if it doesn't, return the generated number.
Since it's 4 digits, the runtime isn't anything too crazy and I'm mostly generating a few hundred codes a day so there won't be some scenario where I've generated 9999 codes and I keep randomly generating numbers to find the last remaining one.
It would also be fine to have letters in there as well instead of just numbers if it would make the problem easier.
Other than my brute force method, what would be a more efficient way of doing this?
Thank you!
Since you have a constrained number of values that will easily fit in memory, the simplest way I know of is to create a list of the possible values and select one randomly, then remove it from the list so it can't be selected again. This will never have a collision with a previously used number:
function initValues(numValues) {
const values = new Array(numValues);
// fill the array with each value
for (let i = 0; i < values.length; i++) {
values[i] = i;
}
return values;
}
function getValue(array) {
if (!array.length) {
throw new Error("array is empty, no more random values");
}
const i = Math.floor(Math.random() * array.length);
const returnVal = array[i];
array.splice(i, 1);
return returnVal;
}
// sample code to use it
const rands = initValues(10000);
console.log(getValue(rands));
console.log(getValue(rands));
console.log(getValue(rands));
console.log(getValue(rands));
This works by doing the following:
Generate an array of all possible values.
When you need a value, select one from the array with a random index.
After selecting the value, remove it from the array.
Return the selected value.
Items are never repeated because they are removed from the array when used.
There are no collisions with used values because you're always just selecting a random value from the remaining unused values.
This relies on the fact that an array of integers is pretty well optimized in Javascript so doing a .splice() on a 10,000 element array is still pretty fast (as it can probably just be memmove instructions).
FYI, this could be made more memory efficient by using a typed array since your numbers can be represented in 16-bit values (instead of the default 64 bits for doubles). But, you'd have to implement your own version of .splice() and keep track of the length yourself since typed arrays don't have these capabilities built in.
For even larger problems like this where memory usage becomes a problem, I've used a BitArray to keep track of previous usage of values.
Here's a class implementation of the same functionality:
class Randoms {
constructor(numValues) {
this.values = new Array(numValues);
for (let i = 0; i < this.values.length; i++) {
this.values[i] = i;
}
}
getRandomValue() {
if (!this.values.length) {
throw new Error("no more random values");
}
const i = Math.floor(Math.random() * this.values.length);
const returnVal = this.values[i];
this.values.splice(i, 1);
return returnVal;
}
}
const rands = new Randoms(10000);
console.log(rands.getRandomValue());
console.log(rands.getRandomValue());
console.log(rands.getRandomValue());
console.log(rands.getRandomValue());
Knuth's multiplicative method looks to work pretty well: it'll map numbers 0 to 9999 to a random-looking other number 0 to 9999, with no overlap:
const hash = i => i*2654435761 % (10000);
const s = new Set();
for (let i = 0; i < 10000; i++) {
const n = hash(i);
if (s.has(n)) { console.log(i, n); break; }
s.add(n);
}
To implement it, simply keep track of an index that gets incremented each time a new one is generated:
const hash = i => i*2654435761 % (10000);
let i = 1;
console.log(
hash(i++),
hash(i++),
hash(i++),
hash(i++),
hash(i++),
);
These results aren't actually random, but they probably do the job well enough for most purposes.
Disclaimer:
This is copy-paste from my answer to another question here. The code was in turn ported from yet another question here.
Utilities:
function isPrime(n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (let i = 5; i * i <= n; i = i + 6) {
if (n % i == 0 || n % (i + 2) == 0) return false;
}
return true;
}
function findNextPrime(n) {
if (n <= 1) return 2;
let prime = n;
while (true) {
prime++;
if (isPrime(prime)) return prime;
}
}
function getIndexGeneratorParams(spaceSize) {
const N = spaceSize;
const Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
const firstIndex = Math.floor(Math.random() * spaceSize);
return [firstIndex, N, Q]
}
function getNextIndex(prevIndex, N, Q) {
return (prevIndex + Q) % N
}
Usage
// Each day you bootstrap to get a tuple of these parameters and persist them throughout the day.
const [firstIndex, N, Q] = getIndexGeneratorParams(10000)
// need to keep track of previous index generated.
// it’s a seed to generate next one.
let prevIndex = firstIndex
// calling this function gives you the unique code
function getHashCode() {
prevIndex = getNextIndex(prevIndex, N, Q)
return prevIndex.toString().padStart(4, "0")
}
console.log(getHashCode());
Explanation
For simplicity let’s say you want generate non-repeat numbers from 0 to 35 in random order. We get pseudo-randomness by polling a "full cycle iterator"†. The idea is simple:
have the indexes 0..35 layout in a circle, denote upperbound as N=36
decide a step size, denoted as Q (Q=23 in this case) given by this formula‡
Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
randomly decide a starting point, e.g. number 5
start generating seemingly random nextIndex from prevIndex, by
nextIndex = (prevIndex + Q) % N
So if we put 5 in we get (5 + 23) % 36 == 28. Put 28 in we get (28 + 23) % 36 == 15.
This process will go through every number in circle (jump back and forth among points on the circle), it will pick each number only once, without repeating. When we get back to our starting point 5, we know we've reach the end.
†: I'm not sure about this term, just quoting from this answer
‡: This formula only gives a nice step size that will make things look more "random", the only requirement for Q is it must be coprime to N
This problem is so small I think a simple solution is best. Build an ordered array of the 10k possible values & permute it at the start of each day. Give the k'th value to the k'th request that day.
It avoids the possible problem with your solution of having multiple collisions.
I want to make a function that modifies a variable based on the given argument.
The function checks a variable and the number in that string. Then via the argument, I specify either increase or decrease the number by 1 (++1).
There is an array as well, that if the number is equal to the length of the array, then it turns to 1 and if the number is less than 1 then it is equal the size of the array. This is to make sure the number of the string does not get less than 1 or more than the length of the array.
the string with the number is Music1. So the circle would be like:
...., Music1, Music2, Music3, Music4, Music1, Music2, Music3, ....
var MyArray = ["Music1", "Music2", "Music3", "Music4"];
var currentMusic = "Music1";
$(".increase").on('click tap', nextMusic);
$(".decrease").on('click tap', previousMusic);
function nextMusic() {
unaryChange('plus')
}
function previousMusic() {
unaryChange('minus')
}
function unaryChange(operation) {
if (currentMusic === "Music4") {
currentMusic = "Music1"
} else if (currentMusic === "Music0") {
currentMusic = "Music4"
}
if (operation === "plus") {
currentMusic = currentMusic.replace(/\d+$/, function(n) {
return ++n
});
} else {
currentMusic = currentMusic.replace(/\d+$/, function(n) {
return --n
});
}
console.log(currentMusic);
$(".text").text(currentMusic);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="increase">increase</button>
<button class="decrease">decrease</button>
<p class="text">value</p>
The above method almost does the job, however I am looking for an easier and more professional solution. It does not look efficient. For example, there must be a better way to specify the argument operation instead of a string like plus, or the conditions.
I need this function to be rewritten in a better way, more professionally and works as described.
Thanks in advance.
It is better to work with array index instead of the values
function unaryChange(operation) {
var currentIndex = MyArray.findIndex(function(item) {
return item === currentMusic;
});
if(operation === 'plus') {
newIndex = currentIndex < MyArray.length - 1 && currentIndex + 1 || 0;
} else {
newIndex = currentIndex > 0 ? currentIndex -1 : MyArray.length -1;
}
currentMusic = MyArray[newIndex]
$(".text").text(currentMusic);
}
In this case whatever the size of the array it will work.
A working example https://jsbin.com/rahomorupa/4/edit?html,js,console,output
Building on Joe's answer I'd suggest you define constants for plus and minus as +1 and -1 respectively to simplify the increment/decrement logic, along with the modulus operator to handle the array wrap-around:
const PLUS = 1;
const MINUS = -1;
function unaryChange(operation) {
var currentIndex = MyArray.findIndex(function(item) {
return item === currentMusic;
});
// If it's invoked as unaryChange(PLUS) or unaryChange(MINUS)
// we don't need any conditional logic to handle the increment,
// and with the % operator we don't need additional bounds overflow
// logic. (This latter bit is complicated somewhat by the need to
// handle a minus step from index 0.)
const {length} = MyArray;
const newIndex = ((currentIndex + operation) % length + length) % length;
currentMusic = MyArray[newIndex]
$(".text").text(currentMusic);
}
The % operator returns the remainder of a division, which conveniently loops back around to 0 when used with an array index against the array length:
const array = ['first', 'second', 'third'];
for (let i = 0; i < 20; i++) {
console.log(array[i % array.length]);
}
You can pass a Boolean for plus, use an arrow function, and a ternary operator:
var MyArray = ["Music1", "Music2", "Music3", "Music4"];
var currentMusic = "Music1";
$(".increase").on('click tap', nextMusic);
$(".decrease").on('click tap', previousMusic);
function nextMusic() {
unaryChange(true)
}
function previousMusic() {
unaryChange(false)
}
function unaryChange(plus) {
currentMusic = currentMusic == "Music4" ? "Music1" : (currentMusic == "Music0" ? "Music4" : currentMusic);
currentMusic = currentMusic.replace(/\d+$/, n => plus ? ++n : --n);
console.log(currentMusic);
$(".text").text(currentMusic);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="increase">increase</button>
<button class="decrease">decrease</button>
<p class="text">value</p>
Since you have an array of music, it's better to use that instead. There's no need to operate from the text, you just need to update the array index to the next value and pass it to the function, and let it get the song name directly.
Since we want to be between the boundaries of 0 and the array length, here's what is used to do this:
Get the next song: (currentTrackIndex + 1) % tracks.length. That will get the next index value and apply modulo to it so it will round back if it exceedes the array length.
Get the previous song: (currentTrackIndex - 1 + tracks.length) % tracks.length. It's pretty much the same as getting the next song, save for the case when the index it's already at zero. If you apply modulo to a negative number, you will get a negative result and will mess up your array index. So instead of using a conditional clause ("if (currentTrackIndex === 0 ...)"), let's add the array length. Why? Because since 0 % n == 0 and n % n == 0, adding the array length will not change the modulo result, while keeping your index as a positive number.
(I changed the name from MyArray to tracks and unaryChange to changeTrack, to give it better meaning clarity)
var tracks = ["Music1", "Music2", "Music3", "Music4"];
var currentTrackIndex = 0;
$(".increase").on('click tap', nextMusic);
$(".decrease").on('click tap', previousMusic);
function nextMusic() {
//It will move to the next track. If it's over the array length, it will reset to 0
changeTrack((currentTrackIndex + 1) % tracks.length)
}
function previousMusic() {
//It will move to the previous song. If it's below zero, it will reset to the last track index
changeTrack((currentTrackIndex + tracks.length - 1) % tracks.length)
}
function changeTrack(newTrackIndex) {
currentTrackIndex = newTrackIndex;
var currentTrack = tracks[currentTrackIndex];
console.log(currentTrackIndex);
$(".text").text(currentTrack);
}
Here's how I'd do it. Since it seems that the word Music is just a prefix used to designate a particular unit, I wont store it over and over again in a array.
As for jQuery? Yeah, nah.
"use strict";
function byId(id){return document.getElementById(id)}
window.addEventListener('load', onLoaded, false);
function onLoaded(evt)
{
let prefix = 'Music';
let count = 4, index=0;
byId('increase').addEventListener('click', function(evt){index++; index %= count; update();}, false);
byId('decrease').addEventListener('click', function(evt){index--; if (index<0) index=count-1; update();}, false);
function update()
{
byId('status').textContent = `${prefix}${index+1}`;
}
}
<span id='status'>Music1</span><br>
<button id='increase'>+</button><button id='decrease'>-</button>
I think this is a good start. Accessing the indices of the array versus the values feels a lot cleaner. Using ternaries cleans up a lot of logic into one line as well.
var MyArray = ["Music1", "Music2", "Music3", "Music4"];
var currentMusic = 0;
$(".increase").on('click tap', unaryChange);
$(".decrease").on('click tap', unaryChange);
function unaryChange() {
if (event.target.className === "increase") {
currentMusic = (currentMusic < 3 ? currentMusic + 1 : 0)
} else {
currentMusic = (currentMusic > 0 ? currentMusic -= 1 : 3)
}
console.log(MyArray[currentMusic]);
$(".text").text(MyArray[currentMusic]);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="increase">increase</button>
<button class="decrease">decrease</button>
<p class="text">value</p>
I'm trying to fill an array with missing intermediate data
My data input is like this
var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]];
I wanna fill the array with missing value but I need to respect this rule:
The 1st value on 2d array must be the next sequence number, so 5.23
... 5.24 ... 5.25 ...
The 2nd value on 2d array must be the same element from the i+1
value
So the results in this case would be
var data = [[5.23,7],[5.24,7],[5.25,7],[5.26,7],[5.27,7],[5.28,7],[5.29,8],[5.30,8],[5.31,8],[5.32,8],[5.33,8],[5.34,8],[5.35,8]];
This little piece of code works, but I don't know
how to put in loop
and how to write a while loop that pass every time the new length of the array
var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]];
if (data[1][0]-data[0][0] > 0.01) {
data.push([data[0][0]+0.01,data[1][1]]);
data.sort(function (a, b) { return a[0] - b[0]; });
} else {
check the next element
}
console.log(data);
Any idea?
Here's another idea... I thought it might feel more natural to loop through the sequence numbers directly.
Your final array will range (in this example) from 5.23 to 5.35 incrementing by 0.01. This approach uses a for loop starting going from 5.23 to 5.35 incrementing by 0.01.
key points
Rounding: Work in x100 then divide back down to avoid floating point rounding issues. I round to the neared hundredth using toFixed(2) and then converting back to a number (with leading + operator).
Indexing: Recognizing 5.23 is the zero index with each index incrementing 1/100, you can calculate index from numerical values, ex. 100*(5.31-5.23) equals 8 (so 5.31 belongs in output[8]).
2nd values: given a numerical value (ex. 5.31), just find the first element in the data array with a higher 1st value and use its 2nd value - this is a corollary of your requirement. Because 5.31 <= 5.28 is false, don't use 7 (from [5.28,7]). Because 5.31 <= 5.32 is true, use 8 (from [5.32,8]).
EDIT
I improved the performance a bit - (1) initialize output instead of modifying array size, (2) work in multiples of 100 instead of continuously rounding from floating point to hundredths.
I ran 5000 iterations on a longer example and, on average, these modifications make this approach 3x faster than Redu's (where the original was 2x slower).
var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]];
var output = Array((data[data.length-1][0]-data[0][0]).toFixed(2)*100+1)
function getIndex(value){
return (value-data[0][0]*100)
}
for( var i = 100*data[0][0]; i <= 100*data[data.length-1][0]; i++ ){
output[getIndex(i)] = [i/100, data.find( d => i <= 100*d[0] )[1]]
}
//console.log(output)
// Performance comparison
function option1(data){
let t = performance.now()
var output = Array((data[data.length-1][0]-data[0][0]).toFixed(2)*100+1)
function getIndex(value){
return (value-data[0][0]*100)
}
for( var i = 100*data[0][0]; i <= 100*data[data.length-1][0]; i++ ){
output[getIndex(i)] = [i/100, data.find( d => i <= 100*d[0] )[1]]
}
return performance.now()-t
}
function option2(data){
let t = performance.now()
newData = data.reduce((p,c,i,a) => i ? p.concat(Array(Math.round(c[0]*100 - a[i-1][0]*100)).fill()
.map((_,j) => [Number((a[i-1][0]+(j+1)/100).toFixed(2)),c[1]]))
: [c],[]);
return performance.now()-t
}
var testdata = [[1.13,4],[2.05,6],[5.23,7],[5.28,7],[5.32,8],[5.35,8],[8.91,9],[10.31,9]];
var nTrials = 10000;
for(var trial=0, t1=0; trial<=nTrials; trial++) t1 += option1(testdata)
for(var trial=0, t2=0; trial<=nTrials; trial++) t2 += option2(testdata)
console.log(t1/nTrials) // ~0.4 ms
console.log(t2/nTrials) // ~0.55 ms
Array.prototype.reduce() is sometimes handy to extend the array. May be you can do as follows;
var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]],
newData = data.reduce((p,c,i,a) => i ? p.concat(Array(Math.round(c[0]*100 - a[i-1][0]*100)).fill()
.map((_,j) => [Number((a[i-1][0]+(j+1)/100).toFixed(2)),c[1]]))
: [c],[]);
console.log(newData);
var data = [[1.01,3],[1.04,4],[1.09,5],[1.10,6],[1.15,7]],
newData = data.reduce((p,c,i,a) => i ? p.concat(Array(Math.round(c[0]*100 - a[i-1][0]*100)).fill()
.map((_,j) => [Number((a[i-1][0]+(j+1)/100).toFixed(2)),c[1]]))
: [c],[]);
console.log(newData);
I propose this solution :
var data = [[5.23,7],[5.28,7],[5.32,8],[5.35,8]];
var res = [];
data.forEach((item, index, arr) => {
res.push(item);
var temp = item[0];
while (arr[index+1] && arr[index+1][0]-temp > 0.01){
temp += 0.01;
res.push([temp, arr[index+1][1]]);
}
});
console.log(res);
If I use <= instead of <, I'll get NaN, why?
function addArgs(){
var sum = 0, count = 0;
while(count <= arguments.length){
sum += arguments[count];
count++;
}
return sum;
}
in the last iteration of your loop, count is arguments.length, therefore arguments[count] === arguments[arguments.length] === undefined, and sum += undefined results in sum === NaN
Suppose your argument is 3 elements:
arguments = [0, 1, 2]
Your count will iterate as 0 => 1 => 2 => 3 (and on 3rd you are out of bound of the array, since it has 3 elements, but indexed starting with 0.
That's basics of iterating through loop.
When you iterate through a list and use the index to access the items of the list (like you're doing), you always iterate up to length - 1 or < length. The reason is that the list index starts from zero, not one. For instance, a list of 3 items has it's length equals 3 and the indexes of its items are 0, 1, and 2. There is no item with index 3, so if you iterate up to length or <= length, the counter will reach 3 in the last iteration and the attempt to retrieve the item with the index 3 will fail and return undefined.
Finally, adding the undefined to the sum will results in a NaN because undefined is not a number.
It seems that arguments[count] is not a number (NaN). In Javascript, when the second argument in an expression is not a number, the first one is also treated as not a number.
Thus, sum ends up the function being treated as another data type.
http://www.w3schools.com/js/js_datatypes.asp
All iterations start from 0(Also, count = 0 in your code). So, max count equals arguments.length-1.
addArgs(2,5,8); -> arguments[0] = 2; arguments[1] = 5; arguments[2] = 8;
Besides that, you can use <= when count starts from 1
function addArgs(){
var sum = 0, count = 1;
while(count <= arguments.length){
sum += arguments[count-1];
count++;
}
return sum;
}
addArgs(2,3,4);//9