0

I'm trying to pick colors from an array, and I've seen many examples on how to do this randomly, but is there a way of doing this in the order the array elements are ordered. For example, this picks color from an array randomly, and it works perfectly.

function getColor() {
  var colors = [ '#4B3E4D', '#D74F33', '#7C0380', '#CC0099', '#FF6600', '#1E8C93', '#009900', '#D74F33',
                 '#D31900', '#2F0093', '#CC3300', '#002254', '#9d0000', '#006B8F', '#3f0e0e', '#0066FF',
                 '#338533', '#0066CC' ];

  var random_color = colors[Math.floor(Math.random() * colors.length)];

  return random_color;
}

What do I need to do to follow the order '#4B3E4D', '#D74F33', '#7C0380'... and so on? I appreciate any help with this beginner question.

2
  • 2
    You could have a global variable that keeps track of what index you're at, and increment that variable every time the function is called. You would then use this variable to replace Math.floor(Math.random() * colors.length). Commented May 13, 2015 at 17:13
  • 1
    @danBuonocore is right, of course, but your question makes me wonder whether what you're doing is the easiest way to achieve what you want. Is there are reason you can't simply loop through the colors? And even if you can't—does it make sense to declare the colours inside of the function every time it is called? If you have a variable to keep track of where you are, do you need a function at all? Commented May 13, 2015 at 17:17

6 Answers 6

2

You can add the behavior you want to the array. The code below adds a getNext function that will get the next value from currentIndex. If currentIndex reaches or surpasses the array's length, it'll reset the current index to 0 and get the first element.

var colors = [];
colors.currentIndex = -1;
colors.getNext = function() {
    if (this.length > 0) {
        this.currentIndex += 1;
        if (this.currentIndex >= this.length) this.currentIndex = 0;
        return this[this.currentIndex];
    }
}

$('#nextColor').click(function() {
   $('#currentColor').text(colors.getNext()); 
});

jsFiddle

The advantage to added the members to the array is that you don't have to pollute any scope with variables that are tracking something specific to this object.

Sign up to request clarification or add additional context in comments.

Comments

2

What you need is some sort of state or context. I.e., what color am I on, and what color comes next?

Typically, functions are a poor choice for this, because it means you have side effects. That is, the function changes the greater context of the program, and is no longer predictable. (Functions that return random results are generally okay because it's understood that they're unpredictable.)

One way to accomplish this is to use an object that has a method, which is the object-oriented approach to managing state:

function ColorProvider() {
    this.colors = ['#4B3E4D', '#D74F33' /*...*/];
    this.currentColorIndex = 0;
}
// gets the next color; returns "undefined" when all colors
// have been exhausted
ColorProvider.prototype.getNextColor = function() {
    return this.colors[this.currentColorIndex++];
}
ColorProvider.prototype.reset = function() {
    this.currentColorIndex = 0;
}

It's a lot of work for small functionality, but it is the "sane" (object-oriented) way to handle state-based function. You would use it like this:

var cp = new ColorProvider();
cp.getNextColor();   // returns '#4B3E4D'
cp.getNextColor();   // returns '#D74F33'

(Note that this, and the other implementations I'm suggesting, simply start returning undefined when there are no more colors. It would be easy enough to modify these examples so that they loop around and start again at the beginning.)

Another way to do it is to use a closure, which is similar to the object-oriented approach:

var getNextColor = (function() {
    var currentColorIndex = 0;
    var colors = ['#4B3E4D', '#D74F33' /*...*/];
    return function() {
        return colors[currentColorIndex++];
    }
})();

Lastly, maybe you don't want a function at all...maybe you just want to have colors be an array that's lying around, and you can just iterate over it as needed:

var colors = ['#4B3E4D', '#D74F33' /*...*/];
// whenever you need it....
for(var i=0; i<colors.length; i++) {
    colors[i];   // '#4B3E4D', '#D74F33', etc.
}
// or....
colors.forEach(function(c) {
    // c takes on the values '#4B3E4D', '#D74F33', etc.
});

Comments

1

You have to keep track of which color is currently in use outside the function like so:

var currentColorIndex = -1;

Then every time you want the next color, you can just call the function like so:

var newColor = getColor(++currentColorIndex);

.

function getColor(currentColorIndex) {

var colors = ['#4B3E4D', '#D74F33', '#7C0380', '#CC0099', '#FF6600', '#1E8C93', '#009900', '#D74F33',
    '#D31900', '#2F0093', '#CC3300', '#002254', '#9d0000', '#006B8F', '#3f0e0e', '#0066FF',
    '#338533', '#0066CC'];


return colors[currentColorIndex];
}

Comments

1

As mentioned you could use a variable to track the colours, and an if statement to ensure you haven't reached the limit of the array.

var counter = 0

function getColor() {

  var colors = ['#4B3E4D', '#D74F33', '#7C0380', '#CC0099', '#FF6600', '#1E8C93', '#009900', '#D74F33',
        '#D31900', '#2F0093', '#CC3300', '#002254', '#9d0000', '#006B8F', '#3f0e0e', '#0066FF',
        '#338533', '#0066CC'];

  var random_color = colors[counter];
  counter ++;
  if (counter === colors.length) counter = 0;
  return random_color;
};

Comments

1

Assuming you don't want to use a global variable (because it's a bad idea), you can use an IIFE to create a private scope to track the index.

var getColor = (function() {
  var index = 0;
  var colors = [ '#4B3E4D', '#D74F33', '#7C0380', '#CC0099', '#FF6600', '#1E8C93', '#009900', '#D74F33',
                 '#D31900', '#2F0093', '#CC3300', '#002254', '#9d0000', '#006B8F', '#3f0e0e', '#0066FF',
                 '#338533', '#0066CC' ];

  return function() {
    var color = colors[index];
    index = (index + 1) % colors.length;
    return color;
  }
})();

getColor(); // => "#4B3E4D"
getColor(); // => "#D74F33"
// ...
getColor(); // => "#0066CC"
getColor(); // => "#4B3E4D"

This will return each color in turn, and when it gets to the end of the array start over at the beginning.

Comments

1

You just need to keep track of the color index.

var i = 0;
function getColor() {

var colors = ['#4B3E4D', '#D74F33', '#7C0380', '#CC0099', '#FF6600',     '#1E8C93', '#009900', '#D74F33',
    '#D31900', '#2F0093', '#CC3300', '#002254', '#9d0000', '#006B8F',     '#3f0e0e', '#0066FF',
    '#338533', '#0066CC'];

var color = colors[i];
i++;

return color;
}

And to keep everything under control, you add an IEF.

var getColor = (function(){
    var colors = ['#4B3E4D', '#D74F33', '#7C0380', '#CC0099', '#FF6600',     '#1E8C93', '#009900', '#D74F33',
        '#D31900', '#2F0093', '#CC3300', '#002254', '#9d0000', '#006B8F',     '#3f0e0e', '#0066FF',
        '#338533', '#0066CC'];
    var i = 0;

    var getColor = function(){
        return colors[i++];
    };

    return getColor;
})();

Some tests :

console.log(getColor()); // #4B3E4D
console.log(getColor()); // #D74F33
// ...

There is going to be an issue when the index will reach the array.length but I think you can manage that.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.