Simon Mathewson
Simon Mathewson

Reputation: 731

"Refill" an array

I have a site with different message-boxes. The heads of these boxes should always have a different random color. In order to prevent messages next to each other having the same color, I use splice to remove the used colors from the array colors. At some point the array colors will be empty, which is why the remaining boxes will have no color. How can I "refill" the array, so every element gets a random color?

Here is my page: http://www.tdvk.de/. Yellow is the default head color.

function selectColor() {
    var random = Math.floor(Math.random() * colors.length);
    return colors.splice( random, 1 )[0];
}

var colors = [ "rgb(199, 179, 78)", "rgb(127, 195, 133)", "rgb(102, 169, 162)", "rgb(132, 122, 224)" ];

var i = 0;

while ( i != colors.length ) {
    $( "tr" ).each(function(i) {
        var head = $( ".msgHead", this );
        head.css( "background-color", selectColor() );
        i++;
    });
}

if { i = colors.length ) {
    /* reset array and continue */
}

Upvotes: 4

Views: 429

Answers (5)

Chop
Chop

Reputation: 534

Instead of hardcoding color combinations, you can generate them on the fly

    var usedColors = []

    function randomDigit( max ){
        return Math.floor( Math.random() * max )
    }

    function getNewColor(){
        var max = 255
        var r = randomDigit( max )
        var g = randomDigit( max )
        var b = randomDigit( max )
        if ( !colorUsed( r, g, b ) ) return storeColor( r, g, b )
        else return getNewColor()
    }

    function storeColor( r, g, b ){
        usedColors[ r + "," + g + "," + b ] = true
        return [ r, g, b ]
    }

    function colorUsed(r,g,b){
        return usedColors[ r + "," + g + "," + b ]
    }

    var newColor = getNewColor()

Or if you prefer to have them predefined

    function shuffle(o){
        for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
        return o;
    };

    var colors = [ "rgb(199, 179, 78)", "rgb(127, 195, 133)", "rgb(102, 169, 162)", "rgb(132, 122, 224)" ]

    function getColors(boxCount){
        var colorArr = []

        var fullIterations = Math.floor( boxCount / colors.length )

        var lastIteration = boxCount - fullIterations * colors.length

        for(var x = 0; x < fullIterations; x++){
            for(var i = 0; i < colors.length; i++){ var color = colors[ i ]; colorArr.push(color);  }
            colors = shuffle( colors )
        }

        for(var x = 0; x < lastIteration; x++){ var color = colors[ x ]; colorArr.push(color);  }

        colors = shuffle( colors )

        return colorArr
    }

    var newColors = getColors(20)

Upvotes: 0

Matt Burland
Matt Burland

Reputation: 45155

Your question is leaving out some important details, but I think what you want to do is keep selecting random colors from the array, repeating no values until you've used every color and only then start repeating.

Probably you best solution might be to start with a Fisher-Yates shuffle of your original array and then just select each color in turn until you reach the last color. Then simply reshuffle and start back from the beginning.

So in pseudo-code, you'd have something like this:

shuffleArray
colorIndex = 0; 

while still selecting colors
    select color at array[colorIndex]
    colorIndex++
    if (colorIndex > array.length)
        colorIndex = 0;
        shuffleArray

For example:

  // from http://bost.ocks.org/mike/shuffle/

  function shuffle(array) {
    var m = array.length,
      t, i;

    // While there remain elements to shuffle…
    while (m) {

      // Pick a remaining element…
      i = Math.floor(Math.random() * m--);

      // And swap it with the current element.
      t = array[m];
      array[m] = array[i];
      array[i] = t;
    }

    return array;
  }

var colorIndex = 0;
var colors = ["rgb(199, 179, 78)", "rgb(127, 195, 133)", "rgb(102, 169, 162)", "rgb(132, 122, 224)"];

var i = 0;

shuffle(colors);
while (i < 8) { 
  alert(colors[colorIndex++]);
  i++;
  if (colorIndex >= colors.length) {
    shuffle(colors);
    colorIndex = 0;
  }
}

Note there is one more thing to consider. Regardless of how you do this, there is a possibility that the first element selected from your "refilled" array will be the same as the last element from the previous cycle. You might want to think about how to handle that case.

Upvotes: 1

Ethan Lynn
Ethan Lynn

Reputation: 1009

Given that you're boxes are ordered from top to bottom. Why not select the random color by making sure the color of the previous box (above) is different than the current box. For example:

// remove splice from selectColor, the only thing selectColor needs to do now is select a random color from your list.

var previousColor = null;
$("tr").each(function (element) {
    // select random color
    var color = selectColor();
    // make sure it is different than previous color
    while (color === previousColor) selectColor();

    // ...
    // set element color
    // ...

    // make sure next element is different
    previousColor = color;
});

Upvotes: 0

KJ Price
KJ Price

Reputation: 5984

Instead of using splice, why not just iterate through them?

var selectedColorIndex = Math.round(Math.random() * colors.length);
function selectColor() {
    var random = Math.floor(Math.random() * colors.length);
    return colors[selectedColorIndex];
}

Edit: this will now select a random element.

Upvotes: 2

Scott
Scott

Reputation: 3764

How to refill with the same colors:

if ( i = colors.length ) {
    colors = [ "rgb(199, 179, 78)", "rgb(127, 195, 133)", "rgb(102, 169, 162)", "rgb(132, 122, 224)" ];
}

But like others have suggested, there are other ways to do this like wrapping around to the first element again and not splicing, or randomizing which values there are.

Upvotes: 3

Related Questions