Privateer
Privateer

Reputation: 196

Why am I unable to draw a random element of an array populated by AJAX?

This on e is a doozey.

I have while loop to generate a random number that is not the same as any other random number produced before. The random number is used to select a text value from an object.

for example:

quoteArray[1] = "some text"
quoteArray[2] = "some different text"
quoteArray[3] = "text again"
quoteArray[4] = "completely different text"
quoteArray[5] = "ham sandwich"

This is part of a larger function and after that function has cycled through = quoteArray.length it resets and starts the cycle over again. The issue I am hitting is that the following code is SOMETIMES producing an infinite loop:

//Note: at this point in the function I have generated a random number once already and stored it in 'randomnumber'
//I use this while statement to evaluate 'randomnumber' until the condition of it NOT being a number that has already been used and NOT being the last number is met.
while(randomnumber === rotationArray[randomnumber] || randomnumber === lastnumber){
    randomnumber = Math.floor(Math.random() * (quoteArray.length));
}

When I console.log(randomnumber) - when I am stuck in the loop - I am just getting '0' as a result. When stuck in the loop it doesn't appear as though Math.floor(Math.random() * (quoteArray.length)) is producing a random number but rather just '0' infinitely.

can anyone tell me why I am running into this problem?



EDIT: Here is the complete pertinent code with function + variable declarations


// Function to initialize the quoteObj
    function quoteObj(text,cname,ccompany,url,height) {
            this.text=text;
            this.cname=cname;
            this.ccompany=ccompany;
            this.url=url;
            this.height=height;
        }

// Populate my quotes Object with the quotation information from the XML sheet. 

    var qObj = new quoteObj('','','','');
    var quoteArray = new Array();
    var counter = 0;

//cycles through each XML item and loads the data into an object which is then stored in an array
    $.ajax({
        type: "GET",
        url: "quotes.xml",
        dataType: "xml",
        success: function(xml) {
            $(xml).find('quote').each(function(){
                quoteArray[counter] = new quoteObj('','','','');
                console.log(quoteArray[counter]);
                quoteArray[counter].text = $(this).find('text').text();
                quoteArray[counter].cname = $(this).find('customer_name').text();
                quoteArray[counter].ccompany = $(this).find('customer_company').text();
                quoteArray[counter].url = $(this).find('project').text();
                ++counter;
            });
        }
    });

// This is the setion that is generating my infinite loop issue.
// I've included all of the other code in case people are wondering specific things about how an item was initialized, etc. 

// Generate a random first quote then randomly progress through the entire set and start all over.      

    var randomnumber = Math.floor(Math.random() * (quoteArray.length));
    var rotationArray = new Array(quoteArray.length);
    var v = 0;
    var lastnumber = -1;

    bHeight = $('#rightbox').height() + 50;
    var cHeight = 0;
    var divtoanim = $('#customerquotes').parent();

//NOT RELATED// 
// Give the innershadow a height so that overflow hidden works with the quotations. 
    $(divtoanim).css({'height' : bHeight});

// Rotate the Quotations Randomly function. 
    setInterval(function(){     
        randomnumber = Math.floor(Math.random() * (quoteArray.length));

//checks to see if the function loop needs to start at the beginning. 
        if(v == (quoteArray.length)){
            rotationArray.length = 0;
            v = 0;
        }
//determines if the random number is both different than any other random number generated before and that is is not the same as the last random number
        while(randomnumber === rotationArray[randomnumber] || randomnumber === lastnumber){
            randomnumber = Math.floor(Math.random() * (quoteArray.length));
        }   

        lastnumber = randomnumber;
        rotationArray[randomnumber] = randomnumber;
        ++v;

//NOT RELATED//
//animation sequence
        $('#ctext, #cname').animate({'opacity':'0'},2000, function(){
            $('#ctext').html(quoteArray[randomnumber].text);
            $('#cname').html('- ' + quoteArray[randomnumber].cname);

            cHeight = $('#customerquotes').height() + 50;
            adjustHeight(bHeight,cHeight,divtoanim);

            $('#ctext').delay(500).animate({'opacity':'1'},500);
            $('#cname').delay(1500).animate({'opacity':'1'},500);
        });
    },15000); 

Upvotes: 1

Views: 9144

Answers (3)

Kevin Peno
Kevin Peno

Reputation: 9196

First, since you updated your question, be sure that you are handling asynchronous data properly. Since an ajax call is asynchronous, you will need to be sure to only run the randomizer once the call is successful and data has been returned.

Second, assuming you are handling the asyc data properly, the size of your result set is likely it is too small. Thus, you are probably randomly getting the same number too often. Then, you can't use this number because you have already done so.

What you need to do is pop off the parts that are already used from the results array each time. Recalculate the array length, then pull a random from that. However, the likelihood of this feeling random is very slim.

There is probably a more efficient way to do this, but here's my go:

var results = ['some text','some text2','some text3','some text4','some text5', /* ...etc */ ],
       randomable = results;

function getRandomOffset( arr )
{
    var offset,
           len;

    if( arr.length < 1 )
        return false;
    else if( arr.length > 1 )
        offset = Math.floor(Math.random() * arr.length );
    else
        offset = 0;

    arr.splice( offset, 1 );

    return [
        offset,
        arr
    ];

}

while( res = getRandomOffset( randomable ) )
{
    // Set the randomable for next time
    randomable = res[1];
    // Do something with your resulting index
    results[ res[0] ];
}

The arrgument sent to the function should be the array that is returned form it (except the first time). Then call that function as you need until it returns false.

Upvotes: 1

David Tang
David Tang

Reputation: 93674

This is an asynchronous problem: the array quoteArray is empty when the code runs, because it fires off the ajax request, and moves on. Anything that depends on quoteArray should be inside the success function of $.ajax.

The array has a length when you type quoteArray.length in the console, only because by that time the Ajax request has completed.

Upvotes: 3

kilrizzy
kilrizzy

Reputation: 2943

have you tried something like

Math.floor(Math.random() * (5));

To make sure the array length is being found properly?

Upvotes: 0

Related Questions