Append an array's reverse to itself - javascript

I have created an array of color values which represents a smooth transition of colors from red to blue.
Now I want this array to take you from red to blue and back again. The obvious solution is to append the reverse of the array to the array.
I have written code to do it, but it isn't working as I understand it should. Instead, it's creating the reversed array, repeated. Instead of "Red to Blue, Blue to Red", it's going "Blue to Red, Blue To Red".
Clearly, there's some behavior of arrays in javascript that I haven't grasped yet.
What should I be doing?
My first attempt is this:
colors = colors.concat(colors.reverse());
Based on the first stackoverflow answer, I tried this:
var arrayCopy = colors;
arrayCopy.reverse();
colors = colors.concat(arrayCopy);
But this produces identical results!
For context, here's the surrounding code:
///////////////////////////////////////////////////////////
// Creating the array which takes you from Red to Blue
//
var colorSteps = 400;
var startColor = [255, 0, 0];
var endColor = [0, 127, 255];
var steps = new Array();
var j = 0;
for (j = 0; j < 3; ++j) {
steps[j] = (endColor[j] - startColor[j]) / colorSteps;
}
var colors = Array();
for (j = 0; j < colorSteps; ++j) {
colors[j] = [
Math.floor(startColor[0] + steps[0] * j),
Math.floor(startColor[1] + steps[1] * j),
Math.floor(startColor[2] + steps[2] * j)
];
}
////////////////////////////////////////////////////////
// Here's the bit where I'm trying to make it a mirror
// of itself!
//
// It ain't working
//
colors = colors.concat(colors.reverse());
///////////////////////////////////////////////////////
// Demonstrating what the colors are
//
j = 0;
var changeColorFunction = function () {
if (++j >= colors.length) {
j = 0;
}
var colorName = "rgb(" + colors[j][0] + ", " + colors[j][1] + ", " + colors[j][2] + ")";
debugText.style.background = colorName;
debugText.innerHTML = j;
}
setInterval(changeColorFunction, 10);

The problem with:
colors = colors.concat(colors.reverse());
... is that colors.reverse() mutates the colors array itself, meaning that you're appending a reversed array to an already-reversed array. Try this instead:
colors = colors.concat(colors.slice().reverse());

Copy your colors array to somewhere first. reverse changes the array itself, not merely returns a reverted one.
UPDATE
Code sample:
colors.concat(colors.slice(0).reverse());

Related

Get random non repeated color

I have a function that returns a random color. I push these colors into an array. I don't want the colors to be repeated in the array. So I did this:
$scope.getRandomColor = function getRandomColor(arrayToCheckIfAlreadyContains) {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
//check if array contains the generated color
if(arrayToCheckIfAlreadyContains.indexOf(color) >= 0){
let nonRepeatColor = $scope.getRandomColor(arrayToCheckIfAlreadyContains);
console.log("color repeated", color, arrayToCheckIfAlreadyContains);
return nonRepeatColor;
}
return color;
}
But I don't know if this is efficient or will even work for sure. Also, it would be great if the colors are distinguishable. Sometimes I get colors that are almost same. How do I make sure that doesn't happen.
hsl can help you produce distinguishable colors. Try this.
function makeColor(colorNum, colors){
if (colors < 1) colors = 1;
// defaults to one color - avoid divide by zero
return colorNum * (360 / colors) % 360;
}
// This could be length of your array.
var totalDIVs = 20;
var totalColors = totalDIVs;
for (var i = 0; i < totalDIVs; i++){
var element = document.createElement('div');
document.body.appendChild(element);
var color = "hsl( " + makeColor(i, totalColors) + ", 100%, 50% )";
element.style.backgroundColor = color;
element.innerHTML = color;
}
You might consider using hsl instead of hex notation - pick a number between 0 and 359 for the initial color, then select the other colors such that they're equidistant. For example:
function getColors(num) {
const initialColor = Math.floor(Math.random() * 360);
const increment = 360 / num;
const hsls = [];
for (let i = 0; i < num; i++) {
hsls.push(Math.round((initialColor + (i * increment)) % 360));
}
return hsls;
}
function displayNew() {
container.innerHTML = '';
const hsls = getColors(input.value);
hsls.forEach((hsl) => {
const div = container.appendChild(document.createElement('div'));
div.style.backgroundColor = 'hsl(' + hsl + ', 100%, 50%)';
});
}
#container > div {
height: 30px;
}
<input id="input" onkeyup="displayNew()" type="number">
<div id="container"></div>
From your code I don't quite understand what you are doing if the color is already in the array: do you want to pick another random color until you find one color that is not in the array?
Anyways, since your second goal (distinguishable colors), I guess you need some extra work: every time you pick a random color, you need to check its similarity against ALL the colors in the array!
Something like the following:
getRandomColor = function getRandomColor(arrayToCheckIfAlreadyContains) {
let colorFound = true;
let letters = '0123456789ABCDEF';
do {
colorFound = true;
var randomColor = '#';
for (var i = 0; i < 6; i++) {
randomColor += letters[Math.floor(Math.random() * 16)];
}
arrayToCheckIfAlreadyContains.some(color => {
if (distanceBetweenColor(color, randomColor) < TRESHOLD) {
/* Branch taken when randomColor is too similar
* to an already existing color. */
colorFound = false;
return true;
}
return false;
});
} while (!colorFound);
}
Now, how implementing distanceBetweenColor()? You should use Delta-E algorithm: I suggest you to read this answer in SO: https://stackoverflow.com/a/15189004/6070423
EDIT: Notice the use of some instead of forEach: doing this, you stop the iteration as soon as you find a color that is too similar.

