1

I'm confused as to why the below produces the same pairs of values for race and gender in each iteration through the for loop. I would imagine the initial seed value for random() (not explicitly declared in my code) is taken in the first iteration and then the loop proceeds to the next value in the random sequence based on the initial seed, but it appears that is not the case. Instead, it appears to use the same seed/value for each iteration, leading to identical values of race and gender across each iteration.

Minimal working example:

    // Two arrays
    var race = ["B","W"];
    var gender = ["F", "M"]; 
    
    for (i = 0; i < 6; i++) {
            var race = race[Math.floor(Math.random()*race.length)];
            var gender =  gender[Math.floor(Math.random()*gender.length)];
            document.write(race)
            document.write(gender)
            } 

    // Example result: BMBMBMBMBMBM (BM repeated 6 times)

Is there someway of looping through using independently drawn values instead of producing identical values for each loop iteration? Apparently this has been answered in C++ and there are many questions on Javascript seeding, but not this.

user120242
  • 14,918
  • 3
  • 38
  • 52
socialscientist
  • 3,759
  • 5
  • 23
  • 58
  • 2
    In the code snippet you are using "race" and "gender" for both the array and the randomly generated variables. You need to use 4 different names. – Adder May 14 '20 at 15:02

3 Answers3

2

In the code snippet you are using "race" and "gender" for both the array and the randomly generated variables. You need to use 4 different names.

    // Two arrays
    var races = ["B","W"];
    var genders = ["F", "M"]; 
    
    for (i = 0; i < 6; i++) {
            var race = races[Math.floor(Math.random()*races.length)];
            var gender =  genders[Math.floor(Math.random()*genders.length)];
            document.write(race)
            document.write(gender)
            } 

    // Example result: BMBMBMBMBMBM (BM repeated 6 times)

If you had used "const" and "let" you would have received an error message.

    // Two arrays
    const race = ["B","W"];
    const gender = ["F", "M"]; 
    
    for (i = 0; i < 6; i++) {
            let race = race[Math.floor(Math.random()*race.length)];
            let gender =  gender[Math.floor(Math.random()*gender.length)];
            document.write(race)
            document.write(gender)
            } 
Adder
  • 5,708
  • 1
  • 28
  • 56
  • Might also mention why problems like this would not be possible when using `let` or `const` – Patrick Roberts May 14 '20 at 15:06
  • I don't think that's necessary nor helpful; it doesn't help OP understand the confusion due to hoisting. – user120242 May 14 '20 at 15:15
  • 1
    Note that I am accepting this as an answer for most directly answering the question but that the answer by @user120242 is more helpful for the technical explanation of hoisting. – socialscientist May 14 '20 at 17:18
  • @Adder Thanks, this makes sense. One question in light of looking at this solution: why does using the code above cause document.write(races) within the loop to produce a single value for the first and last iterations of the loop but two values for the iterations between the first and last? I would expect document.write(races) to return [B,W] for every iteration of the loop if it comes after document.write(race). – socialscientist May 14 '20 at 17:18
  • 1
    @user3614648 that was actually my intention, which is why I left out the actual answer, that already existed in the other answers – user120242 May 14 '20 at 17:21
  • 1
    @user3614648 look at my runnable snippet example for explanation of that and look at the output of console.log. I also put comments in the code to explain it. The second iteration is acting on the string "M"[(length=1 so it's always 0)] – user120242 May 14 '20 at 18:22
  • 1
    @user3614648 ah I see your confusion. the output of console.log makes it look like it's one value. I've changed it so you can take a look again to see what's happening. also remember the first iteration is two arrays being coerced into strings – user120242 May 14 '20 at 18:32
1

Take a look at your variable names - you reuse race and gender as the arrays and then assign a value to them. Did a quick mockup:

const race = ["B","W"];
const gender = ["F", "M"];    

for (i = 0; i < 6; i++) {
  let rand1 = Math.random();
  let rand2 = Math.random();
  console.log(`Random 1: ${rand1}, Random 2: ${rand2}`);
  let r = race[Math.floor(rand1*race.length)];
  let g =  gender[Math.floor(rand2*gender.length)];
  console.log(`Race: ${r}, Gender: ${g}`);
}
pxslip
  • 299
  • 1
  • 6
  • Thank you! I guess brain wasn't on. It's weird that this produces console.log(race) within the loop where the first and last elements are not ["B","W"] though? – socialscientist May 14 '20 at 17:16
1

Javascript will move var declarations to the top of the scope context due to hoisting.
https://www.w3schools.com/js/js_hoisting.asp
For a more accurate description you can read this, but the above description is more conceptually accessible: https://developer.mozilla.org/en-US/docs/Glossary/Hoisting

Your code actually ends up equivalent to this:

// Two arrays
var race = ["B","W"];
var gender = ["F", "M"];    

for (i = 0; i < 6; i++) {
        race = race[Math.floor(Math.random()*race.length)];
        gender =  gender[Math.floor(Math.random()*gender.length)];
        document.write(race)
        document.write(gender)
        } 

// Example result: BMBMBMBMBMBM (BM repeated 6 times)

I've inserted a console.log in the code below, to show what is happening:

// Two arrays
    var race = ["B","W"];
    var gender = ["F", "M"]; 
    
    for (i = 0; i < 6; i++) {
// note that after the first loop, you are actually just using the string
// the race[] is actually just taking 1 character from race, or ie ("M F")[0]
console.log("race=",race,"gender=",gender);
            var race = race[Math.floor(Math.random()*race.length)];
            var gender =  gender[Math.floor(Math.random()*gender.length)];
            document.write(race)
            document.write(gender)
            } 

    // Example result: BMBMBMBMBMBM (BM repeated 6 times)

It is generally recommended as best practice that you use let and const instead of var to avoid introducing bugs like these, since let and const are not hoisted and discourage reusing the variables in ways that may cause unexpected behavior like this.

user120242
  • 14,918
  • 3
  • 38
  • 52