Manipulate more javascript array based on another array

I've a strange thing to do but I don't know how to start
I start with this vars
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
So to start all the 3 array have the same length and the very first operation is to see if there is a duplicate value in sky array, in this case the 0 is duplicated and only in this case is at the end, but all of time the sky array is sorted. So I've to remove all the duplicate (in this case 0) from sky and remove the corresponding items from base and sum the corresponding items on ite. So if there's duplicate on position 4,5 I've to manipulate this conditions. But let see the new 3 array:
var new_base = [1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var new_sky = [0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var new_ite = [139,38,13,15,6,4,6,3,2,1,2,1,1,1];
If you see the new_ite have 139 instead the 64,52,23, that is the sum of 64+52+23, because the first 3 items on sky are the same (0) so I remove two corresponding value from base and sky too and I sum the corresponding value into the new_ite array.
There's a fast way to do that? I thought a for loops but I stuck at the very first for (i = 0; i < sky.length; i++) lol, cuz I've no idea on how to manipulate those 3 array in that way
J
When removing elements from an array during a loop, the trick is to start at the end and move to the front. It makes many things easier.
for( var i = sky.length-1; i>=0; i--) {
if (sky[i] == prev) {
// Remove previous index from base, sky
// See http://stackoverflow.com/questions/5767325/how-to-remove-a-particular-element-from-an-array-in-javascript
base.splice(i+1, 1);
sky.splice(i+1, 1);
// Do sum, then remove
ite[i] += ite[i+1];
ite.splice(i+1, 1);
}
prev = sky[i];
}
I won't speak to whether this is the "fastest", but it does work, and it's "fast" in terms of requiring little programmer time to write and understand. (Which is often the most important kind of fast.)
I would suggest this solution where j is used as index for the new arrays, and i for the original arrays:
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
var new_base = [], new_sky = [], new_ite = [];
var j = -1;
sky.forEach(function (sk, i) {
if (!i || sk !== sky[i-1]) {
new_ite[++j] = 0;
new_base[j] = base[i];
new_sky[j] = sk;
}
new_ite[j] += ite[i];
});
console.log('new_base = ' + new_base);
console.log('new_sky = ' + new_sky);
console.log('new_ite = ' + new_ite);
You can use Array#reduce to create new arrays from the originals according to the rules:
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330];
var sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17];
var ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1];
var result = sky.reduce(function(r, n, i) {
var last = r.sky.length - 1;
if(n === r.sky[last]) {
r.ite[last] += ite[i];
} else {
r.base.push(base[i]);
r.sky.push(n);
r.ite.push(ite[i]);
}
return r;
}, { base: [], sky: [], ite: [] });
console.log('new base:', result.base.join(','));
console.log('new sky:', result.sky.join(','));
console.log('new ite:', result.ite.join(','));
atltag's answer is fastest. Please see:
https://repl.it/FBpo/5
Just with a single .reduce() in O(n) time you can do as follows; (I have used array destructuring at the assignment part. One might choose to use three .push()s though)
var base = [1,1,1,2,3,5,7,9,14,19,28,40,56,114,232,330],
sky = [0,0,0,3,4,5,6,7,8,9,10,11,12,14,16,17],
ite = [64,52,23,38,13,15,6,4,6,3,2,1,2,1,1,1],
results = sky.reduce((r,c,i) => c === r[1][r[1].length-1] ? (r[2][r[2].length-1] += ite[i],r)
: ([r[0][r[0].length],r[1][r[1].length],r[2][r[2].length]] = [base[i],c,ite[i]],r),[[],[],[]]);
console.log(JSON.stringify(results));

setAttribute() in a multi dimensional array

What I'm trying to figure out is how to give another class to certain div's in a two-dimensional array that has a value of "3" . This is what I tried at first.
numRows are the amount rows in the array.
numSeatsPerRow are the number of array items in every "numRows".
r and t are the positions in the two-dimensional array.
for(var r = 0; r<numRows; r++){
for(var t = 0; t<numSeatsPerRow; t++){
if(myArray[r][t] === 3){
document.getElementsByTagName("div")[r][t].setAttribute("class", "busySpot");
}
}
}
I soon understood that this wouldn't work. I tried to make a formula which could calculate every position as an ordinary array but couldn't make it work. Is there any other way to solve my problem?
In the code below I create a two-dimensional array based on two different inputs
var myArray = new Array(numRows);
for(var i = 0; i <numRows; i++){
myArray[i] = new Array(numSeatsPerRow);
}
In the next piece of code I give all array items the value 1 and then draw a green box for every item.
for(var h = 0; h<numRows; h++){
document.body.appendChild(//efter varje rad sker ett <br> i utskriften
document.createElement("br"));
document.body.appendChild(
document.createElement("br"));
for(var g = 0; g<numSeatsPerRow; g++){
myArray[h][g] = 1
var vSpot = document.createElement("div");
vSpot.className = "vacantSpot";
document.body.appendChild(vSpot);
}
}
What I'm trying to do in the top code is give the random boxes that I talked about another class.
this is how i came up with the random coordinates:
for(var v = 0; v < 15; v++){
var test = true;
while(test){
var x = Math.floor((Math.random() * numSeatsPerRow) + 0 );
var y = Math.floor((Math.random() * numRows) + 0);
if(myArray[x][y] === 1) {
myArray[x][y] = 3;
test = false;
}
}
}
Thanks!

Adding two indexed geometries to a BufferGeometry

At the moment I'm using a set of predefined data (containing the indices, vertices and colors) and multiple THREE.Geometry's to add objects to a scene. As you could imagine, this is pretty slow as it requires adding and removing many objects at once or merging many together.
However if I used a single THREE.BufferGeometry this would allow me to use _gl.bufferSubData to add and remove objects and in theory should have minimal affect on the performance.
The trouble I'm having is putting this into practice. I already have the bufferSubData function working, however I'm unable to add two sets of data in the same BufferGeometry. I'm guessing this is because the data does not follow on from each other (as they're two separate objects) so they're both using the same indices. This image shows the result.
I've created a JSFiddle which uses an array named section containing the chunk data. If anyone could take a look and change it so it adds both sets of data I would really appreciate it:
http://jsfiddle.net/dUqwT/
Also, I've been unable to find the purpose of the index offset. If someone could link or explain what it's used for, that would be very helpful.
Thanks for the help!
Alright, worked out how it's done and I've updated the JSFiddle:
http://jsfiddle.net/dUqwT/1/
It was more simple than I thought and it had nothing to do with the index offset (still no idea what that does). I just ensured that it appends to each array correctly so positions, indices and colors aren't overwritten. Did that by using two variables and setting them to the appropriate lengths.
Realistically, as the objects I'm adding to the BufferGeometry will be dynamic, I'll need to give each object a certain amount of the buffer it can use instead of setting two vars to the length. That will allow me to then remove and change each object using _gl.bufferSubData.
for (var chunkID = 0; chunkID < 2; chunkID++) {
var chunkIndices = section[chunkID].indices;
var chunkVertices = section[chunkID].vertices;
var chunkColors = section[chunkID].colors;
var sectionXPos = chunkID * 32;
var sectionYPos = 0;
var sectionZPos = 0;
// Add indices to BufferGeometry
for ( var i = 0; i < chunkIndices.length; i ++ ) {
var q = chunkIndices[i];
var j = i * 3 + iLength;
indices[ j ] = (q[0] + vLength / 3) % chunkSize;
indices[ j + 1 ] = (q[1] + vLength / 3) % chunkSize;
indices[ j + 2 ] = (q[2] + vLength / 3) % chunkSize;
}
// Add vertices to BufferGeometry
for ( var i = 0; i < chunkVertices.length; i ++ ) {
var q = chunkVertices[i];
var j = i * 3 + vLength;
// positions
positions[ j ] = q[0] + sectionXPos;
positions[ j + 1 ] = q[1] + sectionYPos;
positions[ j + 2 ] = q[2] + sectionZPos;
// colors
var hexColor = chunkColors[i / 4];
color.set(hexColor);
colors[ j ] = color.r;
colors[ j + 1 ] = color.g;
colors[ j + 2 ] = color.b;
}
iLength += chunkIndices.length * 3;
vLength += chunkVertices.length * 3;
}

Javascript Random problem?

var swf=["1.swf","2.swf","3.swf"];
var i = Math.floor(Math.random()*swf.length);
alert(swf[i]); // swf[1] >> 2.swf
This case ,Random output One number.
How to Random output two different numbers ?
var swf = ['1.swf', '2.swf', '3.swf'],
// shuffle
swf = swf.sort(function () { return Math.floor(Math.random() * 3) - 1; });
// use swf[0]
// use swf[1]
Even though the above should work fine, for academical correctness and highest performance and compatibility, you may want to shuffle like this instead:
var n = swf.length;
for(var i = n - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = swf[i];
swf[i] = swf[j];
swf[j] = tmp;
}
Credits to tvanfosson and Fisher/Yates. :)
You can use splice to remove the chosen element, then simply select another randomly. The following leaves the original array intact, but if that's not necessary you can use the original and omit the copy. Shown using a loop to demonstrate how to select an arbitrary number of times upto the size of the original array.
var swf=["1.swf","2.swf","3.swf"];
var elementsToChoose = 2;
var copy = swf.slice(0);
var chosen = [];
for (var j = 0; j < elementsToChoose && copy.length; ++j) {
var i = Math.floor(Math.random()*copy.length);
chosen.push( copy.splice(i,1) );
}
for (var j = 0, len = chosen.length; j < len; ++j) {
alert(chosen[j]);
}
I would prefer this way as the bounds are known (you are not getting a random number and comparing it what you already have. It could loop 1 or 1000 times).
var swf = ['1.swf', '2.swf', '3.swf'],
length = swf.length,
i = Math.floor(Math.random() * length);
firstRandom = swf[i];
// I originally used `delete` operator here. It doesn't remove the member, just
// set its value to `undefined`. Using `splice` is the correct way to do it.
swf.splice(i, 1);
length--;
var j = Math.floor(Math.random() * length),
secondRandom = swf[j];
alert(firstRandom + ' - ' + secondRandom);
Patrick DW informed me of delete operator just leaving the value as undefined. I did some Googling and came up with this alternate solution.
Be sure to check Tvanfosson's answer or Deceze's answer for cleaner/alternate solutions.
This is what I would do to require two numbers to be different (could be better answer out there)
var swf=["1.swf","2.swf","3.swf"];
var i = Math.floor(Math.random()*swf.length);
var j;
do {
j = Math.floor(Math.random()*swf.length);
} while (j === i);
alert(swf[i]);
alert(swf[j]);
Edit: should be j===i

Categories

Resources